@@ -1204,7 +1204,6 @@ func TestV3AdvanceBillingInvoice(t *testing.T) {
12041204
12051205 var (
12061206 customerID string
1207- planID string
12081207 invoiceID string // standard invoice ID
12091208 gatheringInvoiceID string // gathering invoice ID
12101209 )
@@ -1229,14 +1228,9 @@ func TestV3AdvanceBillingInvoice(t *testing.T) {
12291228 // right after creation (auto-advance is otherwise instantaneous with the default
12301229 // P0D draft period, which would leave nothing in draft to advance). Once the
12311230 // period elapses, the first advance call unblocks trigger_next and drives the
1232- // invoice forward; the second call then finds no further automatic transition.
1233- //
1234- // The default profile's payment settings auto-charge via CollectionMethodChargeAutomatically,
1235- // which (with credits enabled in e2e) settles the invoice to "paid" synchronously
1236- // the moment it's advanced past issued — leaving nothing observable to assert on.
1237- // Overriding payment to send_invoice (same as the manual-approval profiles used
1238- // elsewhere in this file) stops that auto-settlement, so advancing lands on a real,
1239- // inspectable non-final status instead.
1231+ // invoice forward through every further auto-advance-eligible transition in one
1232+ // shot — including settlement — so it lands on "paid" directly; the second call
1233+ // then finds no further transition available.
12401234 profile := createNewBillingProfileFromDefault (t , c , uniqueKey ("advance_invoice" ), func (profile * v3sdk.CreateBillingProfileRequest ) {
12411235 profile .Name = uniqueKey ("advance_invoice_profile" )
12421236 if profile .Workflow .Invoicing == nil {
@@ -1261,127 +1255,27 @@ func TestV3AdvanceBillingInvoice(t *testing.T) {
12611255 require .NotEmpty (t , customerID , "depends on customer creation" )
12621256
12631257 now := time .Now ().UTC ()
1264- price := api.RateCardUsageBasedPrice {}
1265- require .NoError (t , price .FromFlatPriceWithPaymentTerm (api.FlatPriceWithPaymentTerm {
1266- Amount : api .Numeric ("10.00" ),
1267- Type : api .FlatPriceWithPaymentTermTypeFlat ,
1268- PaymentTerm : lo .ToPtr (api .PricePaymentTermInAdvance ),
1269- }))
1270-
12711258 invoiceAt := now .Add (time .Hour )
1272- lineResp , err := v1 .CreatePendingInvoiceLineWithResponse (t .Context (), customerID , api.InvoicePendingLineCreateInput {
1273- Currency : "USD" ,
1274- Lines : []api.InvoicePendingLineCreate {
1275- {
1276- Name : uniqueKey ("advance_inv_gathering_line" ),
1277- InvoiceAt : invoiceAt ,
1278- Period : api.Period {
1279- From : now .Add (- 24 * time .Hour ),
1280- To : invoiceAt ,
1281- },
1282- Price : & price ,
1283- },
1284- },
1285- })
1286- require .NoError (t , err )
1287- require .Equal (t , http .StatusCreated , lineResp .StatusCode (), "line: %s" , string (lineResp .Body ))
1288- require .NotNil (t , lineResp .JSON201 )
1289-
1290- gatheringInvoiceID = (* lineResp .JSON201 ).Invoice .Id
1291- require .NotEmpty (t , gatheringInvoiceID )
1292- })
1293-
1294- t .Run ("Should create meter, feature, plan, and subscription" , func (t * testing.T ) {
1295- require .NotEmpty (t , customerID , "depends on customer creation" )
1296-
1297- // An active subscription is what actually drives the billing-worker's sync
1298- // loop for this customer; without one, a raw past-due pending line just sits
1299- // in the gathering invoice forever (nothing else periodically collects it).
1300- meter , err := c .Meters .Create (t .Context (), v3sdk.CreateMeterRequest {
1301- Key : uniqueKey ("advanceinv_meter" ),
1302- Name : gofakeit .ProductName (),
1303- Aggregation : v3sdk .MeterAggregationCount ,
1304- EventType : uniqueKey ("advanceinv_event" ),
1305- })
1306- c .requireStatus (http .StatusCreated , err )
1307-
1308- feature , err := c .Features .Create (t .Context (), v3sdk.CreateFeatureRequest {
1309- Key : uniqueKey ("advanceinv_feature" ),
1310- Name : gofakeit .ProductName (),
1311- Meter : & v3sdk.FeatureMeterReferenceInput {ID : meter .ID },
1312- })
1313- c .requireStatus (http .StatusCreated , err )
1314-
1315- plan , err := c .Plans .Create (t .Context (), v3sdk.CreatePlanRequest {
1316- Key : uniqueKey ("advanceinv_plan" ),
1317- Name : gofakeit .ProductName (),
1318- Currency : "USD" ,
1319- BillingCadence : "P1M" ,
1320- Phases : []v3sdk.PlanPhaseInput {{
1321- Key : uniqueKey ("inv_phase_1" ),
1322- Name : uniqueKey ("Test Phase" ),
1323- RateCards : []v3sdk.RateCardInput {validUnitRateCard (* feature )},
1324- }},
1325- })
1326- c .requireStatus (http .StatusCreated , err )
1327- planID = plan .ID
1328-
1329- plan , err = c .Plans .Publish (t .Context (), planID )
1330- c .requireStatus (http .StatusOK , err )
1331- assert .Equal (t , v3sdk .PlanStatusActive , plan .Status )
1332-
1333- _ , err = c .Subscriptions .Create (t .Context (), v3sdk.SubscriptionCreate {
1334- Customer : v3sdk.SubscriptionChangeCustomer {ID : lo .ToPtr (customerID )},
1335- Plan : v3sdk.SubscriptionChangePlan {ID : lo .ToPtr (planID )},
1259+ gatheringInvoiceID = createFlatPendingLine (t , v1 , customerID , uniqueKey ("advance_inv_gathering_line" ), invoiceAt , api.Period {
1260+ From : now .Add (- 24 * time .Hour ),
1261+ To : invoiceAt ,
13361262 })
1337- c .requireStatus (http .StatusCreated , err )
13381263 })
13391264
13401265 t .Run ("Should create a standard invoice in draft status" , func (t * testing.T ) {
13411266 require .NotEmpty (t , customerID , "depends on customer creation" )
13421267
13431268 now := time .Now ().UTC ()
1344- price := api.RateCardUsageBasedPrice {}
1345- require .NoError (t , price .FromFlatPriceWithPaymentTerm (api.FlatPriceWithPaymentTerm {
1346- Amount : api .Numeric ("10.00" ),
1347- Type : api .FlatPriceWithPaymentTermTypeFlat ,
1348- PaymentTerm : lo .ToPtr (api .PricePaymentTermInAdvance ),
1349- }))
1350-
1351- lineResp , err := v1 .CreatePendingInvoiceLineWithResponse (t .Context (), customerID , api.InvoicePendingLineCreateInput {
1352- Currency : "USD" ,
1353- Lines : []api.InvoicePendingLineCreate {{
1354- Name : uniqueKey ("advanceinv_line" ),
1355- InvoiceAt : now .Add (- 10 * time .Hour ),
1356- Period : api.Period {
1357- From : now .Add (- 24 * time .Hour ),
1358- To : now .Add (- 2 * time .Hour ),
1359- },
1360- Price : & price ,
1361- }},
1269+ createFlatPendingLine (t , v1 , customerID , uniqueKey ("advanceinv_line" ), now .Add (- 10 * time .Hour ), api.Period {
1270+ From : now .Add (- 24 * time .Hour ),
1271+ To : now .Add (- 2 * time .Hour ),
13621272 })
1363- require .NoError (t , err )
1364- require .Equal (t , http .StatusCreated , lineResp .StatusCode ())
1365-
1366- ctx := t .Context ()
1367- customers := api.InvoiceListParamsCustomers {customerID }
1368- assert .EventuallyWithT (t , func (co * assert.CollectT ) {
1369- listResp , err := v1 .ListInvoicesWithResponse (ctx , & api.ListInvoicesParams {
1370- Customers : & customers ,
1371- PageSize : lo .ToPtr (api .PaginationPageSize (100 )),
1372- })
1373- require .NoError (co , err )
1374- require .Equal (co , http .StatusOK , listResp .StatusCode ())
13751273
1376- invoice , found := lo .Find (listResp .JSON200 .Items , func (inv api.Invoice ) bool {
1377- return inv .Status == api .InvoiceStatusDraft
1378- })
1379- assert .True (co , found , "charges have not advanced a pending line into a standard invoice yet" )
1380- if found {
1381- invoiceID = invoice .Id
1382- }
1383- }, time .Minute , time .Second )
1384- require .NotEmpty (t , invoiceID )
1274+ // A raw pending line just sits in the gathering invoice until something
1275+ // explicitly asks for it to be invoiced (see invoicePendingLinesNow).
1276+ invoice := invoicePendingLinesNow (t , v1 , customerID , nil )
1277+ require .Equal (t , api .InvoiceStatusDraft , invoice .Status )
1278+ invoiceID = invoice .Id
13851279 })
13861280
13871281 t .Run ("Should advance the standard invoice via v3 POST" , func (t * testing.T ) {
@@ -1398,10 +1292,9 @@ func TestV3AdvanceBillingInvoice(t *testing.T) {
13981292 stdInv , err := inv .AsInvoiceStandard ()
13991293 require .NoError (co , err )
14001294 assert .Equal (co , invoiceID , stdInv .ID )
1401- // send_invoice payment settings prevent auto-settlement, so advancing
1402- // should land on a real non-final status (e.g. issued/payment_processing),
1403- // not race straight through to paid.
1404- assert .NotEqual (co , v3sdk .InvoiceStandardStatusPaid , stdInv .Status )
1295+ // AutoAdvance cascades through every further auto-advance-eligible
1296+ // transition in one call, so the invoice lands straight on "paid".
1297+ assert .Equal (co , v3sdk .InvoiceStandardStatusPaid , stdInv .Status )
14051298 }, 20 * time .Second , time .Second )
14061299 })
14071300
0 commit comments