@@ -74,21 +74,23 @@ pub fn calculate_component_period_for_invoice_date(
7474}
7575
7676fn calculate_proration_factor ( period : & Period ) -> Option < f64 > {
77- let days_in_period = period. end . signed_duration_since ( period. start ) . num_days ( ) as u64 ; // +1 ?
77+ // +1 because inclusive
78+ let days_in_period = period. end . signed_duration_since ( period. start ) . num_days ( ) as u64 + 1 ;
7879 let days_in_month_from = period. start . days_in_month ( ) as u64 ;
7980 let days_in_month_to = period. end . days_in_month ( ) as u64 ;
8081
81- // if from is end of month and from.day <= to.day. Ex: 2023-02-28 -> 2023-03-28+
82- if period. start . day ( ) == days_in_month_from as u32 && period. end . day ( ) >= period. start . day ( ) {
82+ // if from is end of month and from.day <= to.day. Ex: 2023-02-28 -> 2023-03-27+
83+ if period. start . day ( ) == days_in_month_from as u32 && period. end . day ( ) >= period. start . day ( ) - 1
84+ {
8385 return None ;
8486 }
8587
8688 if days_in_period >= days_in_month_from {
8789 return None ;
8890 }
8991
90- // if to is end of month and from.day >= to.day. Ex: 2023-01-28+ -> 2023-02-28
91- if period. end . day ( ) == days_in_month_to as u32 && period. start . day ( ) >= period. end . day ( ) {
92+ // if to is end of month and from.day >= to.day. Ex: 2023-01-28+ -> 2023-02-27
93+ if period. end . day ( ) == days_in_month_to as u32 && period. start . day ( ) > period. end . day ( ) {
9294 return None ;
9395 }
9496
@@ -122,17 +124,19 @@ pub fn calculate_advance_period_range(
122124 let period_end = {
123125 let should_add_full_period = started_after_billing_day || !is_partial;
124126
125- if should_add_full_period {
127+ let next_period_start = if should_add_full_period {
126128 add_months_at_billing_day ( period_start, months_per_period, billing_day)
127129 . expect ( "Failed to calculate period end date" )
128130 } else {
129131 // For the first period when billing started before the billing day,
130- // end on the billing day of the current month
132+ // end on the day before the billing day of the current month
131133 let target_day = period_start. days_in_month ( ) . min ( billing_day) ;
132134 period_start
133135 . with_day ( target_day)
134136 . expect ( "Failed to set period end day" )
135- }
137+ } ;
138+ // subtract one day to make the end date inclusive
139+ next_period_start. pred_opt ( ) . unwrap_or ( next_period_start)
136140 } ;
137141
138142 Period {
@@ -150,9 +154,10 @@ pub fn calculate_arrear_period_range(
150154 let months_per_period = billing_period. as_months ( ) ;
151155
152156 // For arrear billing, we're billing for a period that has already ended
153- let period_end = invoice_date;
157+ // The period ends the day before the invoice date (inclusive)
158+ let period_end = invoice_date. pred_opt ( ) . unwrap_or ( invoice_date) ;
154159
155- let period_start = subtract_months_at_billing_day ( period_end , months_per_period, billing_day)
160+ let period_start = subtract_months_at_billing_day ( invoice_date , months_per_period, billing_day)
156161 . expect ( "Failed to calculate arrear period start" )
157162 . max ( billing_start_or_resume_date) ;
158163
@@ -200,143 +205,143 @@ mod test {
200205 1 ,
201206 true , // is_first_period
202207 "2021-01-01" ,
203- "2021-02-01 "
208+ "2021-01-31 "
204209 ) ]
205210 #[ case(
206211 BillingPeriodEnum :: Monthly ,
207212 "2021-02-01" , // invoice_date (start of second period)
208213 1 ,
209214 false , // not first period
210215 "2021-02-01" ,
211- "2021-03-01 "
216+ "2021-02-28 "
212217 ) ]
213218 #[ case(
214219 BillingPeriodEnum :: Monthly ,
215220 "2021-03-01" , // invoice_date (start of third period)
216221 1 ,
217222 false ,
218223 "2021-03-01" ,
219- "2021-04-01 "
224+ "2021-03-31 "
220225 ) ]
221226 #[ case(
222227 BillingPeriodEnum :: Monthly ,
223228 "2021-01-10" , // invoice_date
224229 1 ,
225230 true , // first period
226231 "2021-01-10" ,
227- "2021-02-01 "
232+ "2021-01-31 "
228233 ) ]
229234 #[ case(
230235 BillingPeriodEnum :: Monthly ,
231236 "2021-02-01" , // invoice_date
232237 1 ,
233238 false ,
234239 "2021-02-01" ,
235- "2021-03-01 "
240+ "2021-02-28 "
236241 ) ]
237242 #[ case(
238243 BillingPeriodEnum :: Monthly ,
239244 "2021-03-01" , // invoice_date
240245 1 ,
241246 false ,
242247 "2021-03-01" ,
243- "2021-04-01 "
248+ "2021-03-31 "
244249 ) ]
245250 #[ case(
246251 BillingPeriodEnum :: Monthly ,
247252 "2021-01-01" , // invoice_date
248253 10 ,
249254 true , // first period
250255 "2021-01-01" ,
251- "2021-01-10 "
256+ "2021-01-09 "
252257 ) ]
253258 #[ case(
254259 BillingPeriodEnum :: Monthly ,
255260 "2021-01-10" , // invoice_date
256261 10 ,
257262 false ,
258263 "2021-01-10" ,
259- "2021-02-10 "
264+ "2021-02-09 "
260265 ) ]
261266 #[ case(
262267 BillingPeriodEnum :: Monthly ,
263268 "2021-02-10" , // invoice_date
264269 10 ,
265270 false ,
266271 "2021-02-10" ,
267- "2021-03-10 "
272+ "2021-03-09 "
268273 ) ]
269274 #[ case(
270275 BillingPeriodEnum :: Quarterly ,
271276 "2021-01-10" , // invoice_date
272277 1 ,
273278 true ,
274279 "2021-01-10" ,
275- "2021-04-01 "
280+ "2021-03-31 "
276281 ) ]
277282 #[ case(
278283 BillingPeriodEnum :: Quarterly ,
279284 "2021-04-01" , // invoice_date
280285 1 ,
281286 false ,
282287 "2021-04-01" ,
283- "2021-07-01 "
288+ "2021-06-30 "
284289 ) ]
285290 #[ case(
286291 BillingPeriodEnum :: Quarterly ,
287292 "2021-07-01" , // invoice_date
288293 1 ,
289294 false ,
290295 "2021-07-01" ,
291- "2021-10-01 "
296+ "2021-09-30 "
292297 ) ]
293298 #[ case(
294299 BillingPeriodEnum :: Quarterly ,
295300 "2021-01-01" , // invoice_date
296301 10 ,
297302 true ,
298303 "2021-01-01" ,
299- "2021-01-10 "
304+ "2021-01-09 "
300305 ) ]
301306 #[ case(
302307 BillingPeriodEnum :: Quarterly ,
303308 "2021-01-10" , // invoice_date
304309 10 ,
305310 false ,
306311 "2021-01-10" ,
307- "2021-04-10 "
312+ "2021-04-09 "
308313 ) ]
309314 #[ case(
310315 BillingPeriodEnum :: Quarterly ,
311316 "2021-04-10" , // invoice_date
312317 10 ,
313318 false ,
314319 "2021-04-10" ,
315- "2021-07-10 "
320+ "2021-07-09 "
316321 ) ]
317322 #[ case(
318323 BillingPeriodEnum :: Annual ,
319324 "2021-01-10" , // invoice_date
320325 1 ,
321326 true ,
322327 "2021-01-10" ,
323- "2022-01-01 "
328+ "2021-12-31 "
324329 ) ]
325330 #[ case(
326331 BillingPeriodEnum :: Annual ,
327332 "2022-01-01" , // invoice_date
328333 1 ,
329334 false ,
330335 "2022-01-01" ,
331- "2023-01-01 "
336+ "2022-12-31 "
332337 ) ]
333338 #[ case(
334339 BillingPeriodEnum :: Annual ,
335340 "2023-01-01" , // invoice_date
336341 1 ,
337342 false ,
338343 "2023-01-01" ,
339- "2024-01-01 "
344+ "2023-12-31 "
340345 ) ]
341346 fn test_calculate_advance_period_range (
342347 #[ case] billing_period : BillingPeriodEnum ,
@@ -364,79 +369,79 @@ mod test {
364369 "2021-01-01" , // billing_start_date
365370 1 ,
366371 "2021-01-01" , // max(calculated_start, billing_start) = max(2021-01-01, 2021-01-01)
367- "2021-02-01 "
372+ "2021-01-31 "
368373 ) ]
369374 #[ case(
370375 BillingPeriodEnum :: Monthly ,
371376 "2021-03-01" , // invoice_date
372377 "2021-01-01" , // billing_start_date
373378 1 ,
374379 "2021-02-01" , // calculated back 1 month from 2021-03-01
375- "2021-03-01 "
380+ "2021-02-28 "
376381 ) ]
377382 #[ case(
378383 BillingPeriodEnum :: Monthly ,
379384 "2021-04-01" , // invoice_date
380385 "2021-01-01" , // billing_start_date
381386 1 ,
382387 "2021-03-01" , // calculated back 1 month from 2021-04-01
383- "2021-04-01 "
388+ "2021-03-31 "
384389 ) ]
385390 #[ case(
386391 BillingPeriodEnum :: Monthly ,
387392 "2021-02-01" , // invoice_date
388393 "2021-01-10" , // billing_start_date (service started mid-month)
389394 1 ,
390395 "2021-01-10" , // max(2021-01-01, 2021-01-10) = 2021-01-10
391- "2021-02-01 "
396+ "2021-01-31 "
392397 ) ]
393398 #[ case(
394399 BillingPeriodEnum :: Monthly ,
395400 "2021-01-10" , // invoice_date
396401 "2021-01-01" , // billing_start_date
397402 10 ,
398403 "2021-01-01" , // max(2020-12-10, 2021-01-01) = 2021-01-01
399- "2021-01-10 "
404+ "2021-01-09 "
400405 ) ]
401406 #[ case(
402407 BillingPeriodEnum :: Monthly ,
403408 "2021-02-10" , // invoice_date
404409 "2021-01-01" , // billing_start_date
405410 10 ,
406411 "2021-01-10" , // calculated back 1 month from 2021-02-10
407- "2021-02-10 "
412+ "2021-02-09 "
408413 ) ]
409414 #[ case(
410415 BillingPeriodEnum :: Quarterly ,
411416 "2021-04-01" , // invoice_date
412417 "2021-01-10" , // billing_start_date
413418 1 ,
414419 "2021-01-10" , // max(2021-01-01, 2021-01-10) = 2021-01-10
415- "2021-04-01 "
420+ "2021-03-31 "
416421 ) ]
417422 #[ case(
418423 BillingPeriodEnum :: Quarterly ,
419424 "2021-07-01" , // invoice_date
420425 "2021-01-10" , // billing_start_date
421426 1 ,
422427 "2021-04-01" , // calculated back 3 months from 2021-07-01
423- "2021-07-01 "
428+ "2021-06-30 "
424429 ) ]
425430 #[ case(
426431 BillingPeriodEnum :: Annual ,
427432 "2022-01-01" , // invoice_date
428433 "2021-01-10" , // billing_start_date
429434 1 ,
430435 "2021-01-10" , // max(2021-01-01, 2021-01-10) = 2021-01-10
431- "2022-01-01 "
436+ "2021-12-31 "
432437 ) ]
433438 #[ case(
434439 BillingPeriodEnum :: Annual ,
435440 "2023-01-01" , // invoice_date
436441 "2021-01-10" , // billing_start_date
437442 1 ,
438443 "2022-01-01" , // calculated back 12 months from 2023-01-01
439- "2023-01-01 "
444+ "2022-12-31 "
440445 ) ]
441446 fn test_calculate_arrear_period_range (
442447 #[ case] billing_period : BillingPeriodEnum ,
0 commit comments