diff --git a/api/spec/packages/aip-client-javascript/README.md b/api/spec/packages/aip-client-javascript/README.md index 47226a31dd..2463a9ccf2 100644 --- a/api/spec/packages/aip-client-javascript/README.md +++ b/api/spec/packages/aip-client-javascript/README.md @@ -194,10 +194,11 @@ The full call path, HTTP route, and a short description are listed below. ### Invoices -| Method | HTTP | Description | -| ---------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `client.invoices.list` | `GET /openmeter/billing/invoices` | List billing invoices. Returns a page of invoices. Gathering invoices are never included. Use `filter` to narrow by status, customer, dates, or service period start. Use `sort` to control ordering. | -| `client.invoices.get` | `GET /openmeter/billing/invoices/{invoiceId}` | Get a billing invoice by ID. Returns the full invoice resource including line items, status details, totals, and workflow configuration snapshot. | +| Method | HTTP | Description | +| ------------------------ | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `client.invoices.list` | `GET /openmeter/billing/invoices` | List billing invoices. Returns a page of invoices. Gathering invoices are never included. Use `filter` to narrow by status, customer, dates, or service period start. Use `sort` to control ordering. | +| `client.invoices.get` | `GET /openmeter/billing/invoices/{invoiceId}` | Get a billing invoice by ID. Returns the full invoice resource including line items, status details, totals, and workflow configuration snapshot. | +| `client.invoices.update` | `PUT /openmeter/billing/invoices/{invoiceId}` | Update a billing invoice. Only the mutable fields of the invoice can be edited: description, labels, supplier, customer, workflow settings, and top-level lines. Top-level lines are matched by `id`; lines without an `id` are created, and existing lines omitted from `lines` are deleted. Detailed (child) lines are always computed and cannot be edited directly. Only invoices in draft status can be updated. | ### Tax diff --git a/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts b/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts index f8f16053b8..2f35704190 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts @@ -7,6 +7,8 @@ import type { ListInvoicesResponse, GetInvoiceRequest, GetInvoiceResponse, + UpdateInvoiceRequest, + UpdateInvoiceResponse, } from '../models/operations/invoices.js' export function listInvoices( @@ -38,3 +40,18 @@ export function getInvoice( http(client).get(path, options).json(), ) } + +export function updateInvoice( + client: Client, + req: UpdateInvoiceRequest, + options?: RequestOptions, +): Promise> { + const path = encodePath('openmeter/billing/invoices/{invoiceId}', { + invoiceId: req.invoiceId, + }) + return request(() => + http(client) + .put(path, { ...options, json: req.body }) + .json(), + ) +} diff --git a/api/spec/packages/aip-client-javascript/src/index.ts b/api/spec/packages/aip-client-javascript/src/index.ts index e7b0c87659..3acf9aab33 100644 --- a/api/spec/packages/aip-client-javascript/src/index.ts +++ b/api/spec/packages/aip-client-javascript/src/index.ts @@ -75,6 +75,11 @@ export type { InvoiceAvailableActionDetails, InvoiceWorkflowInvoicingSettings, InvoiceLineExternalReferences, + UpdateLabels, + UpdateBillingInvoiceWorkflowInvoicingSettings, + UpdateBillingWorkflowPaymentChargeAutomaticallySettings, + UpdateBillingWorkflowPaymentSendInvoiceSettings, + UpdatePriceFree, LlmCostProvider, LlmCostModel, ProductCatalogValidationError, @@ -100,6 +105,9 @@ export type { Totals, SpendCommitments, InvoiceLineCreditsApplied, + UpdatePriceFlat, + UpdatePriceUnit, + UpdateDiscounts, FeatureManualUnitCost, FeatureLlmUnitCostPricing, LlmCostModelPricing, @@ -119,11 +127,13 @@ export type { FeatureReference, AppReference, ChargeReference, + UpdateResourceReference, Event, MeterQueryRow, AppStripeCreateCustomerPortalSessionResult, ClosedPeriod, SubscriptionAddonTimelineSegment, + UpdateClosedPeriod, CostBasis, CreateCostBasisRequest, FeatureCostQueryRow, @@ -158,7 +168,9 @@ export type { QueryFilterStringMapItem, CustomerKeyReference, CustomerUsageAttribution, + UpdateCustomerUsageAttribution, Address, + UpdateAddress, AppStripeCreateCheckoutSessionCustomerUpdate, AppStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreement, AppStripeCreateCheckoutSessionTaxIdCollection, @@ -181,6 +193,7 @@ export type { AppCatalogItem, TaxCodeAppMapping, PartyTaxIdentity, + UpdateBillingPartyTaxIdentity, WorkflowInvoicingSettings, InvoiceValidationIssue, InvoiceAvailableActions, @@ -202,6 +215,7 @@ export type { CreditTransaction, PriceTier, ChargeTotals, + UpdatePriceTier, FeatureLlmUnitCost, LlmCostPrice, LlmCostOverrideCreate, @@ -219,6 +233,7 @@ export type { CreatePlanAddonRequest, ProfileAppReferences, InvoiceWorkflowAppsReferences, + UpdateRateCardTaxConfig, ListEventsParamsFilter, ListInvoicesParamsFilter, ResourceFilters, @@ -235,6 +250,8 @@ export type { UpsertCustomerRequest, PartyAddresses, InvoiceCustomer, + UpdateBillingPartyAddresses, + UpdateInvoiceCustomer, AppStripeCreateCheckoutSessionConsentCollection, ListCustomerEntitlementAccessResponseData, WorkflowCollectionAlignmentAnchored, @@ -254,6 +271,7 @@ export type { InvoiceWorkflow, InvoiceStatusDetails, InvoiceLineDiscounts, + UpdateBillingInvoiceWorkflow, GovernanceFeatureAccess, CustomerData, UpsertCustomerBillingDataRequest, @@ -261,6 +279,7 @@ export type { CreditTransactionPaginatedResponse, PriceGraduated, PriceVolume, + Array, PricePagePaginatedResponse, CreateCreditGrantRequest, CreditGrant, @@ -273,12 +292,16 @@ export type { CustomerPagePaginatedResponse, Party, Supplier, + UpdateSupplier, AppStripeCreateCheckoutSessionRequestOptions, TaxCodePagePaginatedResponse, InvoiceWorkflowSettings, InvoiceDetailedLine, + UpdateInvoiceWorkflowSettings, CurrencyPagePaginatedResponse, GovernanceQueryResult, + UpdatePriceGraduated, + UpdatePriceVolume, Feature, CreateFeatureRequest, UpdateFeatureRequest, @@ -304,6 +327,7 @@ export type { CreateAddonRequest, UpsertAddonRequest, InvoiceStandardLine, + UpdateInvoiceLineRateCard, Profile, CreateBillingProfileRequest, UpsertBillingProfileRequest, @@ -312,16 +336,20 @@ export type { CreatePlanRequest, UpsertPlanRequest, AddonPagePaginatedResponse, + UpdateInvoiceStandardLine, ProfilePagePaginatedResponse, ChargePagePaginatedResponse, SubscriptionAddonPagePaginatedResponse, PlanPagePaginatedResponse, InvoiceStandard, InvoicePagePaginatedResponse, + UpdateInvoiceStandardRequest, SortQueryInput, BaseErrorInput, WorkflowPaymentSendInvoiceSettingsInput, InvoiceWorkflowInvoicingSettingsInput, + UpdateBillingInvoiceWorkflowInvoicingSettingsInput, + UpdateBillingWorkflowPaymentSendInvoiceSettingsInput, EventInput, UnauthorizedInput, ForbiddenInput, @@ -347,6 +375,7 @@ export type { SubscriptionCancelInput, InvoiceUsageQuantityDetailInput, InvoiceWorkflowInput, + UpdateBillingInvoiceWorkflowInput, CreateCreditGrantRequestInput, CreditGrantInput, WorkflowTaxSettingsInput, @@ -355,6 +384,7 @@ export type { AppStripeCreateCheckoutSessionRequestOptionsInput, InvoiceWorkflowSettingsInput, InvoiceDetailedLineInput, + UpdateInvoiceWorkflowSettingsInput, CreditGrantPagePaginatedResponseInput, BadRequestInput, CustomerStripeCreateCheckoutSessionRequestInput, @@ -380,4 +410,5 @@ export type { PlanPagePaginatedResponseInput, InvoiceStandardInput, InvoicePagePaginatedResponseInput, + UpdateInvoiceStandardRequestInput, } from './models/types.js' diff --git a/api/spec/packages/aip-client-javascript/src/models/operations/invoices.ts b/api/spec/packages/aip-client-javascript/src/models/operations/invoices.ts index c468507d7d..f8efcde992 100644 --- a/api/spec/packages/aip-client-javascript/src/models/operations/invoices.ts +++ b/api/spec/packages/aip-client-javascript/src/models/operations/invoices.ts @@ -4,6 +4,7 @@ import type { InvoicePagePaginatedResponse, ListInvoicesParamsFilter, SortQueryInput, + UpdateInvoiceStandardRequestInput, } from '../types.js' export interface ListInvoicesQuery { @@ -39,3 +40,11 @@ export type GetInvoiceRequest = { invoiceId: string } export type GetInvoiceResponse = z.output + +export type UpdateInvoiceRequest = { + invoiceId: string + body: UpdateInvoiceStandardRequestInput +} +export type UpdateInvoiceResponse = z.output< + typeof schemas.updateInvoiceResponse +> diff --git a/api/spec/packages/aip-client-javascript/src/models/schemas.ts b/api/spec/packages/aip-client-javascript/src/models/schemas.ts index 43395b1c09..61264d7007 100644 --- a/api/spec/packages/aip-client-javascript/src/models/schemas.ts +++ b/api/spec/packages/aip-client-javascript/src/models/schemas.ts @@ -842,6 +842,13 @@ export const invoiceWorkflowInvoicingSettings = z .describe( 'The period for the invoice to be kept in draft status for manual reviews.', ), + due_after: z + .string() + .optional() + + .describe( + 'The period after which the invoice is considered overdue if not paid.', + ), }) .describe( @@ -868,6 +875,73 @@ export const invoiceDetailedLineCostCategory = z .enum(['regular', 'commitment']) .describe('Cost category of a detailed invoice line item.') +export const updateLabels = z + .record(z.string(), z.string()) + + .describe( + 'Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "\\_".', + ) + +export const updateBillingInvoiceWorkflowInvoicingSettings = z + .object({ + auto_advance: z + .boolean() + .optional() + .default(true) + + .describe( + 'Whether to automatically issue the invoice after the draft_period has passed.', + ), + draft_period: z + .string() + .optional() + .default('P0D') + + .describe( + 'The period for the invoice to be kept in draft status for manual reviews.', + ), + }) + + .describe( + 'Invoice-level invoicing settings. A subset of BillingWorkflowInvoicingSettings limited to fields that are meaningful per-invoice. progressive_billing is omitted as it is a gather-time / profile-level decision.', + ) + +export const updateBillingWorkflowPaymentChargeAutomaticallySettings = z + .object({ + collection_method: z + .literal('charge_automatically') + .describe('The collection method for the invoice.'), + }) + + .describe( + 'Payment settings for a billing workflow when the collection method is charge automatically.', + ) + +export const updateBillingWorkflowPaymentSendInvoiceSettings = z + .object({ + collection_method: z + .literal('send_invoice') + .describe('The collection method for the invoice.'), + due_after: z + .string() + .optional() + .default('P30D') + + .describe( + "The period after which the invoice is due. With some payment solutions it's only applicable for manual collection method.", + ), + }) + + .describe( + 'Payment settings for a billing workflow when the collection method is send invoice.', + ) + +export const updatePriceFree = z + .object({ + type: z.literal('free').describe('The type of the price.'), + }) + .describe('Free price.') + export const currencyType = z .enum(['fiat', 'custom']) @@ -1422,6 +1496,35 @@ export const invoiceLineCreditsApplied = z }) .describe('A credit allocation applied to an invoice line item.') +export const updatePriceFlat = z + .object({ + type: z.literal('flat').describe('The type of the price.'), + amount: numeric, + }) + .describe('Flat price.') + +export const updatePriceUnit = z + .object({ + type: z.literal('unit').describe('The type of the price.'), + amount: numeric, + }) + + .describe( + 'Unit price. Charges a fixed rate per billing unit. When UnitConfig is present on the rate card, billing units are the converted quantities (e.g. GB instead of bytes).', + ) + +export const updateDiscounts = z + .object({ + percentage: z + .number() + .nonnegative() + .lte(100) + .optional() + .describe('Percentage discount applied to the price (0–100).'), + usage: numeric.optional(), + }) + .describe('Discount configuration for a rate card.') + export const featureManualUnitCost = z .object({ type: z @@ -1614,6 +1717,12 @@ export const chargeReference = z }) .describe('Reference to a charge associated with an invoice line.') +export const updateResourceReference = z + .object({ + id: ulid, + }) + .describe('TaxCode reference.') + export const dateTimeFieldFilter = z .union([ dateTime, @@ -1769,6 +1878,16 @@ export const subscriptionAddonTimelineSegment = z }) .describe('A subscription add-on event.') +export const updateClosedPeriod = z + .object({ + from: dateTime, + to: dateTime, + }) + + .describe( + 'A period with defined start and end dates. The period is always inclusive at the start and exclusive at the end.', + ) + export const costBasis = z .object({ id: ulid, @@ -2285,6 +2404,20 @@ export const customerUsageAttribution = z 'Mapping to attribute metered usage to the customer. One customer can have zero or more subjects, but one subject can only belong to one customer.', ) +export const updateCustomerUsageAttribution = z + .object({ + subject_keys: z + .array(usageAttributionSubjectKey) + + .describe( + 'The subjects that are attributed to the customer. Can be empty when no usage event subjects are associated with the customer.', + ), + }) + + .describe( + 'Mapping to attribute metered usage to the customer. One customer can have zero or more subjects, but one subject can only belong to one customer.', + ) + export const address = z .object({ country: countryCode.optional(), @@ -2297,6 +2430,18 @@ export const address = z }) .describe('Address') +export const updateAddress = z + .object({ + country: countryCode.optional(), + postal_code: z.string().optional().describe('Postal code.'), + state: z.string().optional().describe('State or province.'), + city: z.string().optional().describe('City.'), + line1: z.string().optional().describe('First line of the address.'), + line2: z.string().optional().describe('Second line of the address.'), + phone_number: z.string().optional().describe('Phone number.'), + }) + .describe('Address') + export const appStripeCreateCheckoutSessionCustomerUpdate = z .object({ address: appStripeCreateCheckoutSessionCustomerUpdateBehavior @@ -2642,6 +2787,15 @@ export const partyTaxIdentity = z 'Identity stores the details required to identify an entity for tax purposes in a specific country.', ) +export const updateBillingPartyTaxIdentity = z + .object({ + code: taxIdentificationCode.optional(), + }) + + .describe( + 'Identity stores the details required to identify an entity for tax purposes in a specific country.', + ) + export const workflowInvoicingSettings = z .object({ auto_advance: z @@ -2752,6 +2906,13 @@ export const invoiceLineBaseDiscount = z }) .describe('Base fields shared by all invoice line item discounts.') +export const updateBillingWorkflowPaymentSettings = z + .discriminatedUnion('collection_method', [ + updateBillingWorkflowPaymentChargeAutomaticallySettings, + updateBillingWorkflowPaymentSendInvoiceSettings, + ]) + .describe('Payment settings for a billing workflow.') + export const listCurrenciesParamsFilter = z .object({ type: currencyType.optional(), @@ -2982,6 +3143,17 @@ export const chargeTotals = z 'The totals of a change. RealTime is only expanded when the `real_time_usage` expand is used.', ) +export const updatePriceTier = z + .object({ + up_to_amount: numeric.optional(), + flat_price: updatePriceFlat.optional(), + unit_price: updatePriceUnit.optional(), + }) + + .describe( + 'A price tier used in graduated and volume pricing. At least one price component (flat_price or unit_price) must be set. When UnitConfig is present on the rate card, up_to_amount is expressed in converted billing units.', + ) + export const featureLlmUnitCost = z .object({ type: z @@ -3248,6 +3420,13 @@ export const invoiceWorkflowAppsReferences = z 'BillingInvoiceWorkflowAppsReferences represents the references (id) to the apps used by a billing profile', ) +export const updateRateCardTaxConfig = z + .object({ + behavior: taxBehavior.optional(), + code: updateResourceReference, + }) + .describe('The tax config of the rate card.') + export const listEventsParamsFilter = z .object({ id: stringFieldFilter.optional(), @@ -3469,7 +3648,6 @@ export const partyAddresses = z export const invoiceCustomer = z .object({ - id: ulid, name: z .string() .min(1) @@ -3477,6 +3655,30 @@ export const invoiceCustomer = z .describe('Display name of the resource. Between 1 and 256 characters.'), usage_attribution: customerUsageAttribution.optional(), billing_address: address.optional(), + id: ulid, + key: externalResourceKey.optional(), + }) + + .describe( + "Snapshot of the customer's information at the time the invoice was issued.", + ) + +export const updateBillingPartyAddresses = z + .object({ + billing_address: updateAddress, + }) + .describe('A collection of addresses for the party.') + +export const updateInvoiceCustomer = z + .object({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + usage_attribution: updateCustomerUsageAttribution.optional(), + billing_address: updateAddress.optional(), + id: ulid, key: externalResourceKey.optional(), }) @@ -3865,6 +4067,16 @@ export const invoiceLineDiscounts = z }) .describe('Discounts applied to an invoice line item.') +export const updateBillingInvoiceWorkflow = z + .object({ + invoicing: updateBillingInvoiceWorkflowInvoicingSettings.optional(), + payment: updateBillingWorkflowPaymentSettings.optional(), + }) + + .describe( + 'Invoice-level snapshot of the workflow configuration. Contains only the settings that are meaningful for an already-created invoice: invoicing behaviour and payment settings. Collection alignment and tax policy are gather-time / profile-wide concerns and are not included.', + ) + export const currency = z .discriminatedUnion('type', [currencyFiat, currencyCustom]) .describe('Fiat or custom currency.') @@ -3941,6 +4153,8 @@ export const priceVolume = z 'Volume tiered price. The maximum quantity within a period determines the per-unit price for all units in that period. When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are expressed in converted billing units.', ) +export const array = z.array(updatePriceTier) + export const featureUnitCost = z .discriminatedUnion('type', [featureManualUnitCost, featureLlmUnitCost]) @@ -4167,13 +4381,28 @@ export const party = z export const supplier = z .object({ - id: z.string().optional().describe('Unique identifier for the party.'), name: z .string() .optional() .describe('Legal name or representation of the party.'), tax_id: partyTaxIdentity.optional(), addresses: partyAddresses.optional(), + id: z.string().optional().describe('Unique identifier for the party.'), + }) + + .describe( + "Snapshot of the supplier's information at the time the invoice was issued. Structurally a read-only subset of `BillingParty` (the type configured on the billing profile), so the snapshot stays aligned with the source. `key` is omitted because it is not part of the snapshotted supplier data.", + ) + +export const updateSupplier = z + .object({ + name: z + .string() + .optional() + .describe('Legal name or representation of the party.'), + tax_id: updateBillingPartyTaxIdentity.optional(), + addresses: updateBillingPartyAddresses.optional(), + id: z.string().optional().describe('Unique identifier for the party.'), }) .describe( @@ -4328,6 +4557,15 @@ export const invoiceDetailedLine = z 'A detailed (child) sub-line belonging to a parent invoice line. Detailed lines represent the individual flat-fee components that make up a usage-based parent line after quantity snapshotting.', ) +export const updateInvoiceWorkflowSettings = z + .object({ + workflow: updateBillingInvoiceWorkflow, + }) + + .describe( + 'Snapshot of the billing workflow configuration captured at invoice creation.', + ) + export const currencyPagePaginatedResponse = z .object({ data: z.array(currency), @@ -4371,6 +4609,26 @@ export const priceUsageBased = z 'Usage-based price types that can appear on a usage-based rate card. When UnitConfig is present on the rate card, these price types operate on billing units (i.e. post-conversion quantities), not raw metered units.', ) +export const updatePriceGraduated = z + .object({ + type: z.literal('graduated').describe('The type of the price.'), + tiers: array, + }) + + .describe( + "Graduated tiered price. Each tier's rate applies only to the usage within that tier. Pricing can change as cumulative usage crosses tier boundaries. When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are expressed in converted billing units.", + ) + +export const updatePriceVolume = z + .object({ + type: z.literal('volume').describe('The type of the price.'), + tiers: array, + }) + + .describe( + 'Volume tiered price. The maximum quantity within a period determines the per-unit price for all units in that period. When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are expressed in converted billing units.', + ) + export const feature = z .object({ id: ulid, @@ -4694,6 +4952,16 @@ export const invoiceLineRateCard = z }) .describe('Rate card configuration snapshot for a usage-based invoice line.') +export const updatePrice = z + .discriminatedUnion('type', [ + updatePriceFree, + updatePriceFlat, + updatePriceUnit, + updatePriceGraduated, + updatePriceVolume, + ]) + .describe('Price.') + export const featurePagePaginatedResponse = z .object({ data: z.array(feature), @@ -4891,7 +5159,6 @@ export const upsertAddonRequest = z export const invoiceStandardLine = z .object({ - id: ulid, name: z .string() .min(1) @@ -4909,6 +5176,7 @@ export const invoiceStandardLine = z created_at: dateTime, updated_at: dateTime, deleted_at: dateTime.optional(), + id: ulid.optional(), type: z .literal('standard_line') .describe('The type of charge this line item represents.'), @@ -4936,6 +5204,15 @@ export const invoiceStandardLine = z 'A top-level line item on an invoice. Each line represents a single charge, typically associated with a rate card from a subscription. Detailed (child) lines are nested under `detailed_lines` when present.', ) +export const updateInvoiceLineRateCard = z + .object({ + price: updatePrice, + tax_config: updateRateCardTaxConfig.optional(), + feature_key: resourceKey.optional(), + discounts: updateDiscounts.optional(), + }) + .describe('Rate card configuration snapshot for a usage-based invoice line.') + export const profile = z .object({ id: ulid, @@ -5196,6 +5473,34 @@ export const invoiceLine = z 'A top-level line item on an invoice. Each line represents a single charge, typically associated with a rate card from a subscription. Detailed (child) lines are nested under `detailed_lines` when present.', ) +export const updateInvoiceStandardLine = z + .object({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: updateLabels.optional(), + id: ulid.optional(), + type: z + .literal('standard_line') + .describe('The type of charge this line item represents.'), + service_period: updateClosedPeriod, + rate_card: updateInvoiceLineRateCard, + }) + + .describe( + 'A top-level line item on an invoice. Each line represents a single charge, typically associated with a rate card from a subscription. Detailed (child) lines are nested under `detailed_lines` when present.', + ) + export const profilePagePaginatedResponse = z .object({ data: z.array(profile), @@ -5270,11 +5575,18 @@ export const invoiceStandard = z .optional() .describe( - 'Line items on this invoice. Always returned on single-resource GET; omitted on list endpoints unless explicitly expanded.', + 'Line items on this invoice. Always returned on single-resource GET; omitted on list endpoints unless explicitly expanded. Editable via update: existing lines are matched by `id`, lines without an `id` are created, and lines present on the invoice but omitted from the update request are deleted. Detailed (child) lines are always computed and cannot be edited directly.', ), }) .describe('A standard invoice for charges owed by the customer.') +export const updateInvoiceLine = z + .discriminatedUnion('type', [updateInvoiceStandardLine]) + + .describe( + 'A top-level line item on an invoice. Each line represents a single charge, typically associated with a rate card from a subscription. Detailed (child) lines are nested under `detailed_lines` when present.', + ) + export const invoice = z .discriminatedUnion('type', [invoiceStandard]) @@ -5282,6 +5594,8 @@ export const invoice = z 'An invoice issued to a customer. The `type` field determines the concrete variant: - `standard`: a standard invoice for charges owed.', ) +export const array_2 = z.array(updateInvoiceLine) + export const invoicePagePaginatedResponse = z .object({ data: z.array(invoice), @@ -5289,6 +5603,31 @@ export const invoicePagePaginatedResponse = z }) .describe('Page paginated response.') +export const updateInvoiceStandardRequest = z + .object({ + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: updateLabels.optional(), + supplier: updateSupplier, + customer: updateInvoiceCustomer, + type: z + .literal('standard') + .describe('Discriminator field identifying this as a standard invoice.'), + workflow: updateInvoiceWorkflowSettings, + lines: array_2.optional(), + }) + .describe('InvoiceStandard update request.') + +export const updateInvoiceRequest = z + .discriminatedUnion('type', [updateInvoiceStandardRequest]) + .describe('UpdateInvoiceRequest update request.') + export const listMeteringEventsQueryParams = z.object({ page: cursorPaginationQueryPage.optional(), filter: listEventsParamsFilter.optional(), @@ -5758,6 +6097,14 @@ export const getInvoicePathParams = z.object({ export const getInvoiceResponse = invoice +export const updateInvoicePathParams = z.object({ + invoiceId: ulid, +}) + +export const updateInvoiceBody = updateInvoiceRequest + +export const updateInvoiceResponse = invoice + export const createTaxCodeBody = createTaxCodeRequest export const createTaxCodeResponse = taxCode diff --git a/api/spec/packages/aip-client-javascript/src/models/types.ts b/api/spec/packages/aip-client-javascript/src/models/types.ts index cb94425015..382795ad3e 100644 --- a/api/spec/packages/aip-client-javascript/src/models/types.ts +++ b/api/spec/packages/aip-client-javascript/src/models/types.ts @@ -255,6 +255,8 @@ export interface InvoiceWorkflowInvoicingSettings { auto_advance: boolean /** The period for the invoice to be kept in draft status for manual reviews. */ draft_period: string + /** The period after which the invoice is considered overdue if not paid. */ + due_after?: string } /** External identifiers for an invoice line item assigned by third-party systems. */ @@ -263,6 +265,58 @@ export interface InvoiceLineExternalReferences { invoicing_id?: string } +/** + * Labels store metadata of an entity that can be used for filtering an entity list + * or for searching across entity types. + * + * Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", + * "mesh", "kic", or "\_". + */ +export type UpdateLabels = Record + +/** + * Invoice-level invoicing settings. + * + * A subset of BillingWorkflowInvoicingSettings limited to fields that are + * meaningful per-invoice. progressive_billing is omitted as it is a gather-time / + * profile-level decision. + */ +export interface UpdateBillingInvoiceWorkflowInvoicingSettings { + /** Whether to automatically issue the invoice after the draft_period has passed. */ + auto_advance: boolean + /** The period for the invoice to be kept in draft status for manual reviews. */ + draft_period: string +} + +/** + * Payment settings for a billing workflow when the collection method is charge + * automatically. + */ +export interface UpdateBillingWorkflowPaymentChargeAutomaticallySettings { + /** The collection method for the invoice. */ + collection_method: 'charge_automatically' +} + +/** + * Payment settings for a billing workflow when the collection method is send + * invoice. + */ +export interface UpdateBillingWorkflowPaymentSendInvoiceSettings { + /** The collection method for the invoice. */ + collection_method: 'send_invoice' + /** + * The period after which the invoice is due. With some payment solutions it's only + * applicable for manual collection method. + */ + due_after: string +} + +/** Free price. */ +export interface UpdatePriceFree { + /** The type of the price. */ + type: 'free' +} + /** LLM Provider */ export interface LlmCostProvider { /** Identifier of the provider, e.g., "openai", "anthropic". */ @@ -551,6 +605,39 @@ export interface InvoiceLineCreditsApplied { description?: string } +/** Flat price. */ +export interface UpdatePriceFlat { + /** The type of the price. */ + type: 'flat' + /** The amount of the flat price. */ + amount: string +} + +/** + * Unit price. + * + * Charges a fixed rate per billing unit. When UnitConfig is present on the rate + * card, billing units are the converted quantities (e.g. GB instead of bytes). + */ +export interface UpdatePriceUnit { + /** The type of the price. */ + type: 'unit' + /** The amount of the unit price. */ + amount: string +} + +/** Discount configuration for a rate card. */ +export interface UpdateDiscounts { + /** Percentage discount applied to the price (0–100). */ + percentage?: number + /** + * Number of usage units granted free before billing starts. Only applies to + * usage-based lines (not flat fees). Usage is treated as zero until this amount is + * exhausted. + */ + usage?: string +} + /** A fixed per-unit cost amount. */ export interface FeatureManualUnitCost { /** The type discriminator for manual unit cost. */ @@ -817,6 +904,11 @@ export interface ChargeReference { id: string } +/** TaxCode reference. */ +export interface UpdateResourceReference { + id: string +} + /** Metering event following the CloudEvents specification. */ export interface Event { /** Identifies the event. */ @@ -952,6 +1044,26 @@ export interface SubscriptionAddonTimelineSegment { quantity: number } +/** + * A period with defined start and end dates. + * + * The period is always inclusive at the start and exclusive at the end. + */ +export interface UpdateClosedPeriod { + /** + * The start of the period. + * + * The period is inclusive at the start. + */ + from: string + /** + * The end of the period. + * + * The period is exclusive at the end. + */ + to: string +} + /** Describes currency basis supported by billing system. */ export interface CostBasis { id: string @@ -1450,6 +1562,18 @@ export interface CustomerUsageAttribution { subject_keys: string[] } +/** + * Mapping to attribute metered usage to the customer. One customer can have zero + * or more subjects, but one subject can only belong to one customer. + */ +export interface UpdateCustomerUsageAttribution { + /** + * The subjects that are attributed to the customer. Can be empty when no usage + * event subjects are associated with the customer. + */ + subject_keys: string[] +} + /** Address */ export interface Address { /** @@ -1471,6 +1595,27 @@ export interface Address { phone_number?: string } +/** Address */ +export interface UpdateAddress { + /** + * Country code in [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) + * alpha-2 format. + */ + country?: string + /** Postal code. */ + postal_code?: string + /** State or province. */ + state?: string + /** City. */ + city?: string + /** First line of the address. */ + line1?: string + /** Second line of the address. */ + line2?: string + /** Phone number. */ + phone_number?: string +} + /** Controls which customer fields can be updated by the checkout session. */ export interface AppStripeCreateCheckoutSessionCustomerUpdate { /** @@ -1960,6 +2105,15 @@ export interface PartyTaxIdentity { code?: string } +/** + * Identity stores the details required to identify an entity for tax purposes in a + * specific country. + */ +export interface UpdateBillingPartyTaxIdentity { + /** Normalized tax identification code shown on the original identity document. */ + code?: string +} + /** Invoice settings for a billing workflow. */ export interface WorkflowInvoicingSettings { /** Whether to automatically issue the invoice after the draftPeriod has passed. */ @@ -2324,6 +2478,25 @@ export interface ChargeTotals { realtime?: Totals } +/** + * A price tier used in graduated and volume pricing. + * + * At least one price component (flat_price or unit_price) must be set. When + * UnitConfig is present on the rate card, up_to_amount is expressed in converted + * billing units. + */ +export interface UpdatePriceTier { + /** + * Up to and including this quantity will be contained in the tier. If undefined, + * the tier is open-ended (the last tier). + */ + up_to_amount?: string + /** The flat price component of the tier. Charged once when the tier is entered. */ + flat_price?: UpdatePriceFlat + /** The unit price component of the tier. Charged per billing unit within the tier. */ + unit_price?: UpdatePriceUnit +} + /** * LLM cost lookup configuration. Each dimension (provider, model, token type) can * be specified as either a static value or a meter group-by property name @@ -2750,6 +2923,12 @@ export interface InvoiceWorkflowAppsReferences { payment: AppReference } +/** The tax config of the rate card. */ +export interface UpdateRateCardTaxConfig { + behavior?: 'inclusive' | 'exclusive' + code: UpdateResourceReference +} + /** Filter options for listing ingested events. */ export interface ListEventsParamsFilter { /** Filter events by ID. */ @@ -3071,7 +3250,6 @@ export interface PartyAddresses { /** Snapshot of the customer's information at the time the invoice was issued. */ export interface InvoiceCustomer { - id: string /** * Display name of the resource. * @@ -3082,6 +3260,36 @@ export interface InvoiceCustomer { usage_attribution?: CustomerUsageAttribution /** The billing address of the customer. Used for tax and invoicing. */ billing_address?: Address + id: string + /** + * Optional external resource key for the customer. + * + * Omitted when the customer was created without a key. Unlike on the customer + * resource itself, the key is optional here because the invoice snapshot may + * predate or omit it. + */ + key?: string +} + +/** A collection of addresses for the party. */ +export interface UpdateBillingPartyAddresses { + /** Billing address. */ + billing_address: UpdateAddress +} + +/** Snapshot of the customer's information at the time the invoice was issued. */ +export interface UpdateInvoiceCustomer { + /** + * Display name of the resource. + * + * Between 1 and 256 characters. + */ + name: string + /** Mapping to attribute metered usage to the customer by the event subject. */ + usage_attribution?: UpdateCustomerUsageAttribution + /** The billing address of the customer. Used for tax and invoicing. */ + billing_address?: UpdateAddress + id: string /** * Optional external resource key for the customer. * @@ -3508,6 +3716,22 @@ export interface InvoiceLineDiscounts { usage?: InvoiceLineUsageDiscount[] } +/** + * Invoice-level snapshot of the workflow configuration. + * + * Contains only the settings that are meaningful for an already-created invoice: + * invoicing behaviour and payment settings. Collection alignment and tax policy + * are gather-time / profile-wide concerns and are not included. + */ +export interface UpdateBillingInvoiceWorkflow { + /** Invoicing settings for this invoice. */ + invoicing?: UpdateBillingInvoiceWorkflowInvoicingSettings + /** Payment settings for this invoice. */ + payment?: + | UpdateBillingWorkflowPaymentChargeAutomaticallySettings + | UpdateBillingWorkflowPaymentSendInvoiceSettings +} + /** Access status for a single feature. */ export interface GovernanceFeatureAccess { /** @@ -3596,6 +3820,8 @@ export interface PriceVolume { tiers: PriceTier[] } +export type Array = UpdatePriceTier[] + /** Page paginated response. */ export interface PricePagePaginatedResponse { data: LlmCostPrice[] @@ -3877,8 +4103,6 @@ export interface Party { * omitted because it is not part of the snapshotted supplier data. */ export interface Supplier { - /** Unique identifier for the party. */ - id?: string /** Legal name or representation of the party. */ name?: string /** @@ -3888,6 +4112,29 @@ export interface Supplier { tax_id?: PartyTaxIdentity /** Address for where information should be sent if needed. */ addresses?: PartyAddresses + /** Unique identifier for the party. */ + id?: string +} + +/** + * Snapshot of the supplier's information at the time the invoice was issued. + * + * Structurally a read-only subset of `BillingParty` (the type configured on the + * billing profile), so the snapshot stays aligned with the source. `key` is + * omitted because it is not part of the snapshotted supplier data. + */ +export interface UpdateSupplier { + /** Legal name or representation of the party. */ + name?: string + /** + * The entity's legal identification used for tax purposes. They may have other + * numbers, but we're only interested in those valid for tax purposes. + */ + tax_id?: UpdateBillingPartyTaxIdentity + /** Address for where information should be sent if needed. */ + addresses?: UpdateBillingPartyAddresses + /** Unique identifier for the party. */ + id?: string } /** @@ -4052,6 +4299,19 @@ export interface InvoiceDetailedLine { unit_price: string } +/** Snapshot of the billing workflow configuration captured at invoice creation. */ +export interface UpdateInvoiceWorkflowSettings { + /** + * The workflow configuration that was active when the invoice was created. + * + * Only the fields that are meaningful at the per-invoice level are included: + * invoicing behaviour (auto-advance, draft period) and payment settings + * (collection method, due date). Profile-wide settings such as collection + * alignment, progressive billing, and tax policy are omitted. + */ + workflow: UpdateBillingInvoiceWorkflow +} + /** Page paginated response. */ export interface CurrencyPagePaginatedResponse { data: (CurrencyFiat | CurrencyCustom)[] @@ -4085,6 +4345,38 @@ export interface GovernanceQueryResult { updated_at: string } +/** + * Graduated tiered price. + * + * Each tier's rate applies only to the usage within that tier. Pricing can change + * as cumulative usage crosses tier boundaries. + * + * When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are + * expressed in converted billing units. + */ +export interface UpdatePriceGraduated { + /** The type of the price. */ + type: 'graduated' + /** The tiers of the graduated price. At least one tier is required. */ + tiers: UpdatePriceTier[] +} + +/** + * Volume tiered price. + * + * The maximum quantity within a period determines the per-unit price for all units + * in that period. + * + * When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are + * expressed in converted billing units. + */ +export interface UpdatePriceVolume { + /** The type of the price. */ + type: 'volume' + /** The tiers of the volume price. At least one tier is required. */ + tiers: UpdatePriceTier[] +} + /** A capability or billable dimension offered by a provider. */ export interface Feature { id: string @@ -4779,7 +5071,6 @@ export interface UpsertAddonRequest { * present. */ export interface InvoiceStandardLine { - id: string /** * Display name of the resource. * @@ -4799,6 +5090,14 @@ export interface InvoiceStandardLine { updated_at: string /** An ISO-8601 timestamp representation of entity deletion date. */ deleted_at?: string + /** + * ID of the line. + * + * Optional on update: omit to create a new line, or supply the ID of an existing + * line to edit it. Existing lines omitted from an update's `lines` array are + * deleted. + */ + id?: string /** The type of charge this line item represents. */ type: 'standard_line' /** @@ -4836,6 +5135,23 @@ export interface InvoiceStandardLine { charge?: ChargeReference } +/** Rate card configuration snapshot for a usage-based invoice line. */ +export interface UpdateInvoiceLineRateCard { + /** The price definition used to calculate charges for this line. */ + price: + | UpdatePriceFree + | UpdatePriceFlat + | UpdatePriceUnit + | UpdatePriceGraduated + | UpdatePriceVolume + /** Tax configuration snapshot for this line. */ + tax_config?: UpdateRateCardTaxConfig + /** The feature key associated with this line's rate card. */ + feature_key?: string + /** Discount configuration from the rate card. */ + discounts?: UpdateDiscounts +} + /** * Billing profiles contain the settings for billing and controls invoice * generation. @@ -5114,6 +5430,49 @@ export interface AddonPagePaginatedResponse { meta: PaginatedMeta } +/** + * A top-level line item on an invoice. + * + * Each line represents a single charge, typically associated with a rate card from + * a subscription. Detailed (child) lines are nested under `detailed_lines` when + * present. + */ +export interface UpdateInvoiceStandardLine { + /** + * Display name of the resource. + * + * Between 1 and 256 characters. + */ + name: string + /** + * Optional description of the resource. + * + * Maximum 1024 characters. + */ + description?: string + labels?: UpdateLabels + /** + * ID of the line. + * + * Optional on update: omit to create a new line, or supply the ID of an existing + * line to edit it. Existing lines omitted from an update's `lines` array are + * deleted. + */ + id?: string + /** The type of charge this line item represents. */ + type: 'standard_line' + /** + * The service period covered by this invoice, spanning the earliest line start to + * the latest line end across all of its lines. + * + * For an invoice with no lines the period is empty, which means `from` will be + * equal to `to`. + */ + service_period: UpdateClosedPeriod + /** The rate card configuration snapshot used to price this line item. */ + rate_card: UpdateInvoiceLineRateCard +} + /** Page paginated response. */ export interface ProfilePagePaginatedResponse { data: Profile[] @@ -5220,17 +5579,54 @@ export interface InvoiceStandard { * Line items on this invoice. * * Always returned on single-resource GET; omitted on list endpoints unless - * explicitly expanded. + * explicitly expanded. Editable via update: existing lines are matched by `id`, + * lines without an `id` are created, and lines present on the invoice but omitted + * from the update request are deleted. Detailed (child) lines are always computed + * and cannot be edited directly. */ lines?: InvoiceStandardLine[] } +export type Array = UpdateInvoiceStandardLine[] + /** Page paginated response. */ export interface InvoicePagePaginatedResponse { data: InvoiceStandard[] meta: PaginatedMeta } +/** InvoiceStandard update request. */ +export interface UpdateInvoiceStandardRequest { + /** + * Optional description of the resource. + * + * Maximum 1024 characters. + */ + description?: string + labels?: UpdateLabels + /** + * Snapshot of the supplier's contact information at the time the invoice was + * issued. + */ + supplier: UpdateSupplier + /** Snapshot of the customer's information at the time the invoice was issued. */ + customer: UpdateInvoiceCustomer + /** Discriminator field identifying this as a standard invoice. */ + type: 'standard' + /** Workflow configuration snapshot captured at invoice creation time. */ + workflow: UpdateInvoiceWorkflowSettings + /** + * Line items on this invoice. + * + * Always returned on single-resource GET; omitted on list endpoints unless + * explicitly expanded. Editable via update: existing lines are matched by `id`, + * lines without an `id` are created, and lines present on the invoice but omitted + * from the update request are deleted. Detailed (child) lines are always computed + * and cannot be edited directly. + */ + lines?: UpdateInvoiceStandardLine[] +} + export interface SortQueryInput { /** The attribute to sort by. */ by: string @@ -5270,6 +5666,25 @@ export interface InvoiceWorkflowInvoicingSettingsInput { auto_advance?: boolean /** The period for the invoice to be kept in draft status for manual reviews. */ draft_period?: string + /** The period after which the invoice is considered overdue if not paid. */ + due_after?: string +} + +export interface UpdateBillingInvoiceWorkflowInvoicingSettingsInput { + /** Whether to automatically issue the invoice after the draft_period has passed. */ + auto_advance?: boolean + /** The period for the invoice to be kept in draft status for manual reviews. */ + draft_period?: string +} + +export interface UpdateBillingWorkflowPaymentSendInvoiceSettingsInput { + /** The collection method for the invoice. */ + collection_method: 'send_invoice' + /** + * The period after which the invoice is due. With some payment solutions it's only + * applicable for manual collection method. + */ + due_after?: string } export interface EventInput { @@ -5534,6 +5949,15 @@ export interface InvoiceWorkflowInput { | WorkflowPaymentSendInvoiceSettingsInput } +export interface UpdateBillingInvoiceWorkflowInput { + /** Invoicing settings for this invoice. */ + invoicing?: UpdateBillingInvoiceWorkflowInvoicingSettingsInput + /** Payment settings for this invoice. */ + payment?: + | UpdateBillingWorkflowPaymentChargeAutomaticallySettings + | UpdateBillingWorkflowPaymentSendInvoiceSettingsInput +} + export interface CreateCreditGrantRequestInput { /** * Display name of the resource. @@ -5851,6 +6275,18 @@ export interface InvoiceDetailedLineInput { unit_price: string } +export interface UpdateInvoiceWorkflowSettingsInput { + /** + * The workflow configuration that was active when the invoice was created. + * + * Only the fields that are meaningful at the per-invoice level are included: + * invoicing behaviour (auto-advance, draft period) and payment settings + * (collection method, due date). Profile-wide settings such as collection + * alignment, progressive billing, and tax policy are omitted. + */ + workflow: UpdateBillingInvoiceWorkflowInput +} + export interface CreditGrantPagePaginatedResponseInput { data: CreditGrantInput[] meta: PaginatedMeta @@ -6102,7 +6538,6 @@ export interface UpsertAddonRequestInput { } export interface InvoiceStandardLineInput { - id: string /** * Display name of the resource. * @@ -6122,6 +6557,14 @@ export interface InvoiceStandardLineInput { updated_at: string /** An ISO-8601 timestamp representation of entity deletion date. */ deleted_at?: string + /** + * ID of the line. + * + * Optional on update: omit to create a new line, or supply the ID of an existing + * line to edit it. Existing lines omitted from an update's `lines` array are + * deleted. + */ + id?: string /** The type of charge this line item represents. */ type: 'standard_line' /** @@ -6522,7 +6965,10 @@ export interface InvoiceStandardInput { * Line items on this invoice. * * Always returned on single-resource GET; omitted on list endpoints unless - * explicitly expanded. + * explicitly expanded. Editable via update: existing lines are matched by `id`, + * lines without an `id` are created, and lines present on the invoice but omitted + * from the update request are deleted. Detailed (child) lines are always computed + * and cannot be edited directly. */ lines?: InvoiceStandardLineInput[] } @@ -6531,3 +6977,34 @@ export interface InvoicePagePaginatedResponseInput { data: InvoiceStandardInput[] meta: PaginatedMeta } + +export interface UpdateInvoiceStandardRequestInput { + /** + * Optional description of the resource. + * + * Maximum 1024 characters. + */ + description?: string + labels?: UpdateLabels + /** + * Snapshot of the supplier's contact information at the time the invoice was + * issued. + */ + supplier: UpdateSupplier + /** Snapshot of the customer's information at the time the invoice was issued. */ + customer: UpdateInvoiceCustomer + /** Discriminator field identifying this as a standard invoice. */ + type: 'standard' + /** Workflow configuration snapshot captured at invoice creation time. */ + workflow: UpdateInvoiceWorkflowSettingsInput + /** + * Line items on this invoice. + * + * Always returned on single-resource GET; omitted on list endpoints unless + * explicitly expanded. Editable via update: existing lines are matched by `id`, + * lines without an `id` are created, and lines present on the invoice but omitted + * from the update request are deleted. Detailed (child) lines are always computed + * and cannot be edited directly. + */ + lines?: UpdateInvoiceStandardLine[] +} diff --git a/api/spec/packages/aip-client-javascript/src/sdk/invoices.ts b/api/spec/packages/aip-client-javascript/src/sdk/invoices.ts index a1bee81d07..b1b2fce9a5 100644 --- a/api/spec/packages/aip-client-javascript/src/sdk/invoices.ts +++ b/api/spec/packages/aip-client-javascript/src/sdk/invoices.ts @@ -1,11 +1,13 @@ import { type Client } from '../core.js' import { unwrap, type RequestOptions } from '../lib/types.js' -import { listInvoices, getInvoice } from '../funcs/invoices.js' +import { listInvoices, getInvoice, updateInvoice } from '../funcs/invoices.js' import type { ListInvoicesRequest, ListInvoicesResponse, GetInvoiceRequest, GetInvoiceResponse, + UpdateInvoiceRequest, + UpdateInvoiceResponse, } from '../models/operations/invoices.js' export class Invoices { @@ -24,4 +26,11 @@ export class Invoices { ): Promise { return unwrap(await getInvoice(this._client, request, options)) } + + async update( + request: UpdateInvoiceRequest, + options?: RequestOptions, + ): Promise { + return unwrap(await updateInvoice(this._client, request, options)) + } } diff --git a/api/spec/packages/aip/src/invoices/invoice.tsp b/api/spec/packages/aip/src/invoices/invoice.tsp index 35a656b723..8b3d9760d1 100644 --- a/api/spec/packages/aip/src/invoices/invoice.tsp +++ b/api/spec/packages/aip/src/invoices/invoice.tsp @@ -42,6 +42,14 @@ union Invoice { // credit_note: BillingCreditNoteInvoice, } +@doc("UpdateInvoiceRequest update request.") +@friendlyName("UpdateInvoiceRequest") +@discriminated(#{ envelope: "none", discriminatorPropertyName: "type" }) +union UpdateInvoiceRequest { + @summary("Standard invoice") + standard: Shared.UpdateRequestNested, +} + /** * The type of a billing invoice. */ @@ -85,14 +93,14 @@ model InvoiceBase { * Snapshot of the supplier's contact information at the time the invoice was * issued. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Supplier") supplier: Supplier; /** * Snapshot of the customer's information at the time the invoice was issued. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Customer") customer: InvoiceCustomer; @@ -159,7 +167,7 @@ model InvoiceStandard { /** * Discriminator field identifying this as a standard invoice. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Type") type: InvoiceType.Standard; @@ -224,7 +232,7 @@ model InvoiceStandard { /** * Workflow configuration snapshot captured at invoice creation time. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Workflow") workflow: InvoiceWorkflowSettings; @@ -232,9 +240,12 @@ model InvoiceStandard { * Line items on this invoice. * * Always returned on single-resource GET; omitted on list endpoints unless - * explicitly expanded. + * explicitly expanded. Editable via update: existing lines are matched by `id`, + * lines without an `id` are created, and lines present on the invoice but omitted + * from the update request are deleted. Detailed (child) lines are always computed + * and cannot be edited directly. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Lines") lines?: InvoiceLine[]; } @@ -330,13 +341,17 @@ enum InvoiceStandardStatus { * Snapshot of the customer's information at the time the invoice was issued. */ @friendlyName("BillingInvoiceCustomer") -@withVisibility(Lifecycle.Read) +@withVisibility(Lifecycle.Read, Lifecycle.Update) model InvoiceCustomer { ...PickProperties< Customers.Customer, - "id" | "name" | "usage_attribution" | "billing_address" + "name" | "usage_attribution" | "billing_address" >; + #suppress "@openmeter/api-spec-aip/doc-decorator" "shared model" + @visibility(Lifecycle.Read, Lifecycle.Update) + id: Shared.ULID; + /** * Optional external resource key for the customer. * @@ -355,9 +370,15 @@ model InvoiceCustomer { * omitted because it is not part of the snapshotted supplier data. */ @friendlyName("BillingSupplier") -@withVisibility(Lifecycle.Read) +@withVisibility(Lifecycle.Read, Lifecycle.Update) model Supplier { - ...OmitProperties; + ...OmitProperties; + + /** + * Unique identifier for the party. + */ + @visibility(Lifecycle.Read, Lifecycle.Update) + id?: string; } /** @@ -387,7 +408,7 @@ model InvoiceWorkflowSettings { * (collection method, due date). Profile-wide settings such as collection * alignment, progressive billing, and tax policy are omitted. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Workflow config") workflow: BillingInvoiceWorkflow; } @@ -429,14 +450,14 @@ model BillingInvoiceWorkflow { /** * Invoicing settings for this invoice. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Invoicing settings") invoicing?: BillingInvoiceWorkflowInvoicingSettings; /** * Payment settings for this invoice. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Payment settings") payment?: Billing.BillingWorkflowPaymentSettings; } @@ -453,7 +474,7 @@ model BillingInvoiceWorkflowInvoicingSettings { /** * Whether to automatically issue the invoice after the draft_period has passed. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) auto_advance?: boolean = true; /** @@ -461,8 +482,16 @@ model BillingInvoiceWorkflowInvoicingSettings { */ @encode(DurationKnownEncoding.ISO8601) @example("P1D") - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) draft_period?: string = "P0D"; + + /** + * The period after which the invoice is considered overdue if not paid. + */ + @encode(DurationKnownEncoding.ISO8601) + @example("P30D") + @visibility(Lifecycle.Read) + due_after?: string; } /** @@ -644,12 +673,23 @@ enum InvoiceLineType { */ @friendlyName("BillingInvoiceStandardLine") model InvoiceStandardLine { - ...Shared.Resource; + ...OmitProperties; + + /** + * ID of the line. + * + * Optional on update: omit to create a new line, or supply the ID of an existing + * line to edit it. Existing lines omitted from an update's `lines` array are + * deleted. + */ + @visibility(Lifecycle.Read, Lifecycle.Update) + @summary("ID") + id?: Shared.ULID; /** * The type of charge this line item represents. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Type") type: InvoiceLineType.StandardLine; @@ -668,7 +708,7 @@ model InvoiceStandardLine { * For an invoice with no lines the period is empty, which means `from` will be * equal to `to`. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Service period") service_period: Shared.ClosedPeriod; @@ -710,7 +750,7 @@ model InvoiceStandardLine { /** * The rate card configuration snapshot used to price this line item. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Rate card") rate_card: InvoiceLineRateCard; @@ -739,28 +779,28 @@ model InvoiceLineRateCard { /** * The price definition used to calculate charges for this line. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Price") price: ProductCatalog.Price; /** * Tax configuration snapshot for this line. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Tax config") tax_config?: ProductCatalog.RateCardTaxConfig; /** * The feature key associated with this line's rate card. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Feature key") feature_key?: Shared.ResourceKey; /** * Discount configuration from the rate card. */ - @visibility(Lifecycle.Read) + @visibility(Lifecycle.Read, Lifecycle.Update) @summary("Discounts") discounts?: ProductCatalog.Discounts; } diff --git a/api/spec/packages/aip/src/invoices/operations.tsp b/api/spec/packages/aip/src/invoices/operations.tsp index 516a8adad2..eda3d5bf31 100644 --- a/api/spec/packages/aip/src/invoices/operations.tsp +++ b/api/spec/packages/aip/src/invoices/operations.tsp @@ -112,4 +112,26 @@ interface InvoicesOperations { | Shared.GetResponse | Common.NotFound | Common.ErrorResponses; + + /** + * Update a billing invoice. + * + * Only the mutable fields of the invoice can be edited: description, labels, + * supplier, customer, workflow settings, and top-level lines. Top-level lines are + * matched by `id`; lines without an `id` are created, and existing lines omitted + * from `lines` are deleted. Detailed (child) lines are always computed and cannot + * be edited directly. Only invoices in draft status can be updated. + */ + @put + @operationId("update-invoice") + @summary("Update a billing invoice") + @extension(Shared.UnstableExtension, true) + @extension(Shared.InternalExtension, true) + @extension(Shared.PrivateExtension, true) + update( + @path invoiceId: Shared.ULID, + + @body + invoice: UpdateInvoiceRequest, + ): Shared.UpdateResponse | Common.NotFound | Common.ErrorResponses; } diff --git a/api/spec/packages/aip/src/shared/request.tsp b/api/spec/packages/aip/src/shared/request.tsp index 17962869a0..4cdadc71bf 100644 --- a/api/spec/packages/aip/src/shared/request.tsp +++ b/api/spec/packages/aip/src/shared/request.tsp @@ -30,3 +30,13 @@ model UpdateRequest T, Lifecycle.Read >>>; + +// Supports visibility filtering for models with nested fields. +@doc("{name} update request.", T) +@friendlyName("Update{name}Request", T) +model UpdateRequestNested + is FilterVisibility< + DefaultKeyVisibility, + #{ all: #[Lifecycle.Update] }, + "Update{name}" + >; diff --git a/api/spec/packages/aip/src/shared/resource.tsp b/api/spec/packages/aip/src/shared/resource.tsp index bad5d3b8a4..650a67da61 100644 --- a/api/spec/packages/aip/src/shared/resource.tsp +++ b/api/spec/packages/aip/src/shared/resource.tsp @@ -78,6 +78,6 @@ model ResourceImmutable { @friendlyName("{name}Reference", T) model ResourceReference { #suppress "@openmeter/api-spec-aip/doc-decorator" "shared model" - @visibility(Lifecycle.Read, Lifecycle.Create) + @visibility(Lifecycle.Read, Lifecycle.Create, Lifecycle.Update) id: ULID; } diff --git a/api/v3/api.gen.go b/api/v3/api.gen.go index 57650de881..96cb62d8e1 100644 --- a/api/v3/api.gen.go +++ b/api/v3/api.gen.go @@ -1457,6 +1457,141 @@ func (e MeteringEventDatacontenttype) Valid() bool { } } +// Defines values for UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod. +const ( + UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethodChargeAutomatically UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod = "charge_automatically" +) + +// Valid indicates whether the value is a known member of the UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod enum. +func (e UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod) Valid() bool { + switch e { + case UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethodChargeAutomatically: + return true + default: + return false + } +} + +// Defines values for UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod. +const ( + UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethodSendInvoice UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod = "send_invoice" +) + +// Valid indicates whether the value is a known member of the UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod enum. +func (e UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod) Valid() bool { + switch e { + case UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethodSendInvoice: + return true + default: + return false + } +} + +// Defines values for UpdateInvoiceStandardLineType. +const ( + UpdateInvoiceStandardLineTypeStandardLine UpdateInvoiceStandardLineType = "standard_line" +) + +// Valid indicates whether the value is a known member of the UpdateInvoiceStandardLineType enum. +func (e UpdateInvoiceStandardLineType) Valid() bool { + switch e { + case UpdateInvoiceStandardLineTypeStandardLine: + return true + default: + return false + } +} + +// Defines values for UpdateInvoiceStandardRequestType. +const ( + UpdateInvoiceStandardRequestTypeStandard UpdateInvoiceStandardRequestType = "standard" +) + +// Valid indicates whether the value is a known member of the UpdateInvoiceStandardRequestType enum. +func (e UpdateInvoiceStandardRequestType) Valid() bool { + switch e { + case UpdateInvoiceStandardRequestTypeStandard: + return true + default: + return false + } +} + +// Defines values for UpdatePriceFlatType. +const ( + UpdatePriceFlatTypeFlat UpdatePriceFlatType = "flat" +) + +// Valid indicates whether the value is a known member of the UpdatePriceFlatType enum. +func (e UpdatePriceFlatType) Valid() bool { + switch e { + case UpdatePriceFlatTypeFlat: + return true + default: + return false + } +} + +// Defines values for UpdatePriceFreeType. +const ( + UpdatePriceFreeTypeFree UpdatePriceFreeType = "free" +) + +// Valid indicates whether the value is a known member of the UpdatePriceFreeType enum. +func (e UpdatePriceFreeType) Valid() bool { + switch e { + case UpdatePriceFreeTypeFree: + return true + default: + return false + } +} + +// Defines values for UpdatePriceGraduatedType. +const ( + UpdatePriceGraduatedTypeGraduated UpdatePriceGraduatedType = "graduated" +) + +// Valid indicates whether the value is a known member of the UpdatePriceGraduatedType enum. +func (e UpdatePriceGraduatedType) Valid() bool { + switch e { + case UpdatePriceGraduatedTypeGraduated: + return true + default: + return false + } +} + +// Defines values for UpdatePriceUnitType. +const ( + UpdatePriceUnitTypeUnit UpdatePriceUnitType = "unit" +) + +// Valid indicates whether the value is a known member of the UpdatePriceUnitType enum. +func (e UpdatePriceUnitType) Valid() bool { + switch e { + case UpdatePriceUnitTypeUnit: + return true + default: + return false + } +} + +// Defines values for UpdatePriceVolumeType. +const ( + UpdatePriceVolumeTypeVolume UpdatePriceVolumeType = "volume" +) + +// Valid indicates whether the value is a known member of the UpdatePriceVolumeType enum. +func (e UpdatePriceVolumeType) Valid() bool { + switch e { + case UpdatePriceVolumeTypeVolume: + return true + default: + return false + } +} + // Addon Add-on allows extending subscriptions with compatible plans with additional // ratecards. type Addon struct { @@ -3331,7 +3466,10 @@ type BillingInvoiceStandard struct { // Lines Line items on this invoice. // // Always returned on single-resource GET; omitted on list endpoints unless - // explicitly expanded. + // explicitly expanded. Editable via update: existing lines are matched by `id`, + // lines without an `id` are created, and lines present on the invoice but omitted + // from the update request are deleted. Detailed (child) lines are always computed + // and cannot be edited directly. Lines *[]BillingInvoiceLine `json:"lines,omitempty"` // Number Human-readable invoice number generated by the invoicing app. @@ -3415,7 +3553,13 @@ type BillingInvoiceStandardLine struct { // ExternalReferences External identifiers for this line item assigned by third-party systems. ExternalReferences *BillingInvoiceLineExternalReferences `json:"external_references,omitempty"` - Id ULID `json:"id"` + + // Id ID of the line. + // + // Optional on update: omit to create a new line, or supply the ID of an existing + // line to edit it. Existing lines omitted from an update's `lines` array are + // deleted. + Id *ULID `json:"id,omitempty"` // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. // @@ -3540,6 +3684,9 @@ type BillingInvoiceWorkflowInvoicingSettings struct { // DraftPeriod The period for the invoice to be kept in draft status for manual reviews. DraftPeriod *string `json:"draft_period,omitempty"` + + // DueAfter The period after which the invoice is considered overdue if not paid. + DueAfter *string `json:"due_after,omitempty"` } // BillingInvoiceWorkflowSettings Snapshot of the billing workflow configuration captured at invoice creation. @@ -6562,6 +6709,114 @@ type UnauthorizedError struct { Type interface{} `json:"type,omitempty"` } +// UpdateAddress Address +type UpdateAddress struct { + // City City. + City *string `json:"city,omitempty"` + + // Country Country code in [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) + // alpha-2 format. + Country *CountryCode `json:"country,omitempty"` + + // Line1 First line of the address. + Line1 *string `json:"line1,omitempty"` + + // Line2 Second line of the address. + Line2 *string `json:"line2,omitempty"` + + // PhoneNumber Phone number. + PhoneNumber *string `json:"phone_number,omitempty"` + + // PostalCode Postal code. + PostalCode *string `json:"postal_code,omitempty"` + + // State State or province. + State *string `json:"state,omitempty"` +} + +// UpdateBillingInvoiceWorkflow Invoice-level snapshot of the workflow configuration. +// +// Contains only the settings that are meaningful for an already-created invoice: +// invoicing behaviour and payment settings. Collection alignment and tax policy +// are gather-time / profile-wide concerns and are not included. +type UpdateBillingInvoiceWorkflow struct { + // Invoicing Invoicing settings for this invoice. + Invoicing *UpdateBillingInvoiceWorkflowInvoicingSettings `json:"invoicing,omitempty"` + + // Payment Payment settings for this invoice. + Payment *UpdateBillingWorkflowPaymentSettings `json:"payment,omitempty"` +} + +// UpdateBillingInvoiceWorkflowInvoicingSettings Invoice-level invoicing settings. +// +// A subset of BillingWorkflowInvoicingSettings limited to fields that are +// meaningful per-invoice. progressive_billing is omitted as it is a gather-time / +// profile-level decision. +type UpdateBillingInvoiceWorkflowInvoicingSettings struct { + // AutoAdvance Whether to automatically issue the invoice after the draft_period has passed. + AutoAdvance *bool `json:"auto_advance,omitempty"` + + // DraftPeriod The period for the invoice to be kept in draft status for manual reviews. + DraftPeriod *string `json:"draft_period,omitempty"` +} + +// UpdateBillingPartyAddresses A collection of addresses for the party. +type UpdateBillingPartyAddresses struct { + // BillingAddress Billing address. + BillingAddress UpdateAddress `json:"billing_address"` +} + +// UpdateBillingPartyTaxIdentity Identity stores the details required to identify an entity for tax purposes in a +// specific country. +type UpdateBillingPartyTaxIdentity struct { + // Code Normalized tax identification code shown on the original identity document. + Code *BillingTaxIdentificationCode `json:"code,omitempty"` +} + +// UpdateBillingWorkflowPaymentChargeAutomaticallySettings Payment settings for a billing workflow when the collection method is charge +// automatically. +type UpdateBillingWorkflowPaymentChargeAutomaticallySettings struct { + // CollectionMethod The collection method for the invoice. + CollectionMethod UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod `json:"collection_method"` +} + +// UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod The collection method for the invoice. +type UpdateBillingWorkflowPaymentChargeAutomaticallySettingsCollectionMethod string + +// UpdateBillingWorkflowPaymentSendInvoiceSettings Payment settings for a billing workflow when the collection method is send +// invoice. +type UpdateBillingWorkflowPaymentSendInvoiceSettings struct { + // CollectionMethod The collection method for the invoice. + CollectionMethod UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod `json:"collection_method"` + + // DueAfter The period after which the invoice is due. With some payment solutions it's only + // applicable for manual collection method. + DueAfter *string `json:"due_after,omitempty"` +} + +// UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod The collection method for the invoice. +type UpdateBillingWorkflowPaymentSendInvoiceSettingsCollectionMethod string + +// UpdateBillingWorkflowPaymentSettings Payment settings for a billing workflow. +type UpdateBillingWorkflowPaymentSettings struct { + union json.RawMessage +} + +// UpdateClosedPeriod A period with defined start and end dates. +// +// The period is always inclusive at the start and exclusive at the end. +type UpdateClosedPeriod struct { + // From The start of the period. + // + // The period is inclusive at the start. + From DateTime `json:"from"` + + // To The end of the period. + // + // The period is exclusive at the end. + To DateTime `json:"to"` +} + // UpdateCreditGrantExternalSettlementRequest Request body for updating the external payment settlement status of a credit // grant. type UpdateCreditGrantExternalSettlementRequest struct { @@ -6569,6 +6824,25 @@ type UpdateCreditGrantExternalSettlementRequest struct { Status BillingCreditPurchasePaymentSettlementStatus `json:"status"` } +// UpdateCustomerUsageAttribution Mapping to attribute metered usage to the customer. One customer can have zero +// or more subjects, but one subject can only belong to one customer. +type UpdateCustomerUsageAttribution struct { + // SubjectKeys The subjects that are attributed to the customer. Can be empty when no usage + // event subjects are associated with the customer. + SubjectKeys []UsageAttributionSubjectKey `json:"subject_keys"` +} + +// UpdateDiscounts Discount configuration for a rate card. +type UpdateDiscounts struct { + // Percentage Percentage discount applied to the price (0–100). + Percentage *float32 `json:"percentage,omitempty"` + + // Usage Number of usage units granted free before billing starts. Only applies to + // usage-based lines (not flat fees). Usage is treated as zero until this amount is + // exhausted. + Usage *Numeric `json:"usage,omitempty"` +} + // UpdateFeatureRequest Request body for updating a feature. Currently only the unit_cost field can be // updated. type UpdateFeatureRequest struct { @@ -6579,47 +6853,76 @@ type UpdateFeatureRequest struct { UnitCost nullable.Nullable[BillingFeatureUnitCost] `json:"unit_cost,omitempty"` } -// UpdateMeterRequest Meter update request. -type UpdateMeterRequest struct { - // Description Optional description of the resource. - // - // Maximum 1024 characters. - Description *string `json:"description,omitempty"` +// UpdateInvoiceCustomer Snapshot of the customer's information at the time the invoice was issued. +type UpdateInvoiceCustomer struct { + // BillingAddress The billing address of the customer. Used for tax and invoicing. + BillingAddress *UpdateAddress `json:"billing_address,omitempty"` - // Dimensions Named JSONPath expressions to extract the group by values from the event data. - // - // Keys must be unique and consist only alphanumeric and underscore characters. - Dimensions *map[string]string `json:"dimensions,omitempty"` + // Id ULID (Universally Unique Lexicographically Sortable Identifier). + Id ULID `json:"id"` - // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. + // Key Optional external resource key for the customer. // - // Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "_". - Labels *Labels `json:"labels,omitempty"` + // Omitted when the customer was created without a key. Unlike on the customer + // resource itself, the key is optional here because the invoice snapshot may + // predate or omit it. + Key *ExternalResourceKey `json:"key,omitempty"` // Name Display name of the resource. // // Between 1 and 256 characters. - Name *string `json:"name,omitempty"` + Name string `json:"name"` + + // UsageAttribution Mapping to attribute metered usage to the customer by the event subject. + UsageAttribution *UpdateCustomerUsageAttribution `json:"usage_attribution,omitempty"` } -// UpdateOrganizationDefaultTaxCodesRequest OrganizationDefaultTaxCodes update request. -type UpdateOrganizationDefaultTaxCodesRequest struct { - // CreditGrantTaxCode Default tax code for credit grants. - CreditGrantTaxCode *TaxCodeReference `json:"credit_grant_tax_code,omitempty"` +// UpdateInvoiceLine A top-level line item on an invoice. +// +// Each line represents a single charge, typically associated with a rate card from +// a subscription. Detailed (child) lines are nested under `detailed_lines` when +// present. +type UpdateInvoiceLine struct { + union json.RawMessage +} - // InvoicingTaxCode Default tax code for invoicing. - InvoicingTaxCode *TaxCodeReference `json:"invoicing_tax_code,omitempty"` +// UpdateInvoiceLineRateCard Rate card configuration snapshot for a usage-based invoice line. +type UpdateInvoiceLineRateCard struct { + // Discounts Discount configuration from the rate card. + Discounts *UpdateDiscounts `json:"discounts,omitempty"` + + // FeatureKey The feature key associated with this line's rate card. + FeatureKey *ResourceKey `json:"feature_key,omitempty"` + + // Price The price definition used to calculate charges for this line. + Price UpdatePrice `json:"price"` + + // TaxConfig Tax configuration snapshot for this line. + TaxConfig *UpdateRateCardTaxConfig `json:"tax_config,omitempty"` } -// UpsertAddonRequest Addon upsert request. -type UpsertAddonRequest struct { +// UpdateInvoiceRequest UpdateInvoiceRequest update request. +type UpdateInvoiceRequest struct { + union json.RawMessage +} + +// UpdateInvoiceStandardLine A top-level line item on an invoice. +// +// Each line represents a single charge, typically associated with a rate card from +// a subscription. Detailed (child) lines are nested under `detailed_lines` when +// present. +type UpdateInvoiceStandardLine struct { // Description Optional description of the resource. // // Maximum 1024 characters. Description *string `json:"description,omitempty"` - // InstanceType The InstanceType of the add-ons. Can be "single" or "multiple". - InstanceType AddonInstanceType `json:"instance_type"` + // Id ID of the line. + // + // Optional on update: omit to create a new line, or supply the ID of an existing + // line to edit it. Existing lines omitted from an update's `lines` array are + // deleted. + Id *ULID `json:"id,omitempty"` // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. // @@ -6631,12 +6934,262 @@ type UpsertAddonRequest struct { // Between 1 and 256 characters. Name string `json:"name"` - // RateCards The rate cards of the add-on. - RateCards []BillingRateCard `json:"rate_cards"` -} + // RateCard The rate card configuration snapshot used to price this line item. + RateCard UpdateInvoiceLineRateCard `json:"rate_card"` -// UpsertAppCustomerDataRequest AppCustomerData upsert request. -type UpsertAppCustomerDataRequest struct { + // ServicePeriod The service period covered by this invoice, spanning the earliest line start to + // the latest line end across all of its lines. + // + // For an invoice with no lines the period is empty, which means `from` will be + // equal to `to`. + ServicePeriod UpdateClosedPeriod `json:"service_period"` + + // Type The type of charge this line item represents. + Type UpdateInvoiceStandardLineType `json:"type"` +} + +// UpdateInvoiceStandardLineType The type of charge this line item represents. +type UpdateInvoiceStandardLineType string + +// UpdateInvoiceStandardRequest InvoiceStandard update request. +type UpdateInvoiceStandardRequest struct { + // Customer Snapshot of the customer's information at the time the invoice was issued. + Customer UpdateInvoiceCustomer `json:"customer"` + + // Description Optional description of the resource. + // + // Maximum 1024 characters. + Description *string `json:"description,omitempty"` + + // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. + // + // Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "_". + Labels *Labels `json:"labels,omitempty"` + + // Lines Line items on this invoice. + // + // Always returned on single-resource GET; omitted on list endpoints unless + // explicitly expanded. Editable via update: existing lines are matched by `id`, + // lines without an `id` are created, and lines present on the invoice but omitted + // from the update request are deleted. Detailed (child) lines are always computed + // and cannot be edited directly. + Lines *[]UpdateInvoiceLine `json:"lines,omitempty"` + + // Supplier Snapshot of the supplier's contact information at the time the invoice was + // issued. + Supplier UpdateSupplier `json:"supplier"` + + // Type Discriminator field identifying this as a standard invoice. + Type UpdateInvoiceStandardRequestType `json:"type"` + + // Workflow Workflow configuration snapshot captured at invoice creation time. + Workflow UpdateInvoiceWorkflowSettings `json:"workflow"` +} + +// UpdateInvoiceStandardRequestType Discriminator field identifying this as a standard invoice. +type UpdateInvoiceStandardRequestType string + +// UpdateInvoiceWorkflowSettings Snapshot of the billing workflow configuration captured at invoice creation. +type UpdateInvoiceWorkflowSettings struct { + // Workflow The workflow configuration that was active when the invoice was created. + // + // Only the fields that are meaningful at the per-invoice level are included: + // invoicing behaviour (auto-advance, draft period) and payment settings + // (collection method, due date). Profile-wide settings such as collection + // alignment, progressive billing, and tax policy are omitted. + Workflow UpdateBillingInvoiceWorkflow `json:"workflow"` +} + +// UpdateMeterRequest Meter update request. +type UpdateMeterRequest struct { + // Description Optional description of the resource. + // + // Maximum 1024 characters. + Description *string `json:"description,omitempty"` + + // Dimensions Named JSONPath expressions to extract the group by values from the event data. + // + // Keys must be unique and consist only alphanumeric and underscore characters. + Dimensions *map[string]string `json:"dimensions,omitempty"` + + // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. + // + // Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "_". + Labels *Labels `json:"labels,omitempty"` + + // Name Display name of the resource. + // + // Between 1 and 256 characters. + Name *string `json:"name,omitempty"` +} + +// UpdateOrganizationDefaultTaxCodesRequest OrganizationDefaultTaxCodes update request. +type UpdateOrganizationDefaultTaxCodesRequest struct { + // CreditGrantTaxCode Default tax code for credit grants. + CreditGrantTaxCode *TaxCodeReference `json:"credit_grant_tax_code,omitempty"` + + // InvoicingTaxCode Default tax code for invoicing. + InvoicingTaxCode *TaxCodeReference `json:"invoicing_tax_code,omitempty"` +} + +// UpdatePrice Price. +type UpdatePrice struct { + union json.RawMessage +} + +// UpdatePriceFlat Flat price. +type UpdatePriceFlat struct { + // Amount The amount of the flat price. + Amount Numeric `json:"amount"` + + // Type The type of the price. + Type UpdatePriceFlatType `json:"type"` +} + +// UpdatePriceFlatType The type of the price. +type UpdatePriceFlatType string + +// UpdatePriceFree Free price. +type UpdatePriceFree struct { + // Type The type of the price. + Type UpdatePriceFreeType `json:"type"` +} + +// UpdatePriceFreeType The type of the price. +type UpdatePriceFreeType string + +// UpdatePriceGraduated Graduated tiered price. +// +// Each tier's rate applies only to the usage within that tier. Pricing can change +// as cumulative usage crosses tier boundaries. +// +// When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are +// expressed in converted billing units. +type UpdatePriceGraduated struct { + // Tiers The tiers of the graduated price. At least one tier is required. + Tiers []UpdatePriceTier `json:"tiers"` + + // Type The type of the price. + Type UpdatePriceGraduatedType `json:"type"` +} + +// UpdatePriceGraduatedType The type of the price. +type UpdatePriceGraduatedType string + +// UpdatePriceTier A price tier used in graduated and volume pricing. +// +// At least one price component (flat_price or unit_price) must be set. When +// UnitConfig is present on the rate card, up_to_amount is expressed in converted +// billing units. +type UpdatePriceTier struct { + // FlatPrice The flat price component of the tier. Charged once when the tier is entered. + FlatPrice *UpdatePriceFlat `json:"flat_price,omitempty"` + + // UnitPrice The unit price component of the tier. Charged per billing unit within the tier. + UnitPrice *UpdatePriceUnit `json:"unit_price,omitempty"` + + // UpToAmount Up to and including this quantity will be contained in the tier. If undefined, + // the tier is open-ended (the last tier). + UpToAmount *Numeric `json:"up_to_amount,omitempty"` +} + +// UpdatePriceUnit Unit price. +// +// Charges a fixed rate per billing unit. When UnitConfig is present on the rate +// card, billing units are the converted quantities (e.g. GB instead of bytes). +type UpdatePriceUnit struct { + // Amount The amount of the unit price. + Amount Numeric `json:"amount"` + + // Type The type of the price. + Type UpdatePriceUnitType `json:"type"` +} + +// UpdatePriceUnitType The type of the price. +type UpdatePriceUnitType string + +// UpdatePriceVolume Volume tiered price. +// +// The maximum quantity within a period determines the per-unit price for all units +// in that period. +// +// When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are +// expressed in converted billing units. +type UpdatePriceVolume struct { + // Tiers The tiers of the volume price. At least one tier is required. + Tiers []UpdatePriceTier `json:"tiers"` + + // Type The type of the price. + Type UpdatePriceVolumeType `json:"type"` +} + +// UpdatePriceVolumeType The type of the price. +type UpdatePriceVolumeType string + +// UpdateRateCardTaxConfig The tax config of the rate card. +type UpdateRateCardTaxConfig struct { + // Behavior Tax behavior. + // + // This enum is used to specify whether tax is included in the price or excluded + // from the price. + Behavior *BillingTaxBehavior `json:"behavior,omitempty"` + + // Code TaxCode reference. + Code UpdateResourceReference `json:"code"` +} + +// UpdateResourceReference TaxCode reference. +type UpdateResourceReference struct { + // Id ULID (Universally Unique Lexicographically Sortable Identifier). + Id ULID `json:"id"` +} + +// UpdateSupplier Snapshot of the supplier's information at the time the invoice was issued. +// +// Structurally a read-only subset of `BillingParty` (the type configured on the +// billing profile), so the snapshot stays aligned with the source. `key` is +// omitted because it is not part of the snapshotted supplier data. +type UpdateSupplier struct { + // Addresses Address for where information should be sent if needed. + Addresses *UpdateBillingPartyAddresses `json:"addresses,omitempty"` + + // Id Unique identifier for the party. + Id *string `json:"id,omitempty"` + + // Name Legal name or representation of the party. + Name *string `json:"name,omitempty"` + + // TaxId The entity's legal identification used for tax purposes. They may have other + // numbers, but we're only interested in those valid for tax purposes. + TaxId *UpdateBillingPartyTaxIdentity `json:"tax_id,omitempty"` +} + +// UpsertAddonRequest Addon upsert request. +type UpsertAddonRequest struct { + // Description Optional description of the resource. + // + // Maximum 1024 characters. + Description *string `json:"description,omitempty"` + + // InstanceType The InstanceType of the add-ons. Can be "single" or "multiple". + InstanceType AddonInstanceType `json:"instance_type"` + + // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. + // + // Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "_". + Labels *Labels `json:"labels,omitempty"` + + // Name Display name of the resource. + // + // Between 1 and 256 characters. + Name string `json:"name"` + + // RateCards The rate cards of the add-on. + RateCards []BillingRateCard `json:"rate_cards"` +} + +// UpsertAppCustomerDataRequest AppCustomerData upsert request. +type UpsertAppCustomerDataRequest struct { // ExternalInvoicing Used if the customer has a linked external invoicing app. ExternalInvoicing *BillingAppCustomerDataExternalInvoicing `json:"external_invoicing,omitempty"` @@ -7172,6 +7725,9 @@ type CreateAddonJSONRequestBody = CreateAddonRequest // UpdateAddonJSONRequestBody defines body for UpdateAddon for application/json ContentType. type UpdateAddonJSONRequestBody = UpsertAddonRequest +// UpdateInvoiceJSONRequestBody defines body for UpdateInvoice for application/json ContentType. +type UpdateInvoiceJSONRequestBody = UpdateInvoiceRequest + // CreateCustomCurrencyJSONRequestBody defines body for CreateCustomCurrency for application/json ContentType. type CreateCustomCurrencyJSONRequestBody = CreateCurrencyCustomRequest @@ -8108,22 +8664,377 @@ func (t *BillingSubscriptionEditTiming) MergeBillingSubscriptionEditTimingEnum(v return err } -// AsDateTime returns the union data inside the BillingSubscriptionEditTiming as a DateTime -func (t BillingSubscriptionEditTiming) AsDateTime() (DateTime, error) { - var body DateTime +// AsDateTime returns the union data inside the BillingSubscriptionEditTiming as a DateTime +func (t BillingSubscriptionEditTiming) AsDateTime() (DateTime, error) { + var body DateTime + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromDateTime overwrites any union data inside the BillingSubscriptionEditTiming as the provided DateTime +func (t *BillingSubscriptionEditTiming) FromDateTime(v DateTime) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeDateTime performs a merge with any union data inside the BillingSubscriptionEditTiming, using the provided DateTime +func (t *BillingSubscriptionEditTiming) MergeDateTime(v DateTime) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t BillingSubscriptionEditTiming) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *BillingSubscriptionEditTiming) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsBillingWorkflowCollectionAlignmentSubscription returns the union data inside the BillingWorkflowCollectionAlignment as a BillingWorkflowCollectionAlignmentSubscription +func (t BillingWorkflowCollectionAlignment) AsBillingWorkflowCollectionAlignmentSubscription() (BillingWorkflowCollectionAlignmentSubscription, error) { + var body BillingWorkflowCollectionAlignmentSubscription + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBillingWorkflowCollectionAlignmentSubscription overwrites any union data inside the BillingWorkflowCollectionAlignment as the provided BillingWorkflowCollectionAlignmentSubscription +func (t *BillingWorkflowCollectionAlignment) FromBillingWorkflowCollectionAlignmentSubscription(v BillingWorkflowCollectionAlignmentSubscription) error { + v.Type = "subscription" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBillingWorkflowCollectionAlignmentSubscription performs a merge with any union data inside the BillingWorkflowCollectionAlignment, using the provided BillingWorkflowCollectionAlignmentSubscription +func (t *BillingWorkflowCollectionAlignment) MergeBillingWorkflowCollectionAlignmentSubscription(v BillingWorkflowCollectionAlignmentSubscription) error { + v.Type = "subscription" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBillingWorkflowCollectionAlignmentAnchored returns the union data inside the BillingWorkflowCollectionAlignment as a BillingWorkflowCollectionAlignmentAnchored +func (t BillingWorkflowCollectionAlignment) AsBillingWorkflowCollectionAlignmentAnchored() (BillingWorkflowCollectionAlignmentAnchored, error) { + var body BillingWorkflowCollectionAlignmentAnchored + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBillingWorkflowCollectionAlignmentAnchored overwrites any union data inside the BillingWorkflowCollectionAlignment as the provided BillingWorkflowCollectionAlignmentAnchored +func (t *BillingWorkflowCollectionAlignment) FromBillingWorkflowCollectionAlignmentAnchored(v BillingWorkflowCollectionAlignmentAnchored) error { + v.Type = "anchored" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBillingWorkflowCollectionAlignmentAnchored performs a merge with any union data inside the BillingWorkflowCollectionAlignment, using the provided BillingWorkflowCollectionAlignmentAnchored +func (t *BillingWorkflowCollectionAlignment) MergeBillingWorkflowCollectionAlignmentAnchored(v BillingWorkflowCollectionAlignmentAnchored) error { + v.Type = "anchored" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t BillingWorkflowCollectionAlignment) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t BillingWorkflowCollectionAlignment) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "anchored": + return t.AsBillingWorkflowCollectionAlignmentAnchored() + case "subscription": + return t.AsBillingWorkflowCollectionAlignmentSubscription() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t BillingWorkflowCollectionAlignment) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *BillingWorkflowCollectionAlignment) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsBillingWorkflowPaymentChargeAutomaticallySettings returns the union data inside the BillingWorkflowPaymentSettings as a BillingWorkflowPaymentChargeAutomaticallySettings +func (t BillingWorkflowPaymentSettings) AsBillingWorkflowPaymentChargeAutomaticallySettings() (BillingWorkflowPaymentChargeAutomaticallySettings, error) { + var body BillingWorkflowPaymentChargeAutomaticallySettings + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBillingWorkflowPaymentChargeAutomaticallySettings overwrites any union data inside the BillingWorkflowPaymentSettings as the provided BillingWorkflowPaymentChargeAutomaticallySettings +func (t *BillingWorkflowPaymentSettings) FromBillingWorkflowPaymentChargeAutomaticallySettings(v BillingWorkflowPaymentChargeAutomaticallySettings) error { + v.CollectionMethod = "charge_automatically" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBillingWorkflowPaymentChargeAutomaticallySettings performs a merge with any union data inside the BillingWorkflowPaymentSettings, using the provided BillingWorkflowPaymentChargeAutomaticallySettings +func (t *BillingWorkflowPaymentSettings) MergeBillingWorkflowPaymentChargeAutomaticallySettings(v BillingWorkflowPaymentChargeAutomaticallySettings) error { + v.CollectionMethod = "charge_automatically" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBillingWorkflowPaymentSendInvoiceSettings returns the union data inside the BillingWorkflowPaymentSettings as a BillingWorkflowPaymentSendInvoiceSettings +func (t BillingWorkflowPaymentSettings) AsBillingWorkflowPaymentSendInvoiceSettings() (BillingWorkflowPaymentSendInvoiceSettings, error) { + var body BillingWorkflowPaymentSendInvoiceSettings + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBillingWorkflowPaymentSendInvoiceSettings overwrites any union data inside the BillingWorkflowPaymentSettings as the provided BillingWorkflowPaymentSendInvoiceSettings +func (t *BillingWorkflowPaymentSettings) FromBillingWorkflowPaymentSendInvoiceSettings(v BillingWorkflowPaymentSendInvoiceSettings) error { + v.CollectionMethod = "send_invoice" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBillingWorkflowPaymentSendInvoiceSettings performs a merge with any union data inside the BillingWorkflowPaymentSettings, using the provided BillingWorkflowPaymentSendInvoiceSettings +func (t *BillingWorkflowPaymentSettings) MergeBillingWorkflowPaymentSendInvoiceSettings(v BillingWorkflowPaymentSendInvoiceSettings) error { + v.CollectionMethod = "send_invoice" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t BillingWorkflowPaymentSettings) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"collection_method"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t BillingWorkflowPaymentSettings) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "charge_automatically": + return t.AsBillingWorkflowPaymentChargeAutomaticallySettings() + case "send_invoice": + return t.AsBillingWorkflowPaymentSendInvoiceSettings() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t BillingWorkflowPaymentSettings) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *BillingWorkflowPaymentSettings) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsCreateChargeFlatFeeRequest returns the union data inside the CreateChargeRequest as a CreateChargeFlatFeeRequest +func (t CreateChargeRequest) AsCreateChargeFlatFeeRequest() (CreateChargeFlatFeeRequest, error) { + var body CreateChargeFlatFeeRequest + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCreateChargeFlatFeeRequest overwrites any union data inside the CreateChargeRequest as the provided CreateChargeFlatFeeRequest +func (t *CreateChargeRequest) FromCreateChargeFlatFeeRequest(v CreateChargeFlatFeeRequest) error { + v.Type = "flat_fee" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCreateChargeFlatFeeRequest performs a merge with any union data inside the CreateChargeRequest, using the provided CreateChargeFlatFeeRequest +func (t *CreateChargeRequest) MergeCreateChargeFlatFeeRequest(v CreateChargeFlatFeeRequest) error { + v.Type = "flat_fee" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsCreateChargeUsageBasedRequest returns the union data inside the CreateChargeRequest as a CreateChargeUsageBasedRequest +func (t CreateChargeRequest) AsCreateChargeUsageBasedRequest() (CreateChargeUsageBasedRequest, error) { + var body CreateChargeUsageBasedRequest + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCreateChargeUsageBasedRequest overwrites any union data inside the CreateChargeRequest as the provided CreateChargeUsageBasedRequest +func (t *CreateChargeRequest) FromCreateChargeUsageBasedRequest(v CreateChargeUsageBasedRequest) error { + v.Type = "usage_based" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCreateChargeUsageBasedRequest performs a merge with any union data inside the CreateChargeRequest, using the provided CreateChargeUsageBasedRequest +func (t *CreateChargeRequest) MergeCreateChargeUsageBasedRequest(v CreateChargeUsageBasedRequest) error { + v.Type = "usage_based" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t CreateChargeRequest) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t CreateChargeRequest) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "flat_fee": + return t.AsCreateChargeFlatFeeRequest() + case "usage_based": + return t.AsCreateChargeUsageBasedRequest() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t CreateChargeRequest) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *CreateChargeRequest) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsInvalidParameterStandard returns the union data inside the InvalidParameters_Item as a InvalidParameterStandard +func (t InvalidParameters_Item) AsInvalidParameterStandard() (InvalidParameterStandard, error) { + var body InvalidParameterStandard + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromInvalidParameterStandard overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterStandard +func (t *InvalidParameters_Item) FromInvalidParameterStandard(v InvalidParameterStandard) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeInvalidParameterStandard performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterStandard +func (t *InvalidParameters_Item) MergeInvalidParameterStandard(v InvalidParameterStandard) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsInvalidParameterMinimumLength returns the union data inside the InvalidParameters_Item as a InvalidParameterMinimumLength +func (t InvalidParameters_Item) AsInvalidParameterMinimumLength() (InvalidParameterMinimumLength, error) { + var body InvalidParameterMinimumLength + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromInvalidParameterMinimumLength overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterMinimumLength +func (t *InvalidParameters_Item) FromInvalidParameterMinimumLength(v InvalidParameterMinimumLength) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeInvalidParameterMinimumLength performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterMinimumLength +func (t *InvalidParameters_Item) MergeInvalidParameterMinimumLength(v InvalidParameterMinimumLength) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsInvalidParameterMaximumLength returns the union data inside the InvalidParameters_Item as a InvalidParameterMaximumLength +func (t InvalidParameters_Item) AsInvalidParameterMaximumLength() (InvalidParameterMaximumLength, error) { + var body InvalidParameterMaximumLength err := json.Unmarshal(t.union, &body) return body, err } -// FromDateTime overwrites any union data inside the BillingSubscriptionEditTiming as the provided DateTime -func (t *BillingSubscriptionEditTiming) FromDateTime(v DateTime) error { +// FromInvalidParameterMaximumLength overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterMaximumLength +func (t *InvalidParameters_Item) FromInvalidParameterMaximumLength(v InvalidParameterMaximumLength) error { b, err := json.Marshal(v) t.union = b return err } -// MergeDateTime performs a merge with any union data inside the BillingSubscriptionEditTiming, using the provided DateTime -func (t *BillingSubscriptionEditTiming) MergeDateTime(v DateTime) error { +// MergeInvalidParameterMaximumLength performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterMaximumLength +func (t *InvalidParameters_Item) MergeInvalidParameterMaximumLength(v InvalidParameterMaximumLength) error { b, err := json.Marshal(v) if err != nil { return err @@ -8134,34 +9045,22 @@ func (t *BillingSubscriptionEditTiming) MergeDateTime(v DateTime) error { return err } -func (t BillingSubscriptionEditTiming) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *BillingSubscriptionEditTiming) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsBillingWorkflowCollectionAlignmentSubscription returns the union data inside the BillingWorkflowCollectionAlignment as a BillingWorkflowCollectionAlignmentSubscription -func (t BillingWorkflowCollectionAlignment) AsBillingWorkflowCollectionAlignmentSubscription() (BillingWorkflowCollectionAlignmentSubscription, error) { - var body BillingWorkflowCollectionAlignmentSubscription +// AsInvalidParameterChoiceItem returns the union data inside the InvalidParameters_Item as a InvalidParameterChoiceItem +func (t InvalidParameters_Item) AsInvalidParameterChoiceItem() (InvalidParameterChoiceItem, error) { + var body InvalidParameterChoiceItem err := json.Unmarshal(t.union, &body) return body, err } -// FromBillingWorkflowCollectionAlignmentSubscription overwrites any union data inside the BillingWorkflowCollectionAlignment as the provided BillingWorkflowCollectionAlignmentSubscription -func (t *BillingWorkflowCollectionAlignment) FromBillingWorkflowCollectionAlignmentSubscription(v BillingWorkflowCollectionAlignmentSubscription) error { - v.Type = "subscription" +// FromInvalidParameterChoiceItem overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterChoiceItem +func (t *InvalidParameters_Item) FromInvalidParameterChoiceItem(v InvalidParameterChoiceItem) error { b, err := json.Marshal(v) t.union = b return err } -// MergeBillingWorkflowCollectionAlignmentSubscription performs a merge with any union data inside the BillingWorkflowCollectionAlignment, using the provided BillingWorkflowCollectionAlignmentSubscription -func (t *BillingWorkflowCollectionAlignment) MergeBillingWorkflowCollectionAlignmentSubscription(v BillingWorkflowCollectionAlignmentSubscription) error { - v.Type = "subscription" +// MergeInvalidParameterChoiceItem performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterChoiceItem +func (t *InvalidParameters_Item) MergeInvalidParameterChoiceItem(v InvalidParameterChoiceItem) error { b, err := json.Marshal(v) if err != nil { return err @@ -8172,24 +9071,22 @@ func (t *BillingWorkflowCollectionAlignment) MergeBillingWorkflowCollectionAlign return err } -// AsBillingWorkflowCollectionAlignmentAnchored returns the union data inside the BillingWorkflowCollectionAlignment as a BillingWorkflowCollectionAlignmentAnchored -func (t BillingWorkflowCollectionAlignment) AsBillingWorkflowCollectionAlignmentAnchored() (BillingWorkflowCollectionAlignmentAnchored, error) { - var body BillingWorkflowCollectionAlignmentAnchored +// AsInvalidParameterDependentItem returns the union data inside the InvalidParameters_Item as a InvalidParameterDependentItem +func (t InvalidParameters_Item) AsInvalidParameterDependentItem() (InvalidParameterDependentItem, error) { + var body InvalidParameterDependentItem err := json.Unmarshal(t.union, &body) return body, err } -// FromBillingWorkflowCollectionAlignmentAnchored overwrites any union data inside the BillingWorkflowCollectionAlignment as the provided BillingWorkflowCollectionAlignmentAnchored -func (t *BillingWorkflowCollectionAlignment) FromBillingWorkflowCollectionAlignmentAnchored(v BillingWorkflowCollectionAlignmentAnchored) error { - v.Type = "anchored" +// FromInvalidParameterDependentItem overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterDependentItem +func (t *InvalidParameters_Item) FromInvalidParameterDependentItem(v InvalidParameterDependentItem) error { b, err := json.Marshal(v) t.union = b return err } -// MergeBillingWorkflowCollectionAlignmentAnchored performs a merge with any union data inside the BillingWorkflowCollectionAlignment, using the provided BillingWorkflowCollectionAlignmentAnchored -func (t *BillingWorkflowCollectionAlignment) MergeBillingWorkflowCollectionAlignmentAnchored(v BillingWorkflowCollectionAlignmentAnchored) error { - v.Type = "anchored" +// MergeInvalidParameterDependentItem performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterDependentItem +func (t *InvalidParameters_Item) MergeInvalidParameterDependentItem(v InvalidParameterDependentItem) error { b, err := json.Marshal(v) if err != nil { return err @@ -8200,56 +9097,33 @@ func (t *BillingWorkflowCollectionAlignment) MergeBillingWorkflowCollectionAlign return err } -func (t BillingWorkflowCollectionAlignment) Discriminator() (string, error) { - var discriminator struct { - Discriminator string `json:"type"` - } - err := json.Unmarshal(t.union, &discriminator) - return discriminator.Discriminator, err -} - -func (t BillingWorkflowCollectionAlignment) ValueByDiscriminator() (interface{}, error) { - discriminator, err := t.Discriminator() - if err != nil { - return nil, err - } - switch discriminator { - case "anchored": - return t.AsBillingWorkflowCollectionAlignmentAnchored() - case "subscription": - return t.AsBillingWorkflowCollectionAlignmentSubscription() - default: - return nil, errors.New("unknown discriminator value: " + discriminator) - } -} - -func (t BillingWorkflowCollectionAlignment) MarshalJSON() ([]byte, error) { +func (t InvalidParameters_Item) MarshalJSON() ([]byte, error) { b, err := t.union.MarshalJSON() return b, err } -func (t *BillingWorkflowCollectionAlignment) UnmarshalJSON(b []byte) error { +func (t *InvalidParameters_Item) UnmarshalJSON(b []byte) error { err := t.union.UnmarshalJSON(b) return err } -// AsBillingWorkflowPaymentChargeAutomaticallySettings returns the union data inside the BillingWorkflowPaymentSettings as a BillingWorkflowPaymentChargeAutomaticallySettings -func (t BillingWorkflowPaymentSettings) AsBillingWorkflowPaymentChargeAutomaticallySettings() (BillingWorkflowPaymentChargeAutomaticallySettings, error) { - var body BillingWorkflowPaymentChargeAutomaticallySettings +// AsUpdateBillingWorkflowPaymentChargeAutomaticallySettings returns the union data inside the UpdateBillingWorkflowPaymentSettings as a UpdateBillingWorkflowPaymentChargeAutomaticallySettings +func (t UpdateBillingWorkflowPaymentSettings) AsUpdateBillingWorkflowPaymentChargeAutomaticallySettings() (UpdateBillingWorkflowPaymentChargeAutomaticallySettings, error) { + var body UpdateBillingWorkflowPaymentChargeAutomaticallySettings err := json.Unmarshal(t.union, &body) return body, err } -// FromBillingWorkflowPaymentChargeAutomaticallySettings overwrites any union data inside the BillingWorkflowPaymentSettings as the provided BillingWorkflowPaymentChargeAutomaticallySettings -func (t *BillingWorkflowPaymentSettings) FromBillingWorkflowPaymentChargeAutomaticallySettings(v BillingWorkflowPaymentChargeAutomaticallySettings) error { +// FromUpdateBillingWorkflowPaymentChargeAutomaticallySettings overwrites any union data inside the UpdateBillingWorkflowPaymentSettings as the provided UpdateBillingWorkflowPaymentChargeAutomaticallySettings +func (t *UpdateBillingWorkflowPaymentSettings) FromUpdateBillingWorkflowPaymentChargeAutomaticallySettings(v UpdateBillingWorkflowPaymentChargeAutomaticallySettings) error { v.CollectionMethod = "charge_automatically" b, err := json.Marshal(v) t.union = b return err } -// MergeBillingWorkflowPaymentChargeAutomaticallySettings performs a merge with any union data inside the BillingWorkflowPaymentSettings, using the provided BillingWorkflowPaymentChargeAutomaticallySettings -func (t *BillingWorkflowPaymentSettings) MergeBillingWorkflowPaymentChargeAutomaticallySettings(v BillingWorkflowPaymentChargeAutomaticallySettings) error { +// MergeUpdateBillingWorkflowPaymentChargeAutomaticallySettings performs a merge with any union data inside the UpdateBillingWorkflowPaymentSettings, using the provided UpdateBillingWorkflowPaymentChargeAutomaticallySettings +func (t *UpdateBillingWorkflowPaymentSettings) MergeUpdateBillingWorkflowPaymentChargeAutomaticallySettings(v UpdateBillingWorkflowPaymentChargeAutomaticallySettings) error { v.CollectionMethod = "charge_automatically" b, err := json.Marshal(v) if err != nil { @@ -8261,23 +9135,23 @@ func (t *BillingWorkflowPaymentSettings) MergeBillingWorkflowPaymentChargeAutoma return err } -// AsBillingWorkflowPaymentSendInvoiceSettings returns the union data inside the BillingWorkflowPaymentSettings as a BillingWorkflowPaymentSendInvoiceSettings -func (t BillingWorkflowPaymentSettings) AsBillingWorkflowPaymentSendInvoiceSettings() (BillingWorkflowPaymentSendInvoiceSettings, error) { - var body BillingWorkflowPaymentSendInvoiceSettings +// AsUpdateBillingWorkflowPaymentSendInvoiceSettings returns the union data inside the UpdateBillingWorkflowPaymentSettings as a UpdateBillingWorkflowPaymentSendInvoiceSettings +func (t UpdateBillingWorkflowPaymentSettings) AsUpdateBillingWorkflowPaymentSendInvoiceSettings() (UpdateBillingWorkflowPaymentSendInvoiceSettings, error) { + var body UpdateBillingWorkflowPaymentSendInvoiceSettings err := json.Unmarshal(t.union, &body) return body, err } -// FromBillingWorkflowPaymentSendInvoiceSettings overwrites any union data inside the BillingWorkflowPaymentSettings as the provided BillingWorkflowPaymentSendInvoiceSettings -func (t *BillingWorkflowPaymentSettings) FromBillingWorkflowPaymentSendInvoiceSettings(v BillingWorkflowPaymentSendInvoiceSettings) error { +// FromUpdateBillingWorkflowPaymentSendInvoiceSettings overwrites any union data inside the UpdateBillingWorkflowPaymentSettings as the provided UpdateBillingWorkflowPaymentSendInvoiceSettings +func (t *UpdateBillingWorkflowPaymentSettings) FromUpdateBillingWorkflowPaymentSendInvoiceSettings(v UpdateBillingWorkflowPaymentSendInvoiceSettings) error { v.CollectionMethod = "send_invoice" b, err := json.Marshal(v) t.union = b return err } -// MergeBillingWorkflowPaymentSendInvoiceSettings performs a merge with any union data inside the BillingWorkflowPaymentSettings, using the provided BillingWorkflowPaymentSendInvoiceSettings -func (t *BillingWorkflowPaymentSettings) MergeBillingWorkflowPaymentSendInvoiceSettings(v BillingWorkflowPaymentSendInvoiceSettings) error { +// MergeUpdateBillingWorkflowPaymentSendInvoiceSettings performs a merge with any union data inside the UpdateBillingWorkflowPaymentSettings, using the provided UpdateBillingWorkflowPaymentSendInvoiceSettings +func (t *UpdateBillingWorkflowPaymentSettings) MergeUpdateBillingWorkflowPaymentSendInvoiceSettings(v UpdateBillingWorkflowPaymentSendInvoiceSettings) error { v.CollectionMethod = "send_invoice" b, err := json.Marshal(v) if err != nil { @@ -8289,7 +9163,7 @@ func (t *BillingWorkflowPaymentSettings) MergeBillingWorkflowPaymentSendInvoiceS return err } -func (t BillingWorkflowPaymentSettings) Discriminator() (string, error) { +func (t UpdateBillingWorkflowPaymentSettings) Discriminator() (string, error) { var discriminator struct { Discriminator string `json:"collection_method"` } @@ -8297,49 +9171,49 @@ func (t BillingWorkflowPaymentSettings) Discriminator() (string, error) { return discriminator.Discriminator, err } -func (t BillingWorkflowPaymentSettings) ValueByDiscriminator() (interface{}, error) { +func (t UpdateBillingWorkflowPaymentSettings) ValueByDiscriminator() (interface{}, error) { discriminator, err := t.Discriminator() if err != nil { return nil, err } switch discriminator { case "charge_automatically": - return t.AsBillingWorkflowPaymentChargeAutomaticallySettings() + return t.AsUpdateBillingWorkflowPaymentChargeAutomaticallySettings() case "send_invoice": - return t.AsBillingWorkflowPaymentSendInvoiceSettings() + return t.AsUpdateBillingWorkflowPaymentSendInvoiceSettings() default: return nil, errors.New("unknown discriminator value: " + discriminator) } } -func (t BillingWorkflowPaymentSettings) MarshalJSON() ([]byte, error) { +func (t UpdateBillingWorkflowPaymentSettings) MarshalJSON() ([]byte, error) { b, err := t.union.MarshalJSON() return b, err } -func (t *BillingWorkflowPaymentSettings) UnmarshalJSON(b []byte) error { +func (t *UpdateBillingWorkflowPaymentSettings) UnmarshalJSON(b []byte) error { err := t.union.UnmarshalJSON(b) return err } -// AsCreateChargeFlatFeeRequest returns the union data inside the CreateChargeRequest as a CreateChargeFlatFeeRequest -func (t CreateChargeRequest) AsCreateChargeFlatFeeRequest() (CreateChargeFlatFeeRequest, error) { - var body CreateChargeFlatFeeRequest +// AsUpdateInvoiceStandardLine returns the union data inside the UpdateInvoiceLine as a UpdateInvoiceStandardLine +func (t UpdateInvoiceLine) AsUpdateInvoiceStandardLine() (UpdateInvoiceStandardLine, error) { + var body UpdateInvoiceStandardLine err := json.Unmarshal(t.union, &body) return body, err } -// FromCreateChargeFlatFeeRequest overwrites any union data inside the CreateChargeRequest as the provided CreateChargeFlatFeeRequest -func (t *CreateChargeRequest) FromCreateChargeFlatFeeRequest(v CreateChargeFlatFeeRequest) error { - v.Type = "flat_fee" +// FromUpdateInvoiceStandardLine overwrites any union data inside the UpdateInvoiceLine as the provided UpdateInvoiceStandardLine +func (t *UpdateInvoiceLine) FromUpdateInvoiceStandardLine(v UpdateInvoiceStandardLine) error { + v.Type = "standard_line" b, err := json.Marshal(v) t.union = b return err } -// MergeCreateChargeFlatFeeRequest performs a merge with any union data inside the CreateChargeRequest, using the provided CreateChargeFlatFeeRequest -func (t *CreateChargeRequest) MergeCreateChargeFlatFeeRequest(v CreateChargeFlatFeeRequest) error { - v.Type = "flat_fee" +// MergeUpdateInvoiceStandardLine performs a merge with any union data inside the UpdateInvoiceLine, using the provided UpdateInvoiceStandardLine +func (t *UpdateInvoiceLine) MergeUpdateInvoiceStandardLine(v UpdateInvoiceStandardLine) error { + v.Type = "standard_line" b, err := json.Marshal(v) if err != nil { return err @@ -8350,24 +9224,55 @@ func (t *CreateChargeRequest) MergeCreateChargeFlatFeeRequest(v CreateChargeFlat return err } -// AsCreateChargeUsageBasedRequest returns the union data inside the CreateChargeRequest as a CreateChargeUsageBasedRequest -func (t CreateChargeRequest) AsCreateChargeUsageBasedRequest() (CreateChargeUsageBasedRequest, error) { - var body CreateChargeUsageBasedRequest +func (t UpdateInvoiceLine) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t UpdateInvoiceLine) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "standard_line": + return t.AsUpdateInvoiceStandardLine() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t UpdateInvoiceLine) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *UpdateInvoiceLine) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsUpdateInvoiceStandardRequest returns the union data inside the UpdateInvoiceRequest as a UpdateInvoiceStandardRequest +func (t UpdateInvoiceRequest) AsUpdateInvoiceStandardRequest() (UpdateInvoiceStandardRequest, error) { + var body UpdateInvoiceStandardRequest err := json.Unmarshal(t.union, &body) return body, err } -// FromCreateChargeUsageBasedRequest overwrites any union data inside the CreateChargeRequest as the provided CreateChargeUsageBasedRequest -func (t *CreateChargeRequest) FromCreateChargeUsageBasedRequest(v CreateChargeUsageBasedRequest) error { - v.Type = "usage_based" +// FromUpdateInvoiceStandardRequest overwrites any union data inside the UpdateInvoiceRequest as the provided UpdateInvoiceStandardRequest +func (t *UpdateInvoiceRequest) FromUpdateInvoiceStandardRequest(v UpdateInvoiceStandardRequest) error { + v.Type = "standard" b, err := json.Marshal(v) t.union = b return err } -// MergeCreateChargeUsageBasedRequest performs a merge with any union data inside the CreateChargeRequest, using the provided CreateChargeUsageBasedRequest -func (t *CreateChargeRequest) MergeCreateChargeUsageBasedRequest(v CreateChargeUsageBasedRequest) error { - v.Type = "usage_based" +// MergeUpdateInvoiceStandardRequest performs a merge with any union data inside the UpdateInvoiceRequest, using the provided UpdateInvoiceStandardRequest +func (t *UpdateInvoiceRequest) MergeUpdateInvoiceStandardRequest(v UpdateInvoiceStandardRequest) error { + v.Type = "standard" b, err := json.Marshal(v) if err != nil { return err @@ -8378,7 +9283,7 @@ func (t *CreateChargeRequest) MergeCreateChargeUsageBasedRequest(v CreateChargeU return err } -func (t CreateChargeRequest) Discriminator() (string, error) { +func (t UpdateInvoiceRequest) Discriminator() (string, error) { var discriminator struct { Discriminator string `json:"type"` } @@ -8386,47 +9291,47 @@ func (t CreateChargeRequest) Discriminator() (string, error) { return discriminator.Discriminator, err } -func (t CreateChargeRequest) ValueByDiscriminator() (interface{}, error) { +func (t UpdateInvoiceRequest) ValueByDiscriminator() (interface{}, error) { discriminator, err := t.Discriminator() if err != nil { return nil, err } switch discriminator { - case "flat_fee": - return t.AsCreateChargeFlatFeeRequest() - case "usage_based": - return t.AsCreateChargeUsageBasedRequest() + case "standard": + return t.AsUpdateInvoiceStandardRequest() default: return nil, errors.New("unknown discriminator value: " + discriminator) } } -func (t CreateChargeRequest) MarshalJSON() ([]byte, error) { +func (t UpdateInvoiceRequest) MarshalJSON() ([]byte, error) { b, err := t.union.MarshalJSON() return b, err } -func (t *CreateChargeRequest) UnmarshalJSON(b []byte) error { +func (t *UpdateInvoiceRequest) UnmarshalJSON(b []byte) error { err := t.union.UnmarshalJSON(b) return err } -// AsInvalidParameterStandard returns the union data inside the InvalidParameters_Item as a InvalidParameterStandard -func (t InvalidParameters_Item) AsInvalidParameterStandard() (InvalidParameterStandard, error) { - var body InvalidParameterStandard +// AsUpdatePriceFree returns the union data inside the UpdatePrice as a UpdatePriceFree +func (t UpdatePrice) AsUpdatePriceFree() (UpdatePriceFree, error) { + var body UpdatePriceFree err := json.Unmarshal(t.union, &body) return body, err } -// FromInvalidParameterStandard overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterStandard -func (t *InvalidParameters_Item) FromInvalidParameterStandard(v InvalidParameterStandard) error { +// FromUpdatePriceFree overwrites any union data inside the UpdatePrice as the provided UpdatePriceFree +func (t *UpdatePrice) FromUpdatePriceFree(v UpdatePriceFree) error { + v.Type = "free" b, err := json.Marshal(v) t.union = b return err } -// MergeInvalidParameterStandard performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterStandard -func (t *InvalidParameters_Item) MergeInvalidParameterStandard(v InvalidParameterStandard) error { +// MergeUpdatePriceFree performs a merge with any union data inside the UpdatePrice, using the provided UpdatePriceFree +func (t *UpdatePrice) MergeUpdatePriceFree(v UpdatePriceFree) error { + v.Type = "free" b, err := json.Marshal(v) if err != nil { return err @@ -8437,22 +9342,24 @@ func (t *InvalidParameters_Item) MergeInvalidParameterStandard(v InvalidParamete return err } -// AsInvalidParameterMinimumLength returns the union data inside the InvalidParameters_Item as a InvalidParameterMinimumLength -func (t InvalidParameters_Item) AsInvalidParameterMinimumLength() (InvalidParameterMinimumLength, error) { - var body InvalidParameterMinimumLength +// AsUpdatePriceFlat returns the union data inside the UpdatePrice as a UpdatePriceFlat +func (t UpdatePrice) AsUpdatePriceFlat() (UpdatePriceFlat, error) { + var body UpdatePriceFlat err := json.Unmarshal(t.union, &body) return body, err } -// FromInvalidParameterMinimumLength overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterMinimumLength -func (t *InvalidParameters_Item) FromInvalidParameterMinimumLength(v InvalidParameterMinimumLength) error { +// FromUpdatePriceFlat overwrites any union data inside the UpdatePrice as the provided UpdatePriceFlat +func (t *UpdatePrice) FromUpdatePriceFlat(v UpdatePriceFlat) error { + v.Type = "flat" b, err := json.Marshal(v) t.union = b return err } -// MergeInvalidParameterMinimumLength performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterMinimumLength -func (t *InvalidParameters_Item) MergeInvalidParameterMinimumLength(v InvalidParameterMinimumLength) error { +// MergeUpdatePriceFlat performs a merge with any union data inside the UpdatePrice, using the provided UpdatePriceFlat +func (t *UpdatePrice) MergeUpdatePriceFlat(v UpdatePriceFlat) error { + v.Type = "flat" b, err := json.Marshal(v) if err != nil { return err @@ -8463,22 +9370,24 @@ func (t *InvalidParameters_Item) MergeInvalidParameterMinimumLength(v InvalidPar return err } -// AsInvalidParameterMaximumLength returns the union data inside the InvalidParameters_Item as a InvalidParameterMaximumLength -func (t InvalidParameters_Item) AsInvalidParameterMaximumLength() (InvalidParameterMaximumLength, error) { - var body InvalidParameterMaximumLength +// AsUpdatePriceUnit returns the union data inside the UpdatePrice as a UpdatePriceUnit +func (t UpdatePrice) AsUpdatePriceUnit() (UpdatePriceUnit, error) { + var body UpdatePriceUnit err := json.Unmarshal(t.union, &body) return body, err } -// FromInvalidParameterMaximumLength overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterMaximumLength -func (t *InvalidParameters_Item) FromInvalidParameterMaximumLength(v InvalidParameterMaximumLength) error { +// FromUpdatePriceUnit overwrites any union data inside the UpdatePrice as the provided UpdatePriceUnit +func (t *UpdatePrice) FromUpdatePriceUnit(v UpdatePriceUnit) error { + v.Type = "unit" b, err := json.Marshal(v) t.union = b return err } -// MergeInvalidParameterMaximumLength performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterMaximumLength -func (t *InvalidParameters_Item) MergeInvalidParameterMaximumLength(v InvalidParameterMaximumLength) error { +// MergeUpdatePriceUnit performs a merge with any union data inside the UpdatePrice, using the provided UpdatePriceUnit +func (t *UpdatePrice) MergeUpdatePriceUnit(v UpdatePriceUnit) error { + v.Type = "unit" b, err := json.Marshal(v) if err != nil { return err @@ -8489,22 +9398,24 @@ func (t *InvalidParameters_Item) MergeInvalidParameterMaximumLength(v InvalidPar return err } -// AsInvalidParameterChoiceItem returns the union data inside the InvalidParameters_Item as a InvalidParameterChoiceItem -func (t InvalidParameters_Item) AsInvalidParameterChoiceItem() (InvalidParameterChoiceItem, error) { - var body InvalidParameterChoiceItem +// AsUpdatePriceGraduated returns the union data inside the UpdatePrice as a UpdatePriceGraduated +func (t UpdatePrice) AsUpdatePriceGraduated() (UpdatePriceGraduated, error) { + var body UpdatePriceGraduated err := json.Unmarshal(t.union, &body) return body, err } -// FromInvalidParameterChoiceItem overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterChoiceItem -func (t *InvalidParameters_Item) FromInvalidParameterChoiceItem(v InvalidParameterChoiceItem) error { +// FromUpdatePriceGraduated overwrites any union data inside the UpdatePrice as the provided UpdatePriceGraduated +func (t *UpdatePrice) FromUpdatePriceGraduated(v UpdatePriceGraduated) error { + v.Type = "graduated" b, err := json.Marshal(v) t.union = b return err } -// MergeInvalidParameterChoiceItem performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterChoiceItem -func (t *InvalidParameters_Item) MergeInvalidParameterChoiceItem(v InvalidParameterChoiceItem) error { +// MergeUpdatePriceGraduated performs a merge with any union data inside the UpdatePrice, using the provided UpdatePriceGraduated +func (t *UpdatePrice) MergeUpdatePriceGraduated(v UpdatePriceGraduated) error { + v.Type = "graduated" b, err := json.Marshal(v) if err != nil { return err @@ -8515,22 +9426,24 @@ func (t *InvalidParameters_Item) MergeInvalidParameterChoiceItem(v InvalidParame return err } -// AsInvalidParameterDependentItem returns the union data inside the InvalidParameters_Item as a InvalidParameterDependentItem -func (t InvalidParameters_Item) AsInvalidParameterDependentItem() (InvalidParameterDependentItem, error) { - var body InvalidParameterDependentItem +// AsUpdatePriceVolume returns the union data inside the UpdatePrice as a UpdatePriceVolume +func (t UpdatePrice) AsUpdatePriceVolume() (UpdatePriceVolume, error) { + var body UpdatePriceVolume err := json.Unmarshal(t.union, &body) return body, err } -// FromInvalidParameterDependentItem overwrites any union data inside the InvalidParameters_Item as the provided InvalidParameterDependentItem -func (t *InvalidParameters_Item) FromInvalidParameterDependentItem(v InvalidParameterDependentItem) error { +// FromUpdatePriceVolume overwrites any union data inside the UpdatePrice as the provided UpdatePriceVolume +func (t *UpdatePrice) FromUpdatePriceVolume(v UpdatePriceVolume) error { + v.Type = "volume" b, err := json.Marshal(v) t.union = b return err } -// MergeInvalidParameterDependentItem performs a merge with any union data inside the InvalidParameters_Item, using the provided InvalidParameterDependentItem -func (t *InvalidParameters_Item) MergeInvalidParameterDependentItem(v InvalidParameterDependentItem) error { +// MergeUpdatePriceVolume performs a merge with any union data inside the UpdatePrice, using the provided UpdatePriceVolume +func (t *UpdatePrice) MergeUpdatePriceVolume(v UpdatePriceVolume) error { + v.Type = "volume" b, err := json.Marshal(v) if err != nil { return err @@ -8541,12 +9454,41 @@ func (t *InvalidParameters_Item) MergeInvalidParameterDependentItem(v InvalidPar return err } -func (t InvalidParameters_Item) MarshalJSON() ([]byte, error) { +func (t UpdatePrice) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t UpdatePrice) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "flat": + return t.AsUpdatePriceFlat() + case "free": + return t.AsUpdatePriceFree() + case "graduated": + return t.AsUpdatePriceGraduated() + case "unit": + return t.AsUpdatePriceUnit() + case "volume": + return t.AsUpdatePriceVolume() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t UpdatePrice) MarshalJSON() ([]byte, error) { b, err := t.union.MarshalJSON() return b, err } -func (t *InvalidParameters_Item) UnmarshalJSON(b []byte) error { +func (t *UpdatePrice) UnmarshalJSON(b []byte) error { err := t.union.UnmarshalJSON(b) return err } @@ -8586,6 +9528,9 @@ type ServerInterface interface { // Get a billing invoice // (GET /openmeter/billing/invoices/{invoiceId}) GetInvoice(w http.ResponseWriter, r *http.Request, invoiceId ULID) + // Update a billing invoice + // (PUT /openmeter/billing/invoices/{invoiceId}) + UpdateInvoice(w http.ResponseWriter, r *http.Request, invoiceId ULID) // List currencies // (GET /openmeter/currencies) ListCurrencies(w http.ResponseWriter, r *http.Request, params ListCurrenciesParams) @@ -8889,6 +9834,12 @@ func (_ Unimplemented) GetInvoice(w http.ResponseWriter, r *http.Request, invoic w.WriteHeader(http.StatusNotImplemented) } +// Update a billing invoice +// (PUT /openmeter/billing/invoices/{invoiceId}) +func (_ Unimplemented) UpdateInvoice(w http.ResponseWriter, r *http.Request, invoiceId ULID) { + w.WriteHeader(http.StatusNotImplemented) +} + // List currencies // (GET /openmeter/currencies) func (_ Unimplemented) ListCurrencies(w http.ResponseWriter, r *http.Request, params ListCurrenciesParams) { @@ -9662,6 +10613,31 @@ func (siw *ServerInterfaceWrapper) GetInvoice(w http.ResponseWriter, r *http.Req handler.ServeHTTP(w, r) } +// UpdateInvoice operation middleware +func (siw *ServerInterfaceWrapper) UpdateInvoice(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "invoiceId" ------------- + var invoiceId ULID + + err = runtime.BindStyledParameterWithOptions("simple", "invoiceId", chi.URLParam(r, "invoiceId"), &invoiceId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "invoiceId", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateInvoice(w, r, invoiceId) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r) +} + // ListCurrencies operation middleware func (siw *ServerInterfaceWrapper) ListCurrencies(w http.ResponseWriter, r *http.Request) { @@ -11964,6 +12940,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/openmeter/billing/invoices/{invoiceId}", wrapper.GetInvoice) }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/openmeter/billing/invoices/{invoiceId}", wrapper.UpdateInvoice) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/openmeter/currencies", wrapper.ListCurrencies) }) @@ -12423,573 +13402,640 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/v3/handlers/billinginvoices/convert.go b/api/v3/handlers/billinginvoices/convert.go index 16640cffb0..e48674b60d 100644 --- a/api/v3/handlers/billinginvoices/convert.go +++ b/api/v3/handlers/billinginvoices/convert.go @@ -18,6 +18,14 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/models/creditsapplied" "github.com/openmeterio/openmeter/openmeter/billing/models/externalid" + "github.com/openmeterio/openmeter/openmeter/productcatalog" + "github.com/openmeterio/openmeter/openmeter/streaming" + "github.com/openmeterio/openmeter/pkg/clock" + "github.com/openmeterio/openmeter/pkg/datetime" + "github.com/openmeterio/openmeter/pkg/models" + "github.com/openmeterio/openmeter/pkg/set" + "github.com/openmeterio/openmeter/pkg/slicesx" + "github.com/openmeterio/openmeter/pkg/timeutil" ) // ToAPIBillingInvoice converts a billing.Invoice domain union to the v3 API type. @@ -160,6 +168,7 @@ func toAPIWorkflow(w billing.InvoiceWorkflow) (api.BillingInvoiceWorkflowSetting invoicing := &api.BillingInvoiceWorkflowInvoicingSettings{ AutoAdvance: lo.ToPtr(config.Invoicing.AutoAdvance), DraftPeriod: lo.ToPtr(config.Invoicing.DraftPeriod.String()), + DueAfter: lo.EmptyableToPtr(config.Invoicing.DueAfter.String()), } var payment *api.BillingWorkflowPaymentSettings @@ -285,7 +294,7 @@ func mapStandardLine(line *billing.StandardLine) (api.BillingInvoiceStandardLine } return api.BillingInvoiceStandardLine{ - Id: line.ID, + Id: lo.ToPtr(line.ID), Name: line.Name, Description: line.Description, Labels: labels.FromMetadata(line.Metadata), @@ -463,3 +472,309 @@ func FromAPIInvoiceSortField(ctx context.Context, field string) (apilegacy.Invoi return "", apierrors.NewUnsupportedSortFieldError(ctx, field, "issued_at", "created_at", "service_period_start") } } + +// mergeStandardInvoiceFromAPI applies the mutable fields of an update request onto an +// already-loaded standard invoice, in place. Only description, labels, supplier, customer, +// workflow, and top-level lines are mutable; all other invoice fields are left untouched. +func mergeStandardInvoiceFromAPI(inv *billing.StandardInvoice, req api.UpdateInvoiceStandardRequest) error { + inv.Description = req.Description + + metadata, err := labels.ToMetadata(req.Labels) + if err != nil { + return fmt.Errorf("converting labels: %w", err) + } + inv.Metadata = metadata + + inv.Supplier = mergeInvoiceSupplierFromAPI(inv.Supplier, req.Supplier) + inv.Customer = mergeInvoiceCustomerFromAPI(inv.Customer, req.Customer) + + workflow, err := mergeInvoiceWorkflowFromAPI(inv.Workflow, req.Workflow) + if err != nil { + return fmt.Errorf("merging workflow: %w", err) + } + inv.Workflow = workflow + + lines, err := mergeStandardInvoiceLinesFromAPI(inv, req.Lines) + if err != nil { + return fmt.Errorf("merging lines: %w", err) + } + inv.Lines = lines + + return nil +} + +// mergeInvoiceSupplierFromAPI applies the editable BillingSupplier snapshot fields (name, tax +// id, address) onto the existing supplier contact. The party ID is not part of the update +// request and is left untouched. +func mergeInvoiceSupplierFromAPI(existing billing.SupplierContact, updated api.UpdateSupplier) billing.SupplierContact { + existing.Name = lo.FromPtrOr(updated.Name, "") + + if updated.Addresses != nil { + existing.Address = billingprofiles.FromAPIAddress(api.Address(updated.Addresses.BillingAddress)) + } else { + existing.Address = models.Address{} + } + + if updated.TaxId != nil { + existing.TaxCode = updated.TaxId.Code + } else { + existing.TaxCode = nil + } + + return existing +} + +// mergeInvoiceCustomerFromAPI applies the editable customer snapshot fields (name, billing +// address) onto the existing customer snapshot. CustomerID, key, and usage attribution are +// immutable identity fields tied to the customer at invoice creation time and are left +// untouched, regardless of what the request echoes back for them. +func mergeInvoiceCustomerFromAPI(existing billing.InvoiceCustomer, updated api.UpdateInvoiceCustomer) billing.InvoiceCustomer { + existing.Name = updated.Name + + if updated.BillingAddress != nil { + addr := billingprofiles.FromAPIAddress(api.Address(*updated.BillingAddress)) + existing.BillingAddress = &addr + } else { + existing.BillingAddress = nil + } + + return existing +} + +// mergeInvoiceWorkflowFromAPI applies the editable per-invoice workflow settings (invoicing +// auto-advance/draft period, payment collection method/due date) onto the existing workflow +// config. Omitting the invoicing or payment sub-object leaves that part of the config +// unchanged; omitted fields within a provided sub-object fall back to +// billing.DefaultWorkflowConfig, mirroring the v1 replace-update semantics. +func mergeInvoiceWorkflowFromAPI(existing billing.InvoiceWorkflow, updated api.UpdateInvoiceWorkflowSettings) (billing.InvoiceWorkflow, error) { + if invoicing := updated.Workflow.Invoicing; invoicing != nil { + existing.Config.Invoicing.AutoAdvance = lo.FromPtrOr(invoicing.AutoAdvance, billing.DefaultWorkflowConfig.Invoicing.AutoAdvance) + + if invoicing.DraftPeriod == nil { + existing.Config.Invoicing.DraftPeriod = billing.DefaultWorkflowConfig.Invoicing.DraftPeriod + } else { + period, err := datetime.ISODurationString(*invoicing.DraftPeriod).Parse() + if err != nil { + return existing, billing.ValidationError{Err: fmt.Errorf("failed to parse draft period: %w", err)} + } + existing.Config.Invoicing.DraftPeriod = period + } + } + + if payment := updated.Workflow.Payment; payment != nil { + disc, err := payment.Discriminator() + if err != nil { + return existing, billing.ValidationError{Err: fmt.Errorf("failed to read payment settings type: %w", err)} + } + + switch disc { + case "charge_automatically": + existing.Config.Payment.CollectionMethod = billing.CollectionMethodChargeAutomatically + existing.Config.Invoicing.DueAfter = billing.DefaultWorkflowConfig.Invoicing.DueAfter + case "send_invoice": + sendInvoice, err := payment.AsUpdateBillingWorkflowPaymentSendInvoiceSettings() + if err != nil { + return existing, billing.ValidationError{Err: fmt.Errorf("reading send invoice settings: %w", err)} + } + + existing.Config.Payment.CollectionMethod = billing.CollectionMethodSendInvoice + + if sendInvoice.DueAfter == nil { + existing.Config.Invoicing.DueAfter = billing.DefaultWorkflowConfig.Invoicing.DueAfter + } else { + period, err := datetime.ISODurationString(*sendInvoice.DueAfter).Parse() + if err != nil { + return existing, billing.ValidationError{Err: fmt.Errorf("failed to parse due after: %w", err)} + } + existing.Config.Invoicing.DueAfter = period + } + default: + return existing, billing.ValidationError{Err: fmt.Errorf("unsupported payment collection method: %s", disc)} + } + } + + return existing, nil +} + +// mergeStandardInvoiceLinesFromAPI reconciles the invoice's top-level lines against the +// update request: lines matched by ID are merged in place, lines without an ID (or with an +// unknown ID) are created, and existing lines omitted from the request are tombstoned. A nil +// lines pointer leaves the invoice's lines untouched, since it means the field wasn't sent. +func mergeStandardInvoiceLinesFromAPI(inv *billing.StandardInvoice, lines *[]api.UpdateInvoiceLine) (billing.StandardInvoiceLines, error) { + if lines == nil { + return inv.Lines, nil + } + + linesByID, _ := slicesx.UniqueGroupBy(inv.Lines.OrEmpty(), func(line *billing.StandardLine) string { + return line.ID + }) + + foundLines := set.New[string]() + out := make([]*billing.StandardLine, 0, len(*lines)) + + processedIDs := set.New[string]() + + for _, apiLine := range *lines { + stdLine, err := apiLine.AsUpdateInvoiceStandardLine() + if err != nil { + return billing.StandardInvoiceLines{}, fmt.Errorf("reading line: %w", err) + } + + id := lo.FromPtr(stdLine.Id) + + if id != "" { + if processedIDs.Has(id) { + return billing.StandardInvoiceLines{}, billing.ValidationError{ + Err: fmt.Errorf("duplicate line ID %q in request", id), + } + } + processedIDs.Add(id) + } + + existingLine, existingLineFound := linesByID[id] + + if id == "" || !existingLineFound { + // We allow injecting fake IDs for new lines, so that discounts can reference + // those, but we are not persisting them to the database. + newLine, err := standardLineFromAPI(stdLine, inv) + if err != nil { + return billing.StandardInvoiceLines{}, fmt.Errorf("creating line: %w", err) + } + + out = append(out, newLine) + } else { + foundLines.Add(id) + + mergedLine, err := mergeStandardLineFromAPI(existingLine, stdLine) + if err != nil { + return billing.StandardInvoiceLines{}, fmt.Errorf("merging line[%s]: %w", id, err) + } + + out = append(out, mergedLine) + } + } + + lineIDs := set.New(lo.Keys(linesByID)...) + + deletedLines := set.Subtract(lineIDs, foundLines).AsSlice() + for _, id := range deletedLines { + existingLine := linesByID[id] + existingLine.DeletedAt = lo.ToPtr(clock.Now()) + out = append(out, existingLine) + } + + return billing.NewStandardInvoiceLines(out), nil +} + +// standardLineFromAPI builds a new top-level standard line from an update request line that +// has no matching existing line (empty or unrecognized ID). +func standardLineFromAPI(line api.UpdateInvoiceStandardLine, inv *billing.StandardInvoice) (*billing.StandardLine, error) { + price, taxConfig, featureKey, discounts, err := mapRateCardFromAPI(line.RateCard) + if err != nil { + return nil, fmt.Errorf("mapping rate card: %w", err) + } + + metadata, err := labels.ToMetadata(line.Labels) + if err != nil { + return nil, fmt.Errorf("converting labels: %w", err) + } + + return &billing.StandardLine{ + StandardLineBase: billing.StandardLineBase{ + ManagedResource: models.NewManagedResource(models.ManagedResourceInput{ + Namespace: inv.Namespace, + Name: line.Name, + Description: line.Description, + }), + + Metadata: metadata, + + InvoiceID: inv.ID, + Currency: inv.Currency, + + Period: timeutil.ClosedPeriod{ + From: line.ServicePeriod.From.Truncate(streaming.MinimumWindowSizeDuration), + To: line.ServicePeriod.To.Truncate(streaming.MinimumWindowSizeDuration), + }, + // InvoiceAt has no scheduling meaning for a manually added standard line (it + // only carries the original gathering-line timestamp for lines rendered from + // gathering invoices, per StandardLineBase.InvoiceAt's doc comment) but is + // required to be non-zero. Use the creation timestamp as the sanctioned fallback. + InvoiceAt: clock.Now().Truncate(streaming.MinimumWindowSizeDuration), + + TaxConfig: taxConfig, + RateCardDiscounts: discounts, + }, + UsageBased: &billing.UsageBasedLine{ + Price: price, + FeatureKey: featureKey, + }, + }, nil +} + +// mergeStandardLineFromAPI applies the editable fields of an update request line onto an +// existing top-level standard line, matched by ID. +func mergeStandardLineFromAPI(existing *billing.StandardLine, line api.UpdateInvoiceStandardLine) (*billing.StandardLine, error) { + price, taxConfig, featureKey, discounts, err := mapRateCardFromAPI(line.RateCard) + if err != nil { + return nil, fmt.Errorf("mapping rate card: %w", err) + } + + metadata, err := labels.ToMetadata(line.Labels) + if err != nil { + return nil, fmt.Errorf("converting labels: %w", err) + } + + existing.Metadata = metadata + existing.Name = line.Name + existing.Description = line.Description + + existing.Period.From = line.ServicePeriod.From.Truncate(streaming.MinimumWindowSizeDuration) + existing.Period.To = line.ServicePeriod.To.Truncate(streaming.MinimumWindowSizeDuration) + + existing.TaxConfig = taxConfig + existing.RateCardDiscounts = discounts + if existing.UsageBased == nil { + return nil, fmt.Errorf("existing line %s has no usage-based pricing", existing.ID) + } + existing.UsageBased.Price = price + existing.UsageBased.FeatureKey = featureKey + + return existing, nil +} + +// mapRateCardFromAPI maps an update request's rate card onto its domain price, tax config, +// feature key, and discounts. Feature key requiredness relative to the price type is enforced +// by billing.UsageBasedLine.Validate downstream, not here. +func mapRateCardFromAPI(rc api.UpdateInvoiceLineRateCard) (*productcatalog.Price, *billing.TaxConfig, string, billing.Discounts, error) { + price, err := plans.FromAPIBillingPrice(api.BillingPrice(rc.Price), nil) + if err != nil { + return nil, nil, "", billing.Discounts{}, fmt.Errorf("mapping price: %w", err) + } + + var discounts billing.Discounts + if rc.Discounts != nil { + pcDiscounts, err := plans.FromAPIBillingRateCardDiscounts(api.BillingRateCardDiscounts(*rc.Discounts)) + if err != nil { + return nil, nil, "", billing.Discounts{}, fmt.Errorf("mapping discounts: %w", err) + } + + discounts = billing.DiscountsFromProductCatalog(pcDiscounts).UpsertCorrelationIDs() + } + + taxConfig := billing.FromProductCatalog(addons.FromAPIBillingRateCardTaxConfig(fromAPIUpdateRateCardTaxConfig(rc.TaxConfig))) + + return price, taxConfig, lo.FromPtrOr(rc.FeatureKey, ""), discounts, nil +} + +func fromAPIUpdateRateCardTaxConfig(taxConfig *api.UpdateRateCardTaxConfig) *api.BillingRateCardTaxConfig { + if taxConfig == nil { + return nil + } + return &api.BillingRateCardTaxConfig{ + Behavior: taxConfig.Behavior, + Code: api.TaxCodeReference{ + Id: taxConfig.Code.Id, + }, + } +} diff --git a/api/v3/handlers/billinginvoices/convert_test.go b/api/v3/handlers/billinginvoices/convert_test.go new file mode 100644 index 0000000000..a60eef3d67 --- /dev/null +++ b/api/v3/handlers/billinginvoices/convert_test.go @@ -0,0 +1,173 @@ +package billinginvoices + +import ( + "testing" + "time" + + decimal "github.com/alpacahq/alpacadecimal" + "github.com/samber/lo" + "github.com/stretchr/testify/require" + + api "github.com/openmeterio/openmeter/api/v3" + "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/productcatalog" + "github.com/openmeterio/openmeter/pkg/models" + "github.com/openmeterio/openmeter/pkg/timeutil" +) + +func mergeTestPeriod() timeutil.ClosedPeriod { + return timeutil.ClosedPeriod{ + From: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), + To: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), + } +} + +func standardLineForMergeTest(t *testing.T, id string, period timeutil.ClosedPeriod) *billing.StandardLine { + t.Helper() + + return &billing.StandardLine{ + StandardLineBase: billing.StandardLineBase{ + ManagedResource: models.NewManagedResource(models.ManagedResourceInput{ + Namespace: "ns", + ID: id, + Name: "line", + CreatedAt: period.From, + UpdatedAt: period.From, + }), + InvoiceID: "invoice-id", + Currency: "USD", + Period: period, + InvoiceAt: period.To, + }, + UsageBased: &billing.UsageBasedLine{ + Price: productcatalog.NewPriceFrom(productcatalog.FlatPrice{Amount: decimal.NewFromInt(1)}), + FeatureKey: "feature-key", + }, + } +} + +func apiLineForMergeTest(t *testing.T, period timeutil.ClosedPeriod, id *string) api.UpdateInvoiceLine { + t.Helper() + + price := api.UpdatePrice{} + require.NoError(t, price.FromUpdatePriceFlat(api.UpdatePriceFlat{ + Amount: "1", + Type: api.UpdatePriceFlatTypeFlat, + })) + + standardLine := api.UpdateInvoiceStandardLine{ + Id: id, + Name: "line", + ServicePeriod: api.UpdateClosedPeriod{ + From: period.From, + To: period.To, + }, + RateCard: api.UpdateInvoiceLineRateCard{ + Price: price, + FeatureKey: lo.ToPtr("feature-key"), + }, + } + + var out api.UpdateInvoiceLine + require.NoError(t, out.FromUpdateInvoiceStandardLine(standardLine)) + + return out +} + +func TestMergeStandardInvoiceLinesFromAPITombstonesOmittedLines(t *testing.T) { + period := mergeTestPeriod() + keptLine := standardLineForMergeTest(t, "kept-line-id", period) + deletedLine := standardLineForMergeTest(t, "deleted-line-id", period) + + inv := &billing.StandardInvoice{ + StandardInvoiceBase: billing.StandardInvoiceBase{ + Namespace: "ns", + ID: "invoice-id", + Currency: "USD", + }, + Lines: billing.NewStandardInvoiceLines([]*billing.StandardLine{keptLine, deletedLine}), + } + + lines := []api.UpdateInvoiceLine{ + apiLineForMergeTest(t, period, lo.ToPtr("kept-line-id")), + apiLineForMergeTest(t, period, nil), + } + + merged, err := mergeStandardInvoiceLinesFromAPI(inv, &lines) + require.NoError(t, err) + + all := merged.OrEmpty() + require.Len(t, all, 3) + + kept, ok := lo.Find(all, func(l *billing.StandardLine) bool { return l.ID == "kept-line-id" }) + require.True(t, ok) + require.Nil(t, kept.DeletedAt) + + deleted, ok := lo.Find(all, func(l *billing.StandardLine) bool { return l.ID == "deleted-line-id" }) + require.True(t, ok) + require.NotNil(t, deleted.DeletedAt) + + newLines := lo.Filter(all, func(l *billing.StandardLine, _ int) bool { + return l.ID != "kept-line-id" && l.ID != "deleted-line-id" + }) + require.Len(t, newLines, 1) +} + +func TestMergeStandardInvoiceLinesFromAPINilLeavesLinesUnchanged(t *testing.T) { + period := mergeTestPeriod() + existing := standardLineForMergeTest(t, "line-id", period) + + inv := &billing.StandardInvoice{ + Lines: billing.NewStandardInvoiceLines([]*billing.StandardLine{existing}), + } + + merged, err := mergeStandardInvoiceLinesFromAPI(inv, nil) + require.NoError(t, err) + require.Equal(t, inv.Lines, merged) +} + +func TestMergeInvoiceCustomerFromAPIPreservesImmutableFields(t *testing.T) { + existing := billing.InvoiceCustomer{ + CustomerID: "cust-id", + Key: lo.ToPtr("cust-key"), + Name: "Old Name", + } + + updated := api.UpdateInvoiceCustomer{ + Id: "attacker-id", + Key: lo.ToPtr("attacker-key"), + Name: "New Name", + BillingAddress: &api.UpdateAddress{ + City: lo.ToPtr("Ghent"), + }, + } + + merged := mergeInvoiceCustomerFromAPI(existing, updated) + + require.Equal(t, "cust-id", merged.CustomerID) + require.Equal(t, lo.ToPtr("cust-key"), merged.Key) + require.Equal(t, "New Name", merged.Name) + require.NotNil(t, merged.BillingAddress) + require.Equal(t, "Ghent", lo.FromPtr(merged.BillingAddress.City)) +} + +func TestMergeInvoiceSupplierFromAPI(t *testing.T) { + existing := billing.SupplierContact{ + ID: "supplier-id", + Name: "Old Supplier", + } + + updated := api.UpdateSupplier{ + Id: lo.ToPtr("attacker-id"), + Name: lo.ToPtr("New Supplier"), + TaxId: &api.UpdateBillingPartyTaxIdentity{ + Code: lo.ToPtr("TAX-1"), + }, + } + + merged := mergeInvoiceSupplierFromAPI(existing, updated) + + require.Equal(t, "supplier-id", merged.ID) + require.Equal(t, "New Supplier", merged.Name) + require.Equal(t, lo.ToPtr("TAX-1"), merged.TaxCode) +} diff --git a/api/v3/handlers/billinginvoices/error_encoder.go b/api/v3/handlers/billinginvoices/error_encoder.go index f43609deb2..2369d5bc0f 100644 --- a/api/v3/handlers/billinginvoices/error_encoder.go +++ b/api/v3/handlers/billinginvoices/error_encoder.go @@ -2,8 +2,11 @@ package billinginvoices import ( "context" + "errors" "net/http" + "github.com/samber/lo" + "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/pkg/framework/commonhttp" "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport/encoder" @@ -18,3 +21,22 @@ func errorEncoder() encoder.ErrorEncoder { commonhttp.HandleErrorIfTypeMatches[billing.AppError](ctx, http.StatusBadRequest, err, w) } } + +func encodeValidationIssue() encoder.ErrorEncoder { + return func(ctx context.Context, err error, w http.ResponseWriter, r *http.Request) bool { + if err == nil { + return false + } + issues, convertErr := billing.ToValidationIssues(err) + if convertErr != nil { + return false + } + + errs := lo.Map(issues, func(issue billing.ValidationIssue, _ int) error { + return issue + }) + + commonhttp.NewHTTPError(http.StatusBadRequest, errors.Join(errs...)).EncodeError(ctx, w) + return true + } +} diff --git a/api/v3/handlers/billinginvoices/handler.go b/api/v3/handlers/billinginvoices/handler.go index a4fe87c27e..e76a6a9ed1 100644 --- a/api/v3/handlers/billinginvoices/handler.go +++ b/api/v3/handlers/billinginvoices/handler.go @@ -10,6 +10,7 @@ import ( type Handler interface { ListBillingInvoices() ListBillingInvoicesHandler GetBillingInvoice() GetBillingInvoiceHandler + UpdateBillingInvoice() UpdateBillingInvoiceHandler } type handler struct { diff --git a/api/v3/handlers/billinginvoices/update.go b/api/v3/handlers/billinginvoices/update.go new file mode 100644 index 0000000000..62a7d86ece --- /dev/null +++ b/api/v3/handlers/billinginvoices/update.go @@ -0,0 +1,105 @@ +package billinginvoices + +import ( + "context" + "fmt" + "net/http" + + api "github.com/openmeterio/openmeter/api/v3" + "github.com/openmeterio/openmeter/api/v3/apierrors" + "github.com/openmeterio/openmeter/api/v3/request" + "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/pkg/framework/commonhttp" + "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" + "github.com/openmeterio/openmeter/pkg/models" +) + +type ( + UpdateBillingInvoiceRequest struct { + Invoice billing.InvoiceID + Update api.UpdateInvoiceRequest + } + UpdateBillingInvoiceParams = string + UpdateBillingInvoiceResponse = api.BillingInvoice + UpdateBillingInvoiceHandler = httptransport.HandlerWithArgs[UpdateBillingInvoiceRequest, UpdateBillingInvoiceResponse, UpdateBillingInvoiceParams] +) + +func (h *handler) UpdateBillingInvoice() UpdateBillingInvoiceHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, invoiceId UpdateBillingInvoiceParams) (UpdateBillingInvoiceRequest, error) { + body := api.UpdateInvoiceRequest{} + if err := request.ParseBody(r, &body); err != nil { + return UpdateBillingInvoiceRequest{}, err + } + + ns, err := h.resolveNamespace(ctx) + if err != nil { + return UpdateBillingInvoiceRequest{}, err + } + + return UpdateBillingInvoiceRequest{ + Invoice: billing.InvoiceID(models.NamespacedID{ + Namespace: ns, + ID: invoiceId, + }), + Update: body, + }, nil + }, + func(ctx context.Context, request UpdateBillingInvoiceRequest) (UpdateBillingInvoiceResponse, error) { + // v3 only exposes standard invoices. UpdateStandardInvoice asserts (rather than + // error-returns) when handed a gathering invoice, so gathering invoices must be + // rejected here as not-found before we ever call it, mirroring how ToAPIBillingInvoice + // rejects them on the read path. + existing, err := h.service.GetInvoiceById(ctx, billing.GetInvoiceByIdInput{Invoice: request.Invoice}) + if err != nil { + return UpdateBillingInvoiceResponse{}, err + } + if existing.Type() != billing.InvoiceTypeStandard { + return UpdateBillingInvoiceResponse{}, billing.NotFoundError{ + ID: request.Invoice.ID, + Entity: billing.EntityInvoice, + Err: fmt.Errorf("unsupported invoice type %q", existing.Type()), + } + } + + invoiceType, err := request.Update.Discriminator() + if err != nil { + return UpdateBillingInvoiceResponse{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{ + {Field: "body.type", Reason: err.Error(), Source: apierrors.InvalidParamSourceBody}, + }) + } + switch invoiceType { + case string(api.BillingInvoiceStandardTypeStandard): + req, err := request.Update.AsUpdateInvoiceStandardRequest() + if err != nil { + return UpdateBillingInvoiceResponse{}, err + } + + updated, err := h.service.UpdateStandardInvoice(ctx, billing.UpdateStandardInvoiceInput{ + Invoice: request.Invoice, + ChangeSource: billing.ChangeSourceAPIRequest, + EditFn: func(inv *billing.StandardInvoice) error { + return mergeStandardInvoiceFromAPI(inv, req) + }, + }) + if err != nil { + return UpdateBillingInvoiceResponse{}, err + } + return ToAPIBillingInvoice(billing.NewInvoice(updated)) + default: + err := fmt.Errorf("unsupported invoice type: %s", invoiceType) + return UpdateBillingInvoiceResponse{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{ + {Field: "body", Reason: err.Error(), Source: apierrors.InvalidParamSourceBody}, + }) + } + }, + commonhttp.JSONResponseEncoderWithStatus[UpdateBillingInvoiceResponse](http.StatusOK), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("update-invoice"), + httptransport.WithErrorEncoder(apierrors.GenericErrorEncoder()), + httptransport.WithErrorEncoder(encodeValidationIssue()), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} diff --git a/api/v3/openapi.yaml b/api/v3/openapi.yaml index 9fed83a0d6..53201f5916 100644 --- a/api/v3/openapi.yaml +++ b/api/v3/openapi.yaml @@ -423,6 +423,49 @@ paths: x-private: true x-internal: true x-unstable: true + put: + operationId: update-invoice + summary: Update a billing invoice + description: |- + Update a billing invoice. + + Only the mutable fields of the invoice can be edited: description, labels, + supplier, customer, workflow settings, and top-level lines. Top-level lines are + matched by `id`; lines without an `id` are created, and existing lines omitted + from `lines` are deleted. Detailed (child) lines are always computed and cannot + be edited directly. Only invoices in draft status can be updated. + parameters: + - name: invoiceId + in: path + required: true + schema: + $ref: '#/components/schemas/ULID' + responses: + '200': + description: Invoice updated response. + content: + application/json: + schema: + $ref: '#/components/schemas/BillingInvoice' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + tags: + - OpenMeter Billing Settings + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateInvoiceRequest' + x-private: true + x-internal: true + x-unstable: true /openmeter/currencies: get: operationId: list-currencies @@ -5744,11 +5787,9 @@ components: BillingInvoiceCustomer: type: object required: - - id - name + - id properties: - id: - $ref: '#/components/schemas/ULID' name: type: string minLength: 1 @@ -5767,6 +5808,8 @@ components: - $ref: '#/components/schemas/Address' description: The billing address of the customer. Used for tax and invoicing. title: Billing Address + id: + $ref: '#/components/schemas/ULID' key: allOf: - $ref: '#/components/schemas/ExternalResourceKey' @@ -6020,25 +6063,21 @@ components: - $ref: '#/components/schemas/BillingPrice' description: The price definition used to calculate charges for this line. title: Price - readOnly: true tax_config: allOf: - $ref: '#/components/schemas/BillingRateCardTaxConfig' description: Tax configuration snapshot for this line. title: Tax config - readOnly: true feature_key: allOf: - $ref: '#/components/schemas/ResourceKey' description: The feature key associated with this line's rate card. title: Feature key - readOnly: true discounts: allOf: - $ref: '#/components/schemas/BillingRateCardDiscounts' description: Discount configuration from the rate card. title: Discounts - readOnly: true additionalProperties: false description: Rate card configuration snapshot for a usage-based invoice line. BillingInvoiceLineUsageDiscount: @@ -6157,13 +6196,11 @@ components: Snapshot of the supplier's contact information at the time the invoice was issued. title: Supplier - readOnly: true customer: allOf: - $ref: '#/components/schemas/BillingInvoiceCustomer' description: Snapshot of the customer's information at the time the invoice was issued. title: Customer - readOnly: true totals: allOf: - $ref: '#/components/schemas/BillingTotals' @@ -6204,7 +6241,6 @@ components: - standard description: Discriminator field identifying this as a standard invoice. title: Type - readOnly: true status: allOf: - $ref: '#/components/schemas/BillingInvoiceStandardStatus' @@ -6261,7 +6297,6 @@ components: - $ref: '#/components/schemas/BillingInvoiceWorkflowSettings' description: Workflow configuration snapshot captured at invoice creation time. title: Workflow - readOnly: true lines: type: array items: @@ -6270,15 +6305,16 @@ components: Line items on this invoice. Always returned on single-resource GET; omitted on list endpoints unless - explicitly expanded. + explicitly expanded. Editable via update: existing lines are matched by `id`, + lines without an `id` are created, and lines present on the invoice but omitted + from the update request are deleted. Detailed (child) lines are always computed + and cannot be edited directly. title: Lines - readOnly: true additionalProperties: false description: A standard invoice for charges owed by the customer. BillingInvoiceStandardLine: type: object required: - - id - name - created_at - updated_at @@ -6289,10 +6325,6 @@ components: - rate_card - detailed_lines properties: - id: - allOf: - - $ref: '#/components/schemas/ULID' - readOnly: true name: type: string minLength: 1 @@ -6325,13 +6357,22 @@ components: - $ref: '#/components/schemas/DateTime' description: An ISO-8601 timestamp representation of entity deletion date. readOnly: true + id: + allOf: + - $ref: '#/components/schemas/ULID' + description: |- + ID of the line. + + Optional on update: omit to create a new line, or supply the ID of an existing + line to edit it. Existing lines omitted from an update's `lines` array are + deleted. + title: ID type: type: string enum: - standard_line description: The type of charge this line item represents. title: Type - readOnly: true lifecycle_controller: allOf: - $ref: '#/components/schemas/BillingLifecycleController' @@ -6350,7 +6391,6 @@ components: For an invoice with no lines the period is empty, which means `from` will be equal to `to`. title: Service period - readOnly: true totals: allOf: - $ref: '#/components/schemas/BillingTotals' @@ -6387,7 +6427,6 @@ components: - $ref: '#/components/schemas/BillingInvoiceLineRateCard' description: The rate card configuration snapshot used to price this line item. title: Rate card - readOnly: true detailed_lines: type: array items: @@ -6514,13 +6553,11 @@ components: - $ref: '#/components/schemas/BillingInvoiceWorkflowInvoicingSettings' description: Invoicing settings for this invoice. title: Invoicing settings - readOnly: true payment: allOf: - $ref: '#/components/schemas/BillingWorkflowPaymentSettings' description: Payment settings for this invoice. title: Payment settings - readOnly: true additionalProperties: false description: |- Invoice-level snapshot of the workflow configuration. @@ -6561,13 +6598,17 @@ components: type: boolean description: Whether to automatically issue the invoice after the draft_period has passed. default: true - readOnly: true draft_period: type: string format: ISO8601 description: The period for the invoice to be kept in draft status for manual reviews. example: P1D default: P0D + due_after: + type: string + format: ISO8601 + description: The period after which the invoice is considered overdue if not paid. + example: P30D readOnly: true additionalProperties: false description: |- @@ -6605,7 +6646,6 @@ components: (collection method, due date). Profile-wide settings such as collection alignment, progressive billing, and tax policy are omitted. title: Workflow config - readOnly: true additionalProperties: false description: Snapshot of the billing workflow configuration captured at invoice creation. BillingLifecycleController: @@ -7773,9 +7813,6 @@ components: BillingSupplier: type: object properties: - id: - type: string - description: Unique identifier for the party. name: type: string description: Legal name or representation of the party. @@ -7789,6 +7826,9 @@ components: allOf: - $ref: '#/components/schemas/BillingPartyAddresses' description: Address for where information should be sent if needed. + id: + type: string + description: Unique identifier for the party. additionalProperties: false description: |- Snapshot of the supplier's information at the time the invoice was issued. @@ -11360,83 +11400,716 @@ components: x-go-type-import: path: github.com/openmeterio/openmeter/api/v3/filters x-go-type: filters.FilterULID - UpdateCreditGrantExternalSettlementRequest: + UpdateAddress: type: object - required: - - status properties: - status: + country: allOf: - - $ref: '#/components/schemas/BillingCreditPurchasePaymentSettlementStatus' - description: The new payment settlement status. + - $ref: '#/components/schemas/CountryCode' + description: |- + Country code in [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) + alpha-2 format. + title: Country + postal_code: + type: string + description: Postal code. + title: Postal Code + state: + type: string + description: State or province. + title: State + city: + type: string + description: City. + title: City + line1: + type: string + description: First line of the address. + title: Line 1 + line2: + type: string + description: Second line of the address. + title: Line 2 + phone_number: + type: string + description: Phone number. + title: Phone Number additionalProperties: false - description: |- - Request body for updating the external payment settlement status of a credit - grant. - UpdateFeatureRequest: + description: Address + UpdateBillingInvoiceWorkflow: type: object properties: - unit_cost: + invoicing: allOf: - - $ref: '#/components/schemas/BillingFeatureUnitCost' - nullable: true - description: |- - Optional per-unit cost configuration. Use "manual" for a fixed per-unit cost, or - "llm" to look up cost from the LLM cost database based on meter group-by - properties. Set to `null` to clear the existing unit cost; omit to leave it - unchanged. - title: Unit cost + - $ref: '#/components/schemas/UpdateBillingInvoiceWorkflowInvoicingSettings' + description: Invoicing settings for this invoice. + title: Invoicing settings + payment: + allOf: + - $ref: '#/components/schemas/UpdateBillingWorkflowPaymentSettings' + description: Payment settings for this invoice. + title: Payment settings additionalProperties: false description: |- - Request body for updating a feature. Currently only the unit_cost field can be - updated. - UpdateMeterRequest: + Invoice-level snapshot of the workflow configuration. + + Contains only the settings that are meaningful for an already-created invoice: + invoicing behaviour and payment settings. Collection alignment and tax policy + are gather-time / profile-wide concerns and are not included. + UpdateBillingInvoiceWorkflowInvoicingSettings: type: object properties: - name: + auto_advance: + type: boolean + description: Whether to automatically issue the invoice after the draft_period has passed. + default: true + draft_period: type: string - minLength: 1 - maxLength: 256 - description: |- - Display name of the resource. + format: ISO8601 + description: The period for the invoice to be kept in draft status for manual reviews. + example: P1D + default: P0D + additionalProperties: false + description: |- + Invoice-level invoicing settings. - Between 1 and 256 characters. - description: + A subset of BillingWorkflowInvoicingSettings limited to fields that are + meaningful per-invoice. progressive_billing is omitted as it is a gather-time / + profile-level decision. + UpdateBillingPartyAddresses: + type: object + required: + - billing_address + properties: + billing_address: + allOf: + - $ref: '#/components/schemas/UpdateAddress' + description: Billing address. + additionalProperties: false + description: A collection of addresses for the party. + UpdateBillingPartyTaxIdentity: + type: object + properties: + code: + allOf: + - $ref: '#/components/schemas/BillingTaxIdentificationCode' + description: Normalized tax identification code shown on the original identity document. + additionalProperties: false + description: |- + Identity stores the details required to identify an entity for tax purposes in a + specific country. + UpdateBillingWorkflowPaymentChargeAutomaticallySettings: + type: object + required: + - collection_method + properties: + collection_method: type: string - maxLength: 1024 - description: |- - Optional description of the resource. - - Maximum 1024 characters. - labels: - $ref: '#/components/schemas/Labels' - dimensions: - type: object - additionalProperties: - type: string + enum: + - charge_automatically + description: The collection method for the invoice. + additionalProperties: false + description: |- + Payment settings for a billing workflow when the collection method is charge + automatically. + UpdateBillingWorkflowPaymentSendInvoiceSettings: + type: object + required: + - collection_method + properties: + collection_method: + type: string + enum: + - send_invoice + description: The collection method for the invoice. + due_after: + type: string + format: ISO8601 description: |- - Named JSONPath expressions to extract the group by values from the event data. - - Keys must be unique and consist only alphanumeric and underscore characters. - example: - type: $.type + The period after which the invoice is due. With some payment solutions it's only + applicable for manual collection method. + example: P30D + default: P30D additionalProperties: false - description: Meter update request. - UpdateOrganizationDefaultTaxCodesRequest: + description: |- + Payment settings for a billing workflow when the collection method is send + invoice. + UpdateBillingWorkflowPaymentSettings: + type: object + oneOf: + - $ref: '#/components/schemas/UpdateBillingWorkflowPaymentChargeAutomaticallySettings' + - $ref: '#/components/schemas/UpdateBillingWorkflowPaymentSendInvoiceSettings' + discriminator: + propertyName: collection_method + mapping: + charge_automatically: '#/components/schemas/UpdateBillingWorkflowPaymentChargeAutomaticallySettings' + send_invoice: '#/components/schemas/UpdateBillingWorkflowPaymentSendInvoiceSettings' + description: Payment settings for a billing workflow. + UpdateClosedPeriod: type: object + required: + - from + - to properties: - invoicing_tax_code: + from: allOf: - - $ref: '#/components/schemas/TaxCodeReference' - description: Default tax code for invoicing. - title: Invoicing tax code - credit_grant_tax_code: + - $ref: '#/components/schemas/DateTime' + description: |- + The start of the period. + + The period is inclusive at the start. + title: Start + example: '2023-01-01T01:01:01.001Z' + to: allOf: - - $ref: '#/components/schemas/TaxCodeReference' - description: Default tax code for credit grants. + - $ref: '#/components/schemas/DateTime' + description: |- + The end of the period. + + The period is exclusive at the end. + title: End + example: '2023-01-01T01:01:01.001Z' + additionalProperties: false + description: |- + A period with defined start and end dates. + + The period is always inclusive at the start and exclusive at the end. + UpdateCreditGrantExternalSettlementRequest: + type: object + required: + - status + properties: + status: + allOf: + - $ref: '#/components/schemas/BillingCreditPurchasePaymentSettlementStatus' + description: The new payment settlement status. + additionalProperties: false + description: |- + Request body for updating the external payment settlement status of a credit + grant. + UpdateCustomerUsageAttribution: + type: object + required: + - subject_keys + properties: + subject_keys: + type: array + items: + $ref: '#/components/schemas/UsageAttributionSubjectKey' + minItems: 0 + description: |- + The subjects that are attributed to the customer. Can be empty when no usage + event subjects are associated with the customer. + title: Subject Keys + additionalProperties: false + description: |- + Mapping to attribute metered usage to the customer. One customer can have zero + or more subjects, but one subject can only belong to one customer. + UpdateDiscounts: + type: object + properties: + percentage: + type: number + minimum: 0 + maximum: 100 + description: Percentage discount applied to the price (0–100). + usage: + allOf: + - $ref: '#/components/schemas/Numeric' + description: |- + Number of usage units granted free before billing starts. Only applies to + usage-based lines (not flat fees). Usage is treated as zero until this amount is + exhausted. + additionalProperties: false + description: Discount configuration for a rate card. + UpdateFeatureRequest: + type: object + properties: + unit_cost: + allOf: + - $ref: '#/components/schemas/BillingFeatureUnitCost' + nullable: true + description: |- + Optional per-unit cost configuration. Use "manual" for a fixed per-unit cost, or + "llm" to look up cost from the LLM cost database based on meter group-by + properties. Set to `null` to clear the existing unit cost; omit to leave it + unchanged. + title: Unit cost + additionalProperties: false + description: |- + Request body for updating a feature. Currently only the unit_cost field can be + updated. + UpdateInvoiceCustomer: + type: object + required: + - name + - id + properties: + name: + type: string + minLength: 1 + maxLength: 256 + description: |- + Display name of the resource. + + Between 1 and 256 characters. + usage_attribution: + allOf: + - $ref: '#/components/schemas/UpdateCustomerUsageAttribution' + description: Mapping to attribute metered usage to the customer by the event subject. + title: Usage Attribution + billing_address: + allOf: + - $ref: '#/components/schemas/UpdateAddress' + description: The billing address of the customer. Used for tax and invoicing. + title: Billing Address + id: + $ref: '#/components/schemas/ULID' + key: + allOf: + - $ref: '#/components/schemas/ExternalResourceKey' + description: |- + Optional external resource key for the customer. + + Omitted when the customer was created without a key. Unlike on the customer + resource itself, the key is optional here because the invoice snapshot may + predate or omit it. + additionalProperties: false + description: Snapshot of the customer's information at the time the invoice was issued. + UpdateInvoiceLine: + type: object + oneOf: + - $ref: '#/components/schemas/UpdateInvoiceStandardLine' + discriminator: + propertyName: type + mapping: + standard_line: '#/components/schemas/UpdateInvoiceStandardLine' + description: |- + A top-level line item on an invoice. + + Each line represents a single charge, typically associated with a rate card from + a subscription. Detailed (child) lines are nested under `detailed_lines` when + present. + UpdateInvoiceLineRateCard: + type: object + required: + - price + properties: + price: + allOf: + - $ref: '#/components/schemas/UpdatePrice' + description: The price definition used to calculate charges for this line. + title: Price + tax_config: + allOf: + - $ref: '#/components/schemas/UpdateRateCardTaxConfig' + description: Tax configuration snapshot for this line. + title: Tax config + feature_key: + allOf: + - $ref: '#/components/schemas/ResourceKey' + description: The feature key associated with this line's rate card. + title: Feature key + discounts: + allOf: + - $ref: '#/components/schemas/UpdateDiscounts' + description: Discount configuration from the rate card. + title: Discounts + additionalProperties: false + description: Rate card configuration snapshot for a usage-based invoice line. + UpdateInvoiceRequest: + type: object + oneOf: + - $ref: '#/components/schemas/UpdateInvoiceStandardRequest' + discriminator: + propertyName: type + mapping: + standard: '#/components/schemas/UpdateInvoiceStandardRequest' + description: UpdateInvoiceRequest update request. + UpdateInvoiceStandardLine: + type: object + required: + - name + - type + - service_period + - rate_card + properties: + name: + type: string + minLength: 1 + maxLength: 256 + description: |- + Display name of the resource. + + Between 1 and 256 characters. + description: + type: string + maxLength: 1024 + description: |- + Optional description of the resource. + + Maximum 1024 characters. + labels: + $ref: '#/components/schemas/Labels' + id: + allOf: + - $ref: '#/components/schemas/ULID' + description: |- + ID of the line. + + Optional on update: omit to create a new line, or supply the ID of an existing + line to edit it. Existing lines omitted from an update's `lines` array are + deleted. + title: ID + type: + type: string + enum: + - standard_line + description: The type of charge this line item represents. + title: Type + service_period: + allOf: + - $ref: '#/components/schemas/UpdateClosedPeriod' + description: |- + The service period covered by this invoice, spanning the earliest line start to + the latest line end across all of its lines. + + For an invoice with no lines the period is empty, which means `from` will be + equal to `to`. + title: Service period + rate_card: + allOf: + - $ref: '#/components/schemas/UpdateInvoiceLineRateCard' + description: The rate card configuration snapshot used to price this line item. + title: Rate card + additionalProperties: false + description: |- + A top-level line item on an invoice. + + Each line represents a single charge, typically associated with a rate card from + a subscription. Detailed (child) lines are nested under `detailed_lines` when + present. + UpdateInvoiceStandardRequest: + type: object + required: + - supplier + - customer + - type + - workflow + properties: + description: + type: string + maxLength: 1024 + description: |- + Optional description of the resource. + + Maximum 1024 characters. + labels: + $ref: '#/components/schemas/Labels' + supplier: + allOf: + - $ref: '#/components/schemas/UpdateSupplier' + description: |- + Snapshot of the supplier's contact information at the time the invoice was + issued. + title: Supplier + customer: + allOf: + - $ref: '#/components/schemas/UpdateInvoiceCustomer' + description: Snapshot of the customer's information at the time the invoice was issued. + title: Customer + type: + type: string + enum: + - standard + description: Discriminator field identifying this as a standard invoice. + title: Type + workflow: + allOf: + - $ref: '#/components/schemas/UpdateInvoiceWorkflowSettings' + description: Workflow configuration snapshot captured at invoice creation time. + title: Workflow + lines: + type: array + items: + $ref: '#/components/schemas/UpdateInvoiceLine' + description: |- + Line items on this invoice. + + Always returned on single-resource GET; omitted on list endpoints unless + explicitly expanded. Editable via update: existing lines are matched by `id`, + lines without an `id` are created, and lines present on the invoice but omitted + from the update request are deleted. Detailed (child) lines are always computed + and cannot be edited directly. + title: Lines + additionalProperties: false + description: InvoiceStandard update request. + UpdateInvoiceWorkflowSettings: + type: object + required: + - workflow + properties: + workflow: + allOf: + - $ref: '#/components/schemas/UpdateBillingInvoiceWorkflow' + description: |- + The workflow configuration that was active when the invoice was created. + + Only the fields that are meaningful at the per-invoice level are included: + invoicing behaviour (auto-advance, draft period) and payment settings + (collection method, due date). Profile-wide settings such as collection + alignment, progressive billing, and tax policy are omitted. + title: Workflow config + additionalProperties: false + description: Snapshot of the billing workflow configuration captured at invoice creation. + UpdateMeterRequest: + type: object + properties: + name: + type: string + minLength: 1 + maxLength: 256 + description: |- + Display name of the resource. + + Between 1 and 256 characters. + description: + type: string + maxLength: 1024 + description: |- + Optional description of the resource. + + Maximum 1024 characters. + labels: + $ref: '#/components/schemas/Labels' + dimensions: + type: object + additionalProperties: + type: string + description: |- + Named JSONPath expressions to extract the group by values from the event data. + + Keys must be unique and consist only alphanumeric and underscore characters. + example: + type: $.type + additionalProperties: false + description: Meter update request. + UpdateOrganizationDefaultTaxCodesRequest: + type: object + properties: + invoicing_tax_code: + allOf: + - $ref: '#/components/schemas/TaxCodeReference' + description: Default tax code for invoicing. + title: Invoicing tax code + credit_grant_tax_code: + allOf: + - $ref: '#/components/schemas/TaxCodeReference' + description: Default tax code for credit grants. title: Credit grant tax code additionalProperties: false description: OrganizationDefaultTaxCodes update request. + UpdatePrice: + type: object + oneOf: + - $ref: '#/components/schemas/UpdatePriceFree' + - $ref: '#/components/schemas/UpdatePriceFlat' + - $ref: '#/components/schemas/UpdatePriceUnit' + - $ref: '#/components/schemas/UpdatePriceGraduated' + - $ref: '#/components/schemas/UpdatePriceVolume' + discriminator: + propertyName: type + mapping: + free: '#/components/schemas/UpdatePriceFree' + flat: '#/components/schemas/UpdatePriceFlat' + unit: '#/components/schemas/UpdatePriceUnit' + graduated: '#/components/schemas/UpdatePriceGraduated' + volume: '#/components/schemas/UpdatePriceVolume' + description: Price. + UpdatePriceFlat: + type: object + required: + - type + - amount + properties: + type: + type: string + enum: + - flat + description: The type of the price. + title: Type + amount: + allOf: + - $ref: '#/components/schemas/Numeric' + description: The amount of the flat price. + title: Amount + additionalProperties: false + description: Flat price. + UpdatePriceFree: + type: object + required: + - type + properties: + type: + type: string + enum: + - free + description: The type of the price. + title: Type + additionalProperties: false + description: Free price. + UpdatePriceGraduated: + type: object + required: + - type + - tiers + properties: + type: + type: string + enum: + - graduated + description: The type of the price. + title: Type + tiers: + type: array + items: + $ref: '#/components/schemas/UpdatePriceTier' + minItems: 1 + description: The tiers of the graduated price. At least one tier is required. + title: Tiers + additionalProperties: false + description: |- + Graduated tiered price. + + Each tier's rate applies only to the usage within that tier. Pricing can change + as cumulative usage crosses tier boundaries. + + When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are + expressed in converted billing units. + UpdatePriceTier: + type: object + properties: + up_to_amount: + allOf: + - $ref: '#/components/schemas/Numeric' + description: |- + Up to and including this quantity will be contained in the tier. If undefined, + the tier is open-ended (the last tier). + title: Up to quantity + flat_price: + allOf: + - $ref: '#/components/schemas/UpdatePriceFlat' + description: The flat price component of the tier. Charged once when the tier is entered. + title: Flat price component + unit_price: + allOf: + - $ref: '#/components/schemas/UpdatePriceUnit' + description: The unit price component of the tier. Charged per billing unit within the tier. + title: Unit price component + additionalProperties: false + description: |- + A price tier used in graduated and volume pricing. + + At least one price component (flat_price or unit_price) must be set. When + UnitConfig is present on the rate card, up_to_amount is expressed in converted + billing units. + UpdatePriceUnit: + type: object + required: + - type + - amount + properties: + type: + type: string + enum: + - unit + description: The type of the price. + title: Type + amount: + allOf: + - $ref: '#/components/schemas/Numeric' + description: The amount of the unit price. + title: Amount + additionalProperties: false + description: |- + Unit price. + + Charges a fixed rate per billing unit. When UnitConfig is present on the rate + card, billing units are the converted quantities (e.g. GB instead of bytes). + UpdatePriceVolume: + type: object + required: + - type + - tiers + properties: + type: + type: string + enum: + - volume + description: The type of the price. + title: Type + tiers: + type: array + items: + $ref: '#/components/schemas/UpdatePriceTier' + minItems: 1 + description: The tiers of the volume price. At least one tier is required. + title: Tiers + additionalProperties: false + description: |- + Volume tiered price. + + The maximum quantity within a period determines the per-unit price for all units + in that period. + + When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are + expressed in converted billing units. + UpdateRateCardTaxConfig: + type: object + required: + - code + properties: + behavior: + $ref: '#/components/schemas/BillingTaxBehavior' + code: + $ref: '#/components/schemas/UpdateResourceReference' + additionalProperties: false + description: The tax config of the rate card. + UpdateResourceReference: + type: object + required: + - id + properties: + id: + $ref: '#/components/schemas/ULID' + additionalProperties: false + description: TaxCode reference. + UpdateSupplier: + type: object + properties: + name: + type: string + description: Legal name or representation of the party. + tax_id: + allOf: + - $ref: '#/components/schemas/UpdateBillingPartyTaxIdentity' + description: |- + The entity's legal identification used for tax purposes. They may have other + numbers, but we're only interested in those valid for tax purposes. + addresses: + allOf: + - $ref: '#/components/schemas/UpdateBillingPartyAddresses' + description: Address for where information should be sent if needed. + id: + type: string + description: Unique identifier for the party. + additionalProperties: false + description: |- + Snapshot of the supplier's information at the time the invoice was issued. + + Structurally a read-only subset of `BillingParty` (the type configured on the + billing profile), so the snapshot stays aligned with the source. `key` is + omitted because it is not part of the snapshotted supplier data. UpsertAddonRequest: type: object required: diff --git a/api/v3/server/routes.go b/api/v3/server/routes.go index 6cbfa5b39c..ca2f24db4a 100644 --- a/api/v3/server/routes.go +++ b/api/v3/server/routes.go @@ -181,6 +181,10 @@ func (s *Server) GetInvoice(w http.ResponseWriter, r *http.Request, invoiceId ap s.billingInvoicesHandler.GetBillingInvoice().With(invoiceId).ServeHTTP(w, r) } +func (s *Server) UpdateInvoice(w http.ResponseWriter, r *http.Request, invoiceId api.ULID) { + s.billingInvoicesHandler.UpdateBillingInvoice().With(invoiceId).ServeHTTP(w, r) +} + // Customer Billing func (s *Server) GetCustomerBilling(w http.ResponseWriter, r *http.Request, customerId api.ULID) { diff --git a/e2e/billinginvoices_v3_test.go b/e2e/billinginvoices_v3_test.go index f527c871ac..ac18b0c320 100644 --- a/e2e/billinginvoices_v3_test.go +++ b/e2e/billinginvoices_v3_test.go @@ -258,10 +258,6 @@ func formatInvoicesForLog(invoices []api.Invoice) string { return formatLogJSON(invoices) } -func formatInvoiceLinesForLog(lines []api.InvoiceLine) string { - return formatLogJSON(lines) -} - func formatLogJSON(v any) string { out, err := json.Marshal(v) if err != nil { @@ -522,3 +518,502 @@ func TestV3ListBillingInvoices(t *testing.T) { assert.GreaterOrEqual(t, resp.Meta.Page.Total, float32(1)) }) } + +// TestV3UpdateBillingInvoice exercises PUT /api/v3/openmeter/billing/invoices/{invoiceId}. +// +// Flow: +// - Create a customer (v3) +// - Pin the customer to a manual-approval billing profile (auto_advance=false, +// send_invoice) so the standard invoice it produces stays in draft status; +// only draft invoices can be updated via v3, and the default profile's +// auto-collect settings otherwise advance the invoice to paid too quickly +// for the update test to observe a draft invoice. +// - Create a meter, feature, plan, and subscription (v3) +// - Create a standard invoice (v1) +// - Create a gathering invoice (v1) for the gathering-invoice-update case +// - Update the standard invoice via v3 PUT and assert the response shape +// - Update the standard invoice with an invalid type and assert 400 +// - Update a gathering invoice via v3 PUT and assert 404 +// - Update an unknown invoice ID via v3 PUT and assert 404 +// - Update a standard invoice with invalid data (e.g., negative amount) and assert 400 +// - Update a standard invoice with a valid change and assert the change is reflected in the response +func TestV3UpdateBillingInvoice(t *testing.T) { + c := newV3Client(t) + v1 := initClient(t) + + var ( + customerID string + planID string + feature *apiv3.Feature + invoice api.Invoice + gatheringInvoiceID string + ) + + t.Run("Should create a customer", func(t *testing.T) { + key := uniqueKey("listinv_customer") + currency := apiv3.CurrencyCode("USD") + + status, customer, problem := c.CreateCustomer(apiv3.CreateCustomerRequest{ + Key: key, + Name: gofakeit.ProductName(), + Currency: ¤cy, + }) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + require.NotNil(t, customer) + customerID = customer.Id + }) + + t.Run("Should pin the customer to a manual-approval billing profile", func(t *testing.T) { + require.NotEmpty(t, customerID, "depends on customer creation") + + profile := createNewBillingProfileFromDefault(t, c, uniqueKey("update_invoice"), func(profile *apiv3.CreateBillingProfileRequest) { + if profile.Workflow.Invoicing == nil { + profile.Workflow.Invoicing = &apiv3.BillingWorkflowInvoicingSettings{} + } + profile.Workflow.Invoicing.AutoAdvance = lo.ToPtr(false) + + sendInvoice := apiv3.BillingWorkflowPaymentSettings{} + require.NoError(t, sendInvoice.FromBillingWorkflowPaymentSendInvoiceSettings(apiv3.BillingWorkflowPaymentSendInvoiceSettings{ + CollectionMethod: apiv3.BillingWorkflowPaymentSendInvoiceSettingsCollectionMethodSendInvoice, + })) + profile.Workflow.Payment = &sendInvoice + }) + + status, _, problem := c.UpdateCustomerBilling(customerID, apiv3.UpsertCustomerBillingDataRequest{ + BillingProfile: &apiv3.BillingProfileReference{Id: profile.Id}, + }) + require.Equal(t, http.StatusOK, status, "problem: %+v", problem) + }) + + t.Run("Should create a single gathering invoice", func(t *testing.T) { + require.NotEmpty(t, customerID, "depends on customer creation") + + now := time.Now().UTC() + price := api.RateCardUsageBasedPrice{} + require.NoError(t, price.FromFlatPriceWithPaymentTerm(api.FlatPriceWithPaymentTerm{ + Amount: api.Numeric("10.00"), + Type: api.FlatPriceWithPaymentTermTypeFlat, + PaymentTerm: lo.ToPtr(api.PricePaymentTermInAdvance), + })) + + invoiceAt := now.Add(time.Hour) + lineResp, err := v1.CreatePendingInvoiceLineWithResponse(t.Context(), customerID, api.InvoicePendingLineCreateInput{ + Currency: "USD", + Lines: []api.InvoicePendingLineCreate{ + { + Name: uniqueKey("update_inv_gathering_line"), + InvoiceAt: invoiceAt, + Period: api.Period{ + From: now.Add(-24 * time.Hour), + To: invoiceAt, + }, + Price: &price, + }, + }, + }) + require.NoError(t, err) + require.Equal(t, http.StatusCreated, lineResp.StatusCode(), "line: %s", string(lineResp.Body)) + require.NotNil(t, lineResp.JSON201) + + gatheringInvoiceID = (*lineResp.JSON201).Invoice.Id + require.NotEmpty(t, gatheringInvoiceID) + }) + + t.Run("Should create meter, feature, plan, and subscription", func(t *testing.T) { + require.NotEmpty(t, customerID, "depends on customer creation") + + status, meter, problem := c.CreateMeter(apiv3.CreateMeterRequest{ + Key: uniqueKey("listinv_meter"), + Name: gofakeit.ProductName(), + Aggregation: apiv3.MeterAggregationCount, + EventType: uniqueKey("listinv_event"), + }) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + + status, f, problem := c.CreateFeature(apiv3.CreateFeatureRequest{ + Key: uniqueKey("listinv_feature"), + Name: gofakeit.ProductName(), + Meter: &apiv3.FeatureMeterReference{Id: meter.Id}, + }) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + feature = f + + status, plan, problem := c.CreatePlan(apiv3.CreatePlanRequest{ + Key: uniqueKey("listinv_plan"), + Name: gofakeit.ProductName(), + Currency: "USD", + BillingCadence: apiv3.ISO8601Duration("P1M"), + Phases: []apiv3.BillingPlanPhase{{ + Key: uniqueKey("inv_phase_1"), + Name: uniqueKey("Test Phase"), + RateCards: []apiv3.BillingRateCard{validUnitRateCard(*feature)}, + }}, + }) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + planID = plan.Id + + status, plan, problem = c.PublishPlan(planID) + require.Equal(t, http.StatusOK, status, "problem: %+v", problem) + assert.Equal(t, apiv3.BillingPlanStatusActive, plan.Status) + + status, _, problem = c.CreateSubscription(apiv3.BillingSubscriptionCreate{ + Customer: struct { + Id *apiv3.ULID `json:"id,omitempty"` + Key *apiv3.ExternalResourceKey `json:"key,omitempty"` + }{Id: lo.ToPtr(customerID)}, + Plan: struct { + Id *apiv3.ULID `json:"id,omitempty"` + Key *apiv3.ResourceKey `json:"key,omitempty"` + Version *int `json:"version,omitempty"` + }{Id: lo.ToPtr(planID)}, + }) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + }) + + t.Run("Should create a standard invoice and wait for it to advance", func(t *testing.T) { + require.NotEmpty(t, customerID) + require.NotNil(t, feature) + + now := time.Now().UTC() + price := api.RateCardUsageBasedPrice{} + require.NoError(t, price.FromFlatPriceWithPaymentTerm(api.FlatPriceWithPaymentTerm{ + Amount: api.Numeric("10.00"), + Type: api.FlatPriceWithPaymentTermTypeFlat, + PaymentTerm: lo.ToPtr(api.PricePaymentTermInAdvance), + })) + + lineResp, err := v1.CreatePendingInvoiceLineWithResponse(t.Context(), customerID, api.InvoicePendingLineCreateInput{ + Currency: "USD", + Lines: []api.InvoicePendingLineCreate{{ + Name: uniqueKey("listinv_line"), + InvoiceAt: now.Add(-10 * time.Hour), + Period: api.Period{ + From: now.Add(-24 * time.Hour), + To: now.Add(-2 * time.Hour), + }, + Price: &price, + }}, + }) + require.NoError(t, err) + require.Equal(t, http.StatusCreated, lineResp.StatusCode()) + + ctx := t.Context() + customers := api.InvoiceListParamsCustomers{customerID} + assert.EventuallyWithT(t, func(co *assert.CollectT) { + listResp, err := v1.ListInvoicesWithResponse(ctx, &api.ListInvoicesParams{ + Customers: &customers, + PageSize: lo.ToPtr(api.PaginationPageSize(100)), + }) + require.NoError(co, err) + require.Equal(co, http.StatusOK, listResp.StatusCode()) + + idx := slices.IndexFunc(listResp.JSON200.Items, func(inv api.Invoice) bool { + return inv.Status != api.InvoiceStatusGathering + }) + require.NotEqual(co, -1, idx, "charges have not advanced a pending line into a standard invoice yet") + invoice = listResp.JSON200.Items[idx] + }, time.Minute, time.Second) + require.NotEmpty(t, invoice) + }) + + t.Run("Should update the standard invoice via v3 PUT", func(t *testing.T) { + require.NotEmpty(t, invoice) + + newDescription := gofakeit.LoremIpsumSentence(5) + + var updateReq apiv3.UpdateInvoiceRequest + require.NoError(t, updateReq.FromUpdateInvoiceStandardRequest(apiv3.UpdateInvoiceStandardRequest{ + Type: apiv3.UpdateInvoiceStandardRequestTypeStandard, + Description: lo.ToPtr(newDescription), + Customer: apiv3.UpdateInvoiceCustomer{ + Id: *invoice.Customer.Id, + Name: *invoice.Customer.Name, + }, + Supplier: updateSupplierFromInvoiceParty(invoice.Supplier), + Workflow: apiv3.UpdateInvoiceWorkflowSettings{}, + Lines: nil, + })) + + status, updatedInv, problem := c.UpdateBillingInvoice(invoice.Id, updateReq) + require.Equal(t, http.StatusOK, status, "problem: %+v", problem) + require.NotNil(t, updatedInv) + + stdInv, err := updatedInv.AsBillingInvoiceStandard() + require.NoError(t, err) + assert.Equal(t, invoice.Id, stdInv.Id) + assert.Equal(t, newDescription, lo.FromPtr(stdInv.Description)) + }) + + t.Run("Should reject an update with an unsupported invoice type", func(t *testing.T) { + require.NotEmpty(t, invoice) + + body := map[string]any{ + "type": "unsupported_invoice_type", + "customer": map[string]any{ + "id": *invoice.Customer.Id, + "name": *invoice.Customer.Name, + }, + "supplier": map[string]any{}, + "workflow": map[string]any{"workflow": map[string]any{}}, + } + + status, raw, problem := c.do(http.MethodPut, "/billing/invoices/"+invoice.Id, body) + assert.Equal(t, http.StatusBadRequest, status, "body: %s", string(raw)) + assert.NotNil(t, problem) + }) + + t.Run("Should return 404 when updating a gathering invoice via v3 PUT", func(t *testing.T) { + require.NotEmpty(t, gatheringInvoiceID, "depends on gathering invoice creation") + + var updateReq apiv3.UpdateInvoiceRequest + require.NoError(t, updateReq.FromUpdateInvoiceStandardRequest(apiv3.UpdateInvoiceStandardRequest{ + Type: apiv3.UpdateInvoiceStandardRequestTypeStandard, + Customer: apiv3.UpdateInvoiceCustomer{ + Id: customerID, + Name: "placeholder", + }, + Supplier: apiv3.UpdateSupplier{}, + Workflow: apiv3.UpdateInvoiceWorkflowSettings{}, + })) + + status, updatedInv, problem := c.UpdateBillingInvoice(gatheringInvoiceID, updateReq) + assert.Equal(t, http.StatusNotFound, status, "problem: %+v", problem) + assert.Nil(t, updatedInv) + assert.NotNil(t, problem) + }) + + t.Run("Should return 404 for an unknown invoice ID via v3 PUT", func(t *testing.T) { + var updateReq apiv3.UpdateInvoiceRequest + require.NoError(t, updateReq.FromUpdateInvoiceStandardRequest(apiv3.UpdateInvoiceStandardRequest{ + Type: apiv3.UpdateInvoiceStandardRequestTypeStandard, + Customer: apiv3.UpdateInvoiceCustomer{ + Id: customerID, + Name: "placeholder", + }, + Supplier: apiv3.UpdateSupplier{}, + Workflow: apiv3.UpdateInvoiceWorkflowSettings{}, + })) + + status, updatedInv, problem := c.UpdateBillingInvoice("01JAAAAAAAAAAAAAAAAAAAAAAA", updateReq) + assert.Equal(t, http.StatusNotFound, status, "problem: %+v", problem) + assert.Nil(t, updatedInv) + assert.NotNil(t, problem) + }) + + t.Run("Should reject a line update with a negative amount", func(t *testing.T) { + require.NotEmpty(t, invoice) + + status, currentInv, problem := c.GetBillingInvoice(invoice.Id) + require.Equal(t, http.StatusOK, status, "problem: %+v", problem) + require.NotNil(t, currentInv) + stdInv, err := currentInv.AsBillingInvoiceStandard() + require.NoError(t, err) + + lines := existingUpdateLines(t, stdInv) + + var negativeLine apiv3.UpdateInvoiceLine + require.NoError(t, negativeLine.FromUpdateInvoiceStandardLine(newFlatUpdateLine(t, uniqueKey("negative_amount_line"), stdInv.ServicePeriod, "-5"))) + lines = append(lines, negativeLine) + + var updateReq apiv3.UpdateInvoiceRequest + require.NoError(t, updateReq.FromUpdateInvoiceStandardRequest(apiv3.UpdateInvoiceStandardRequest{ + Type: apiv3.UpdateInvoiceStandardRequestTypeStandard, + Customer: apiv3.UpdateInvoiceCustomer{ + Id: *invoice.Customer.Id, + Name: *invoice.Customer.Name, + }, + Supplier: updateSupplierFromInvoiceParty(invoice.Supplier), + Workflow: apiv3.UpdateInvoiceWorkflowSettings{}, + Lines: &lines, + })) + + status, updatedInv, problem := c.UpdateBillingInvoice(invoice.Id, updateReq) + require.Equal(t, http.StatusBadRequest, status, "problem: %+v", problem) + assert.Nil(t, updatedInv) + assertProblemDetail(t, problem, "must not be negative") + }) + + t.Run("Should apply a valid line addition and reflect it in the response", func(t *testing.T) { + require.NotEmpty(t, invoice) + + status, currentInv, problem := c.GetBillingInvoice(invoice.Id) + require.Equal(t, http.StatusOK, status, "problem: %+v", problem) + require.NotNil(t, currentInv) + stdInv, err := currentInv.AsBillingInvoiceStandard() + require.NoError(t, err) + + lines := existingUpdateLines(t, stdInv) + + newLineName := uniqueKey("valid_new_line") + var newLine apiv3.UpdateInvoiceLine + require.NoError(t, newLine.FromUpdateInvoiceStandardLine(newFlatUpdateLine(t, newLineName, stdInv.ServicePeriod, "15"))) + lines = append(lines, newLine) + + var updateReq apiv3.UpdateInvoiceRequest + require.NoError(t, updateReq.FromUpdateInvoiceStandardRequest(apiv3.UpdateInvoiceStandardRequest{ + Type: apiv3.UpdateInvoiceStandardRequestTypeStandard, + Customer: apiv3.UpdateInvoiceCustomer{ + Id: *invoice.Customer.Id, + Name: *invoice.Customer.Name, + }, + Supplier: updateSupplierFromInvoiceParty(invoice.Supplier), + Workflow: apiv3.UpdateInvoiceWorkflowSettings{}, + Lines: &lines, + })) + + status, updatedInv, problem := c.UpdateBillingInvoice(invoice.Id, updateReq) + require.Equal(t, http.StatusOK, status, "problem: %+v", problem) + require.NotNil(t, updatedInv) + + updatedStdInv, err := updatedInv.AsBillingInvoiceStandard() + require.NoError(t, err) + require.NotNil(t, updatedStdInv.Lines) + + found := false + for _, l := range *updatedStdInv.Lines { + stdLine, err := l.AsBillingInvoiceStandardLine() + require.NoError(t, err) + if stdLine.Name == newLineName { + found = true + p, err := stdLine.RateCard.Price.AsBillingPriceFlat() + require.NoError(t, err) + assert.Equal(t, "15", p.Amount) + } + } + assert.True(t, found, "expected the newly added line %q to appear in the updated invoice", newLineName) + }) +} + +// updateSupplierFromInvoiceParty maps a v1 BillingParty (the invoice's supplier +// snapshot) into an UpdateSupplier that round-trips its current name and +// address. Update requests replace the full supplier object: an omitted name +// or address is not "leave unchanged", it's reset to empty, which then fails +// SupplierContact.Validate() (name and country are required for invoicing). +func updateSupplierFromInvoiceParty(party api.BillingParty) apiv3.UpdateSupplier { + supplier := apiv3.UpdateSupplier{ + Id: party.Id, + Name: party.Name, + } + + if party.Addresses != nil && len(*party.Addresses) > 0 { + addr := (*party.Addresses)[0] + supplier.Addresses = &apiv3.UpdateBillingPartyAddresses{ + BillingAddress: apiv3.UpdateAddress{ + City: addr.City, + Country: addr.Country, + Line1: addr.Line1, + Line2: addr.Line2, + PhoneNumber: addr.PhoneNumber, + PostalCode: addr.PostalCode, + State: addr.State, + }, + } + } + + return supplier +} + +// existingUpdateLines maps a standard invoice's current lines back into +// UpdateInvoiceLine values (preserving IDs and content), so a follow-up PUT +// can append or modify lines without deleting the ones it doesn't mention. +// Update requests replace the full `lines` array: any existing line whose ID +// is omitted from the request is deleted. +func existingUpdateLines(t *testing.T, stdInv apiv3.BillingInvoiceStandard) []apiv3.UpdateInvoiceLine { + t.Helper() + + if stdInv.Lines == nil { + return nil + } + + lines := make([]apiv3.UpdateInvoiceLine, 0, len(*stdInv.Lines)) + for _, l := range *stdInv.Lines { + stdLine, err := l.AsBillingInvoiceStandardLine() + require.NoError(t, err) + lines = append(lines, updateLineFromStandardLine(t, stdLine)) + } + + return lines +} + +// updateLineFromStandardLine maps a read-side standard line back into an +// UpdateInvoiceLine, preserving its ID and rate card so that resubmitting it +// unchanged is a no-op rather than an unintended edit or deletion. +func updateLineFromStandardLine(t *testing.T, line apiv3.BillingInvoiceStandardLine) apiv3.UpdateInvoiceLine { + t.Helper() + + rateCard := apiv3.UpdateInvoiceLineRateCard{ + FeatureKey: line.RateCard.FeatureKey, + } + + if line.RateCard.Discounts != nil { + rateCard.Discounts = &apiv3.UpdateDiscounts{ + Percentage: line.RateCard.Discounts.Percentage, + Usage: line.RateCard.Discounts.Usage, + } + } + + if line.RateCard.TaxConfig != nil { + rateCard.TaxConfig = &apiv3.UpdateRateCardTaxConfig{ + Behavior: line.RateCard.TaxConfig.Behavior, + Code: apiv3.UpdateResourceReference{Id: line.RateCard.TaxConfig.Code.Id}, + } + } + + disc, err := line.RateCard.Price.Discriminator() + require.NoError(t, err) + + switch disc { + case string(apiv3.BillingPriceFlatTypeFlat): + p, err := line.RateCard.Price.AsBillingPriceFlat() + require.NoError(t, err) + require.NoError(t, rateCard.Price.FromUpdatePriceFlat(apiv3.UpdatePriceFlat{ + Type: apiv3.UpdatePriceFlatTypeFlat, + Amount: p.Amount, + })) + case string(apiv3.BillingPriceUnitTypeUnit): + p, err := line.RateCard.Price.AsBillingPriceUnit() + require.NoError(t, err) + require.NoError(t, rateCard.Price.FromUpdatePriceUnit(apiv3.UpdatePriceUnit{ + Type: apiv3.UpdatePriceUnitTypeUnit, + Amount: p.Amount, + })) + default: + t.Fatalf("unsupported rate card price type in test fixture: %s", disc) + } + + stdLine := apiv3.UpdateInvoiceStandardLine{ + Id: line.Id, + Name: line.Name, + Description: line.Description, + Labels: line.Labels, + Type: apiv3.UpdateInvoiceStandardLineTypeStandardLine, + ServicePeriod: apiv3.UpdateClosedPeriod{ + From: line.ServicePeriod.From, + To: line.ServicePeriod.To, + }, + RateCard: rateCard, + } + + var out apiv3.UpdateInvoiceLine + require.NoError(t, out.FromUpdateInvoiceStandardLine(stdLine)) + return out +} + +// newFlatUpdateLine builds a brand-new (no ID) flat-priced UpdateInvoiceStandardLine +// for tests that append a manual line to an existing invoice. +func newFlatUpdateLine(t *testing.T, name string, period apiv3.ClosedPeriod, amount string) apiv3.UpdateInvoiceStandardLine { + t.Helper() + + rateCard := apiv3.UpdateInvoiceLineRateCard{} + require.NoError(t, rateCard.Price.FromUpdatePriceFlat(apiv3.UpdatePriceFlat{ + Type: apiv3.UpdatePriceFlatTypeFlat, + Amount: amount, + })) + + return apiv3.UpdateInvoiceStandardLine{ + Name: name, + Type: apiv3.UpdateInvoiceStandardLineTypeStandardLine, + ServicePeriod: apiv3.UpdateClosedPeriod(period), + RateCard: rateCard, + } +} diff --git a/e2e/v3helpers_test.go b/e2e/v3helpers_test.go index b38a05e47f..657058766a 100644 --- a/e2e/v3helpers_test.go +++ b/e2e/v3helpers_test.go @@ -427,6 +427,11 @@ func (c *v3Client) ListBillingInvoices(opts ListBillingInvoicesOptions) (int, *a return decodeTyped[apiv3.InvoicePagePaginatedResponse](c, status, raw, problem, http.StatusOK) } +func (c *v3Client) UpdateBillingInvoice(invoiceID string, body apiv3.UpdateInvoiceRequest) (int, *apiv3.BillingInvoice, *v3Problem) { + status, raw, problem := c.do(http.MethodPut, "/billing/invoices/"+invoiceID, body) + return decodeTyped[apiv3.BillingInvoice](c, status, raw, problem, http.StatusOK) +} + // --- Credits --- // CreateCreditGrant posts a credit grant for the given customer. customerID is diff --git a/pkg/set/set.go b/pkg/set/set.go index 4f1777be0c..86fb988149 100644 --- a/pkg/set/set.go +++ b/pkg/set/set.go @@ -33,6 +33,14 @@ func (s *Set[T]) Remove(items ...T) { } } +func (s *Set[T]) Has(item T) bool { + s.mu.RLock() + defer s.mu.RUnlock() + + _, exists := s.content[item] + return exists +} + func (s *Set[T]) AsSlice() []T { s.mu.RLock() defer s.mu.RUnlock()