@@ -349,6 +349,18 @@ func (h *Handler) correctPeriodStartForUpcomingLines(ctx context.Context, subscr
349349 continue
350350 }
351351
352+ // We are not correcting periods for lines that are already ignored
353+ if existingCurrentLine , ok := existingLinesByUniqueID [line .UniqueID ]; ok {
354+ hasAnnotation , err := h .lineOrHierarchyHasSubscriptionSyncIgnoreAnnotation (existingCurrentLine )
355+ if err != nil {
356+ return nil , fmt .Errorf ("checking if line has subscription sync ignore annotation: %w" , err )
357+ }
358+
359+ if hasAnnotation {
360+ continue
361+ }
362+ }
363+
352364 previousPeriodUniqueID := strings .Join ([]string {
353365 subscriptionID ,
354366 line .PhaseKey ,
@@ -363,27 +375,12 @@ func (h *Handler) correctPeriodStartForUpcomingLines(ctx context.Context, subscr
363375 continue
364376 }
365377
366- switch existingPreviousLine .Type () {
367- case billing .LineOrHierarchyTypeLine :
368- previousLine , err := existingPreviousLine .AsLine ()
369- if err != nil {
370- return nil , fmt .Errorf ("getting previous line: %w" , err )
371- }
372-
373- if ! h .isLineInScopeForPeriodCorrection (previousLine ) {
374- continue
375- }
376- case billing .LineOrHierarchyTypeHierarchy :
377- hierarchy , err := existingPreviousLine .AsHierarchy ()
378- if err != nil {
379- return nil , fmt .Errorf ("getting previous hierarchy: %w" , err )
380- }
381-
382- if ! h .isHierarchyInScopeForPeriodCorrection (hierarchy ) {
383- continue
384- }
378+ existingPreviousLineHasAnnotation , err := h .lineOrHierarchyHasSubscriptionSyncIgnoreAnnotation (existingPreviousLine )
379+ if err != nil {
380+ return nil , fmt .Errorf ("checking if previous line has subscription sync ignore annotation: %w" , err )
381+ }
385382
386- default :
383+ if ! existingPreviousLineHasAnnotation {
387384 continue
388385 }
389386
@@ -394,21 +391,46 @@ func (h *Handler) correctPeriodStartForUpcomingLines(ctx context.Context, subscr
394391 continue
395392 }
396393
397- // Should not happen as this line is never the first line
398- // TODO: harmonize truncation logic here!
399- if ! line .ServicePeriod .Start .Equal (line .BillingPeriod .Start ) || ! line .FullServicePeriod .Start .Equal (line .BillingPeriod .Start ) {
400- return nil , fmt .Errorf ("line[%s] service period start does not match billing period start or full service period start" , line .UniqueID )
394+ if ! line .ServicePeriod .Start .Equal (line .FullServicePeriod .Start ) {
395+ // These should match otherwise any pro-rating logic will be invalid (we are never truncating the start of the service period so this should never happen)
396+ return nil , fmt .Errorf ("line[%s] service period and full service period start does not match" , line .UniqueID )
401397 }
402398
399+ // We are not overriding the billing period start as that is only used to determine the invoiceAt for inAdvance items
403400 inScopeLines [idx ].ServicePeriod .Start = previousServicePeriod .End
404- inScopeLines [idx ].BillingPeriod .Start = previousServicePeriod .End
405401 inScopeLines [idx ].FullServicePeriod .Start = previousServicePeriod .End
402+
403+ if line .FullServicePeriod .Start .Equal (line .BillingPeriod .Start ) {
404+ // If the billing period is not truncated, we can update the line's billing period start too
405+ inScopeLines [idx ].BillingPeriod .Start = previousServicePeriod .End
406+ }
406407 }
407408
408409 return inScopeLines , nil
409410}
410411
411- func (h * Handler ) isLineInScopeForPeriodCorrection (line * billing.Line ) bool {
412+ func (h * Handler ) lineOrHierarchyHasSubscriptionSyncIgnoreAnnotation (lineOrHierarchy billing.LineOrHierarchy ) (bool , error ) {
413+ switch lineOrHierarchy .Type () {
414+ case billing .LineOrHierarchyTypeLine :
415+ previousLine , err := lineOrHierarchy .AsLine ()
416+ if err != nil {
417+ return false , fmt .Errorf ("getting previous line: %w" , err )
418+ }
419+
420+ return h .lineHasSubscriptionSyncIgnoreAnnotation (previousLine ), nil
421+ case billing .LineOrHierarchyTypeHierarchy :
422+ hierarchy , err := lineOrHierarchy .AsHierarchy ()
423+ if err != nil {
424+ return false , fmt .Errorf ("getting previous hierarchy: %w" , err )
425+ }
426+
427+ return h .hierarchyHasSubscriptionSyncIgnoreAnnotation (hierarchy ), nil
428+ default :
429+ return false , nil
430+ }
431+ }
432+
433+ func (h * Handler ) lineHasSubscriptionSyncIgnoreAnnotation (line * billing.Line ) bool {
412434 if line .ManagedBy != billing .SubscriptionManagedLine {
413435 // We only correct the period start for subscription managed lines, for manual edits
414436 // we should not apply this logic, as the user might have created a setup where the period start
@@ -434,13 +456,13 @@ func (h *Handler) isLineInScopeForPeriodCorrection(line *billing.Line) bool {
434456 return boolVal
435457}
436458
437- func (h * Handler ) isHierarchyInScopeForPeriodCorrection (hierarchy * billing.SplitLineHierarchy ) bool {
459+ func (h * Handler ) hierarchyHasSubscriptionSyncIgnoreAnnotation (hierarchy * billing.SplitLineHierarchy ) bool {
438460 servicePeriod := hierarchy .Group .ServicePeriod
439461
440462 // The correction can only happen if the last line the progressively billed group is in scope for the period correction
441463 for _ , line := range hierarchy .Lines {
442464 if line .Line .Period .End .Equal (servicePeriod .End ) {
443- return h .isLineInScopeForPeriodCorrection (line .Line )
465+ return h .lineHasSubscriptionSyncIgnoreAnnotation (line .Line )
444466 }
445467 }
446468
0 commit comments