From afe7000128bd93947d2aab5cd02d23da90211f45 Mon Sep 17 00:00:00 2001 From: Rohil Surana Date: Mon, 13 Jul 2026 20:44:16 +0530 Subject: [PATCH] feat(billing): use credit product title as overdraft invoice line item name --- billing/invoice/service.go | 19 +++++++++++++++++-- billing/invoice/service_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/billing/invoice/service.go b/billing/invoice/service.go index eeb30b608..03e0129f1 100644 --- a/billing/invoice/service.go +++ b/billing/invoice/service.go @@ -33,6 +33,9 @@ const ( // MonthCreditRangeForInvoice only month is supported for now, could be week or year MonthCreditRangeForInvoice = "month" CreditOverdraftDescription = "Invoice for the underpayment of credit utilization" + // DefaultCreditOverdraftItemName is the invoice line item description + // used when the overdraft product has no title + DefaultCreditOverdraftItemName = "Credit Overdraft" ) type Repository interface { @@ -80,6 +83,7 @@ type Service struct { stripeAutoTax bool creditOverdraftProduct string + creditOverdraftItemName string creditOverdraftUnitAmount int64 creditOverdraftInvoiceCurrency string creditOverdraftInvoiceDay int @@ -135,9 +139,11 @@ func (s *Service) Init(ctx context.Context) error { } s.creditOverdraftInvoiceCurrency = creditPrice.Currency s.creditOverdraftUnitAmount = int64(float64(creditPrice.Amount) / float64(creditProduct.Config.CreditAmount)) + s.creditOverdraftItemName = creditProduct.Title s.log.InfoContext(ctx, "credit overdraft product details", "unit_amount", s.creditOverdraftUnitAmount, - "currency", s.creditOverdraftInvoiceCurrency) + "currency", s.creditOverdraftInvoiceCurrency, + "item_name", s.overdraftItemName()) } return nil } @@ -529,7 +535,7 @@ func (s *Service) GenerateForCredits(ctx context.Context) error { items := []Item{ { ID: uuid.New().String(), - Name: "Credit Overdraft", + Name: s.overdraftItemName(), Type: CreditItemType, UnitAmount: s.creditOverdraftUnitAmount, Quantity: abs(balance), @@ -580,6 +586,15 @@ func computeOverdraftWindow(startRange time.Time, endRange time.Time, lastOverdr return startRange, alreadyInvoiced } +// overdraftItemName returns the description used on the credit overdraft +// invoice line item, the overdraft product title when available +func (s *Service) overdraftItemName() string { + if s.creditOverdraftItemName != "" { + return s.creditOverdraftItemName + } + return DefaultCreditOverdraftItemName +} + // getCreditOverdraftEndDate get range start and end, end date will be the current date with time set to 00:00:00 // start date will be the end date minus creditOverdraftRangeOfInvoice func (s *Service) getCreditOverdraftEndDate(current time.Time) time.Time { diff --git a/billing/invoice/service_test.go b/billing/invoice/service_test.go index ebbe6e39d..ac0b81da8 100644 --- a/billing/invoice/service_test.go +++ b/billing/invoice/service_test.go @@ -150,3 +150,29 @@ func TestService_getCreditOverdraftRange(t *testing.T) { }) } } + +func TestService_overdraftItemName(t *testing.T) { + tests := []struct { + name string + itemName string + want string + }{ + { + name: "uses the overdraft product title when set", + itemName: "For Satellite Images and Data Analytics Services", + want: "For Satellite Images and Data Analytics Services", + }, + { + name: "falls back to default when product title is empty", + want: DefaultCreditOverdraftItemName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Service{creditOverdraftItemName: tt.itemName} + if got := s.overdraftItemName(); got != tt.want { + t.Errorf("overdraftItemName() = %v, want %v", got, tt.want) + } + }) + } +}