@@ -28,6 +28,7 @@ import (
2828type chargesWithInvoiceNowActions struct {
2929 charges charges.Charges
3030 collectionAlignmentBypassedLines []invoicePendingLinesInput
31+ pendingLineResults []* billing.CreatePendingInvoiceLinesResult
3132}
3233
3334// applyDefaultTaxCodes fills in nil TaxCodeID on each intent's TaxConfig using the namespace's
@@ -67,6 +68,27 @@ func (s *service) applyDefaultTaxCodes(ctx context.Context, namespace string, in
6768}
6869
6970func (s * service ) Create (ctx context.Context , input charges.CreateInput ) (charges.Charges , error ) {
71+ result , err := s .create (ctx , input )
72+ if err != nil {
73+ return nil , err
74+ }
75+
76+ if result == nil {
77+ return nil , fmt .Errorf ("result is nil" )
78+ }
79+
80+ // TODO: once we have proper state machine for credit purchases, we can remove this and make the
81+ // autoAdvanceCreatedCharges handle the invoice now actions.
82+ if len (result .collectionAlignmentBypassedLines ) > 0 {
83+ if err := s .invokeInvoiceNowOnCreate (ctx , result .collectionAlignmentBypassedLines ); err != nil {
84+ return nil , fmt .Errorf ("invoking invoice now on create: %w" , err )
85+ }
86+ }
87+
88+ return s .autoAdvanceCreatedCharges (ctx , result .charges )
89+ }
90+
91+ func (s * service ) create (ctx context.Context , input charges.CreateInput ) (* chargesWithInvoiceNowActions , error ) {
7092 if input .Namespace == "" {
7193 return nil , fmt .Errorf ("namespace is required" )
7294 }
@@ -208,7 +230,7 @@ func (s *service) Create(ctx context.Context, input charges.CreateInput) (charge
208230 }
209231
210232 // Let's generate the gathering lines for the flat fees
211- collectionAlignmentBypassedLines , err := s .createGatheringLines (ctx , gatheringLinesToCreate )
233+ gatheringLineResult , err := s .createGatheringLines (ctx , gatheringLinesToCreate )
212234 if err != nil {
213235 return nil , err
214236 }
@@ -225,26 +247,15 @@ func (s *service) Create(ctx context.Context, input charges.CreateInput) (charge
225247
226248 return & chargesWithInvoiceNowActions {
227249 charges : result ,
228- collectionAlignmentBypassedLines : collectionAlignmentBypassedLines ,
250+ collectionAlignmentBypassedLines : gatheringLineResult .collectionAlignmentBypassedLines ,
251+ pendingLineResults : gatheringLineResult .pendingLineResults ,
229252 }, nil
230253 })
231254 if err != nil {
232255 return nil , err
233256 }
234257
235- if result == nil {
236- return nil , fmt .Errorf ("result is nil" )
237- }
238-
239- // TODO: once we have proper state machine for credit purchases, we can remove this and mek the
240- // autoAdvanceCreatedCharges handle the invoice now actions.
241- if len (result .collectionAlignmentBypassedLines ) > 0 {
242- if err := s .invokeInvoiceNowOnCreate (ctx , result .collectionAlignmentBypassedLines ); err != nil {
243- return nil , fmt .Errorf ("invoking invoice now on create: %w" , err )
244- }
245- }
246-
247- return s .autoAdvanceCreatedCharges (ctx , result .charges )
258+ return result , nil
248259}
249260
250261// autoAdvanceCreatedCharges post-processes newly created charges
@@ -379,9 +390,14 @@ type invoicePendingLinesInput struct {
379390 LineID string
380391}
381392
382- func (s * service ) createGatheringLines (ctx context.Context , gatheringLinesToCreate []gatheringLineWithCustomerID ) ([]invoicePendingLinesInput , error ) {
393+ type createGatheringLinesResult struct {
394+ collectionAlignmentBypassedLines []invoicePendingLinesInput
395+ pendingLineResults []* billing.CreatePendingInvoiceLinesResult
396+ }
397+
398+ func (s * service ) createGatheringLines (ctx context.Context , gatheringLinesToCreate []gatheringLineWithCustomerID ) (createGatheringLinesResult , error ) {
383399 if len (gatheringLinesToCreate ) == 0 {
384- return nil , nil
400+ return createGatheringLinesResult {} , nil
385401 }
386402
387403 gatheringLinesByCurrencyAndCustomer := lo .GroupBy (gatheringLinesToCreate , func (item gatheringLineWithCustomerID ) currencyAndCustomerID {
@@ -391,7 +407,10 @@ func (s *service) createGatheringLines(ctx context.Context, gatheringLinesToCrea
391407 }
392408 })
393409
394- invoiceNowLines := make ([]invoicePendingLinesInput , 0 , len (gatheringLinesToCreate ))
410+ out := createGatheringLinesResult {
411+ collectionAlignmentBypassedLines : make ([]invoicePendingLinesInput , 0 , len (gatheringLinesToCreate )),
412+ pendingLineResults : make ([]* billing.CreatePendingInvoiceLinesResult , 0 , len (gatheringLinesByCurrencyAndCustomer )),
413+ }
395414
396415 for custAndCurrency , lines := range gatheringLinesByCurrencyAndCustomer {
397416 // Let's create the gathering invoice on invoicing side
@@ -403,18 +422,43 @@ func (s *service) createGatheringLines(ctx context.Context, gatheringLinesToCrea
403422 }),
404423 })
405424 if err != nil {
406- return nil , fmt .Errorf ("creating pending invoice lines for charges: %w" , err )
425+ return createGatheringLinesResult {}, fmt .Errorf ("creating pending invoice lines for charges: %w" , err )
426+ }
427+ if result == nil {
428+ return createGatheringLinesResult {}, fmt .Errorf ("creating pending invoice lines for charges: result is nil" )
407429 }
408430
409- for idx , line := range result .Lines {
410- if lines [idx ].BypassCollectionAlignment {
411- invoiceNowLines = append (invoiceNowLines , invoicePendingLinesInput {
431+ out .pendingLineResults = append (out .pendingLineResults , result )
432+
433+ // Correlate the returned lines back to their inputs by charge ID rather than by
434+ // position: billing may drop lines (e.g. zero-amount lines), which would make
435+ // index-based correlation silently read BypassCollectionAlignment from the wrong line.
436+ bypassChargeIDs := make (map [string ]struct {}, len (lines ))
437+ for _ , line := range lines {
438+ if ! line .BypassCollectionAlignment {
439+ continue
440+ }
441+
442+ if line .gatheringLine .ChargeID == nil {
443+ return createGatheringLinesResult {}, fmt .Errorf ("creating pending invoice lines for charges: bypass collection alignment requested for line without charge ID" )
444+ }
445+
446+ bypassChargeIDs [* line .gatheringLine .ChargeID ] = struct {}{}
447+ }
448+
449+ for _ , line := range result .Lines {
450+ if line .ChargeID == nil {
451+ continue
452+ }
453+
454+ if _ , ok := bypassChargeIDs [* line .ChargeID ]; ok {
455+ out .collectionAlignmentBypassedLines = append (out .collectionAlignmentBypassedLines , invoicePendingLinesInput {
412456 CustomerID : custAndCurrency .customerID ,
413457 LineID : line .ID ,
414458 })
415459 }
416460 }
417461 }
418462
419- return invoiceNowLines , nil
463+ return out , nil
420464}
0 commit comments