1616use hiqdev \php \billing \tests \unit \action \ActionTest ;
1717use hiqdev \php \billing \type \Type ;
1818use hiqdev \php \billing \type \TypeInterface ;
19+ use hiqdev \php \units \Quantity ;
1920use hiqdev \php \units \QuantityInterface ;
2021
2122class OnceTest extends ActionTest
@@ -24,6 +25,7 @@ protected function setUp(): void
2425 {
2526 parent ::setUp ();
2627
28+ $ this ->prepaid = Quantity::item (0 );
2729 $ this ->type = $ this ->createType ('monthly,monthly ' );
2830 $ this ->price = $ this ->createPrice ($ this ->type );
2931 }
@@ -92,7 +94,7 @@ public function testPerOneYear_WithOneYearLaterShouldApplyCharge(): void
9294
9395 $ saleTime = new DateTimeImmutable ('22-11-2023 ' );
9496 $ actionTime = $ saleTime ->modify ('+1 year ' );
95- $ action = $ this ->createActionWithSale ($ this -> prepaid -> multiply ( 2 ), $ actionTime , $ saleTime );
97+ $ action = $ this ->createActionWithSale ($ actionTime , $ saleTime );
9698 $ type = $ this ->createType ('monthly,monthly ' );
9799 $ price = $ this ->createPrice ($ type );
98100
@@ -104,6 +106,25 @@ public function testPerOneYear_WithOneYearLaterShouldApplyCharge(): void
104106 $ this ->assertSame ($ charge , $ charges [0 ]);
105107 }
106108
109+ public function testPerOneYear_ShouldChargeInFirstMonthOfSale (): void
110+ {
111+ $ once = $ this ->buildOnce ('1 year ' );
112+ $ saleTime = new DateTimeImmutable ('01-01-2025 12:31:00 ' );
113+ $ actionTime = $ saleTime ->modify ('first day of this month 00:00:00 ' );
114+ $ action = $ this ->createActionWithSale ($ actionTime , $ saleTime );
115+ $ type = $ this ->createType ('monthly,monthly ' );
116+ $ price = $ this ->createPrice ($ type );
117+
118+ $ charge = $ this ->calculator ->calculateCharge ($ price , $ action );
119+ $ this ->assertNonZeroCharge ($ charge );
120+
121+ $ charges = $ once ->modifyCharge ($ charge , $ action );
122+ $ this ->assertIsArray ($ charges );
123+ $ this ->assertCount (1 , $ charges );
124+ $ this ->assertSame ($ charge , $ charges [0 ]);
125+ $ this ->assertNonZeroCharge ($ charges [0 ]);
126+ }
127+
107128 private function createActionWithCustomTime (QuantityInterface $ quantity , DateTimeImmutable $ time ): Action
108129 {
109130 return new Action (null , $ this ->type , $ this ->target , $ quantity , $ this ->customer , $ time );
@@ -115,22 +136,23 @@ public function testPerOneYear_With11MonthsLaterShouldReturnZeroCharge(): void
115136
116137 $ saleTime = new DateTimeImmutable ('22-10-2023 ' );
117138 $ actionTime = $ saleTime ->modify ('+11 months ' );
118- $ action = $ this ->createActionWithSale ($ this -> prepaid -> multiply ( 2 ), $ actionTime , $ saleTime );
139+ $ action = $ this ->createActionWithSale ($ actionTime , $ saleTime );
119140 $ type = $ this ->createType ('monthly,monthly ' );
120141 $ price = $ this ->createPrice ($ type );
121142
122143 $ charge = $ this ->calculator ->calculateCharge ($ price , $ action );
144+ $ this ->assertNonZeroCharge ($ charge );
123145
124146 $ charges = $ once ->modifyCharge ($ charge , $ action );
125147 $ this ->assertCount (1 , $ charges );
126148 $ this ->assertZeroCharge ($ charges [0 ]);
127149 }
128150
129151 private function createActionWithSale (
130- QuantityInterface $ quantity ,
131152 DateTimeImmutable $ actionTime ,
132153 DateTimeImmutable $ saleTime
133154 ): ActionInterface {
155+ $ quantity = Quantity::item (1 );
134156 $ action = $ this ->createActionWithCustomTime ($ quantity , $ actionTime );
135157
136158 $ plan = new Plan (null , '' , $ this ->customer , [$ this ->price ]);
@@ -159,7 +181,7 @@ public function testPerThreeMonths_After3Months_ShouldApplyCharge(): void
159181
160182 $ saleTime = new DateTimeImmutable ('22-01-2024 ' );
161183 $ actionTime = $ saleTime ->modify ('+3 months ' );
162- $ action = $ this ->createActionWithSale ($ this -> prepaid -> multiply ( 2 ), $ actionTime , $ saleTime );
184+ $ action = $ this ->createActionWithSale ($ actionTime , $ saleTime );
163185 $ type = $ this ->createType ('monthly,monthly ' );
164186 $ price = $ this ->createPrice ($ type );
165187
@@ -177,7 +199,7 @@ public function testPerThreeMonths_After2Months_ShouldReturnZeroCharge(): void
177199
178200 $ saleTime = new DateTimeImmutable ('22-01-2024 ' );
179201 $ actionTime = $ saleTime ->modify ('+2 months ' );
180- $ action = $ this ->createActionWithSale ($ this -> prepaid -> multiply ( 2 ), $ actionTime , $ saleTime );
202+ $ action = $ this ->createActionWithSale ($ actionTime , $ saleTime );
181203 $ type = $ this ->createType ('monthly,monthly ' );
182204 $ price = $ this ->createPrice ($ type );
183205
@@ -187,4 +209,44 @@ public function testPerThreeMonths_After2Months_ShouldReturnZeroCharge(): void
187209 $ this ->assertCount (1 , $ charges );
188210 $ this ->assertZeroCharge ($ charges [0 ]);
189211 }
212+
213+ public function testPerOneYearSinceDate (): void
214+ {
215+ $ once = $ this ->buildOnce ('1 year ' )->since ('04.2025 ' );
216+
217+ $ saleTime = new DateTimeImmutable ('2023-01-01 ' ); // Sale time should be ignored, since is set
218+ $ price = $ this ->createPrice ($ this ->createType ('monthly,monthly ' ));
219+
220+ // Action in the same month and year as since: applies
221+ $ actionTime1 = new DateTimeImmutable ('2025-04-30 ' );
222+ $ action1 = $ this ->createActionWithSale ($ actionTime1 , $ saleTime );
223+ $ charge1 = $ this ->calculator ->calculateCharge ($ price , $ action1 );
224+ $ charges1 = $ once ->modifyCharge ($ charge1 , $ action1 );
225+ $ this ->assertCount (1 , $ charges1 );
226+ $ this ->assertEquals ($ charge1 , $ charges1 [0 ]);
227+
228+ // Action one year later in same month: applies
229+ $ actionTime2 = new DateTimeImmutable ('2026-04-01 ' );
230+ $ action2 = $ this ->createActionWithSale ($ actionTime2 , $ saleTime );
231+ $ charge2 = $ this ->calculator ->calculateCharge ($ price , $ action2 );
232+ $ charges2 = $ once ->modifyCharge ($ charge2 , $ action2 );
233+ $ this ->assertCount (1 , $ charges2 );
234+ $ this ->assertEquals ($ charge2 , $ charges2 [0 ]);
235+
236+ // Action one year later in different month: zero charge
237+ $ actionTime3 = new DateTimeImmutable ('2026-05-01 ' );
238+ $ action3 = $ this ->createActionWithSale ($ actionTime3 , $ saleTime );
239+ $ charge3 = $ this ->calculator ->calculateCharge ($ price , $ action3 );
240+ $ charges3 = $ once ->modifyCharge ($ charge3 , $ action3 );
241+ $ this ->assertCount (1 , $ charges3 );
242+ $ this ->assertZeroCharge ($ charges3 [0 ]);
243+
244+ // Action before since date: zero charge
245+ $ actionTime4 = new DateTimeImmutable ('2025-03-01 ' );
246+ $ action4 = $ this ->createActionWithSale ($ actionTime4 , $ saleTime );
247+ $ charge4 = $ this ->calculator ->calculateCharge ($ price , $ action4 );
248+ $ charges4 = $ once ->modifyCharge ($ charge4 , $ action4 );
249+ $ this ->assertCount (1 , $ charges4 );
250+ $ this ->assertZeroCharge ($ charges4 [0 ]);
251+ }
190252}
0 commit comments