diff --git a/openmeter/billing/README.md b/openmeter/billing/README.md new file mode 100644 index 000000000..90b3ffb4b --- /dev/null +++ b/openmeter/billing/README.md @@ -0,0 +1,131 @@ +# Billing + +This package contains the implementation for the billing stack (invoicing, tax and payments). + +The package has the following main entities: + +## BillingProfile + +Captures all the billing details, two main information is stored inside: +- The [billing workflow](./entity/customeroverride.go) (when to invoice, due periods etc) +- References to the apps responsible for tax, invoicing and payments (Sandbox or Stripe for now) + +Only one default billing profile can exist per namespace. + +## CustomerOverride + +Contains customer specific overrides for billing pruposes. It can reference a billing profile other than the default (e.g. when different apps or lifecycle should be used) and allows to override the billing workflow. + +## Invoice + +Invoices are used to store the data required by tax, invoicing and payment app's master copy at OpenMeter side. + +Upon creation all the data required to generate invoices are snapshotted into the invoice entity, so that no updates to entities like Customer, BillingProfile, CustomerOverride change an invoice retrospectively. + +### Gathering invoices + +There are two general kinds of invoices (Invoice.Status) `gathering` invoices are used to collect upcoming lines that are to be added to future invoices. `gathering` invocie's state never changes: when upcoming line items become due, they are just assigned to a new invoice, so that we clone the data required afresh. + +Each customer can have one `gathering` issue per currency. +> For example, if the customer has upcoming charges in USD and HUF, then there will be one `gathering` invoice for HUF and one for USD. + +If there are no upcoming items, the `gathering` invoices are (soft) deleted. + +### Collection + +TODO: document when implemented + +### Invoices + +The invoices are goverened by the [invoice state machine](./service/invoicestate.go). + +Invoices are composed of [lines](./entity/invoiceline.go). Each invoice can only have lines from the same currency. + +The lines can be of different types: +- ManualFee: one time manually added charge +- ManualUsageBased: manually added usage-based charge (can be used to charge addition usage-based prices without the product catalog features) + +Each line has a `period` (`start`, `end`) and an `invoiceAt` property. The period specifies which period of time the line is referring to (in case of usage-based pricing, the underlying meter will be queried for this time-period). `invoiceAt` specifies the time when it is expected to create an invoice that contains this line. The invoice's collection settings can defer this. + +Invoices are always created by collecting one or more line from the `gathering` invoices. The `/v1/api/billing/invoices/lines` endpoint can be used to create new future line items. A new invoice can be created any time. In such case the `gathering` items that are to be invoiced (`invoiceAt`) already are added to the invoice. Any usage-based line, that we can bill early is also added to the invoice for the period between the `period.start` of the line and the time of invoice creation. + +### Line splitting + +To achieve the behavior described above, we are using line splitting. By default we would have one line per billing period that would eventually be part of an invoice: + +``` + period.start period.end +Line1 [status=valid] |--------------------------------------------------------| +``` + +When the usage-based line can be billed mid-period, we `split` the line into two: + +``` + period.start asOf period.end +Line1 [status=split] |--------------------------------------------------------| +SplitLine1 [status=valid] |------------------| +SplitLine2 [status=valid] |-------------------------------------| +``` + +As visible: +- Line1's status changes from `valid` to `split`: it will be ignored in any calculation, it becomes a grouping line between invoices +- SplitLine1 is created with a period between `period.start` and `asof` (time of invoicing): it will be addedd to the freshly created invoice +- SplitLine2 is created with a period between `asof` and `period.end`: it will be pushed to the gathering invoice + +When creating a new invoice between `asof` and `period.end` the same logic continues, but without marking SplitLine2 `split`, instead the new line is added to the original line's parent line: + +``` + period.start asOf1 asof2 period.end +Line1 [status=split] |--------------------------------------------------------| +SplitLine1 [status=valid] |------------------| +SplitLine2 [status=valid] |---------------| +SplitLine3 [status=valid] |---------------------| +``` + +This flattening approach allows us to not to have to recusively traverse lines in the database. + +### Usage-based quantity + +When a line is created for an invoice, the quantity of the underlying merter is captured into the line's qty field. This information is never updated, so late events will have to create new invoice lines when needed. + +### Detailed Lines + +Each (`valid`) line can have one or more detailed lines (children). These lines represent the actual sub-charges that are caused by the parent line. + +Example: +> If a line has: +> - Usage of 200 units +> - Tiered pricing: +> - Tier1: 1 - 50 units cost flat $300 +> - Tier2: 51 - 100 units cost flat $400 +> - Tier3: 100 - 150 units cost flat $400 + $1/unit +> - Tier4: more than 150 units cost $15/unit + +This would yield the following lines: + +- Line with quantity=200 + - Line quantity=1 per_unit_amount=300 total=300 (Tier1) + - Line quantity=1 per_unit_amount=400 total=400 (Tier2) + - Line quantity=1 per_unit_amount=400 total=400 (Tier3, flat component) + - Line quantity=50 per_unit_amount=1 total=50 (Tier3, per unit price) + - Line quantity=50 per_unit_amount=15 total=759 (Tier4) + +Apps can choose to syncronize the original line (if the upstream system understands our pricing model) or can use the sublines to syncronize individual lines without having to understand billing details. + +### Detailed Lines vs Splitting + +TODO: this is TBD as not implemented. + +When we are dealing with a split line, the calculation is by taking the meter's quantity for the whole line period ([`parent.period.start`, `splitline.period.end`]) and the splitline's period (`splitline.period.start`, `splitline.period.end`). + +When substracting the two we get two values: +- line qty: splitline's period +- before line usage: whole line period - splitline's period + +We execute the pricing logic (e.g. tiered pricing) for the line qty, while considering the before usage, as it reflects the already billed for items. + +Corner cases: +- Graduating tiered prices cannot be billed mid billing period (always arrears) +- Min, Avg meters are always billed arrears as they are not composable + + diff --git a/openmeter/billing/adapter.go b/openmeter/billing/adapter.go index d9cce93a6..747136174 100644 --- a/openmeter/billing/adapter.go +++ b/openmeter/billing/adapter.go @@ -46,7 +46,8 @@ type CustomerOverrideAdapter interface { type InvoiceLineAdapter interface { CreateInvoiceLines(ctx context.Context, input CreateInvoiceLinesAdapterInput) (*CreateInvoiceLinesResponse, error) ListInvoiceLines(ctx context.Context, input ListInvoiceLinesAdapterInput) ([]billingentity.Line, error) - AssociateLinesToInvoice(ctx context.Context, input AssociateLinesToInvoiceAdapterInput) error + AssociateLinesToInvoice(ctx context.Context, input AssociateLinesToInvoiceAdapterInput) ([]billingentity.Line, error) + UpdateInvoiceLine(ctx context.Context, input UpdateInvoiceLineAdapterInput) (billingentity.Line, error) } type InvoiceAdapter interface { diff --git a/openmeter/billing/adapter/invoice.go b/openmeter/billing/adapter/invoice.go index 5e506bc43..33665f117 100644 --- a/openmeter/billing/adapter/invoice.go +++ b/openmeter/billing/adapter/invoice.go @@ -20,6 +20,7 @@ import ( "github.com/openmeterio/openmeter/pkg/framework/entutils" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/pagination" + "github.com/openmeterio/openmeter/pkg/slicesx" "github.com/openmeterio/openmeter/pkg/sortx" ) @@ -41,7 +42,7 @@ func (r *adapter) GetInvoiceById(ctx context.Context, in billing.GetInvoiceByIdI } if in.Expand.Lines { - query = r.expandLineItems(query) + query = r.expandInvoiceLineItems(query) } invoice, err := query.Only(ctx) @@ -60,6 +61,12 @@ func (r *adapter) GetInvoiceById(ctx context.Context, in billing.GetInvoiceByIdI return mapInvoiceFromDB(*invoice, in.Expand) } +func (r *adapter) expandInvoiceLineItems(query *db.BillingInvoiceQuery) *db.BillingInvoiceQuery { + return query.WithBillingInvoiceLines(func(q *db.BillingInvoiceLineQuery) { + r.expandLineItems(q) + }) +} + func (r *adapter) LockInvoicesForUpdate(ctx context.Context, input billing.LockInvoicesForUpdateInput) error { if err := input.Validate(); err != nil { return billingentity.ValidationError{ @@ -111,13 +118,6 @@ func (r *adapter) DeleteInvoices(ctx context.Context, input billing.DeleteInvoic return err } -// expandLineItems adds the required edges to the query so that line items can be properly mapped -func (r *adapter) expandLineItems(query *db.BillingInvoiceQuery) *db.BillingInvoiceQuery { - return query.WithBillingInvoiceLines(func(bilq *db.BillingInvoiceLineQuery) { - bilq.WithBillingInvoiceManualLines() - }) -} - func (r *adapter) ListInvoices(ctx context.Context, input billing.ListInvoicesInput) (billing.ListInvoicesResponse, error) { if err := input.Validate(); err != nil { return billing.ListInvoicesResponse{}, billingentity.ValidationError{ @@ -169,7 +169,7 @@ func (r *adapter) ListInvoices(ctx context.Context, input billing.ListInvoicesIn } if input.Expand.Lines { - query = r.expandLineItems(query) + query = r.expandInvoiceLineItems(query) } switch input.OrderBy { @@ -246,6 +246,7 @@ func (r *adapter) CreateInvoice(ctx context.Context, input billing.CreateInvoice SetNillableDueAt(input.DueAt). SetNillableCustomerTimezone(customer.Timezone). SetNillableIssuedAt(lo.EmptyableToPtr(input.IssuedAt)). + SetCustomerSubjectKeys(input.Customer.UsageAttribution.SubjectKeys). // Workflow (cloned) SetBillingWorkflowConfigID(clonedWorkflowConfig.ID). // TODO[later]: By cloning the AppIDs here we could support changing the apps in the billing profile if needed @@ -490,6 +491,7 @@ func mapInvoiceFromDB(invoice db.BillingInvoice, expand billingentity.InvoiceExp PhoneNumber: invoice.CustomerAddressPhoneNumber, }, Timezone: invoice.CustomerTimezone, + Subjects: invoice.CustomerSubjectKeys, }, Period: mapPeriodFromDB(invoice.PeriodStart, invoice.PeriodEnd), IssuedAt: invoice.IssuedAt, @@ -525,10 +527,14 @@ func mapInvoiceFromDB(invoice db.BillingInvoice, expand billingentity.InvoiceExp } if len(invoice.Edges.BillingInvoiceLines) > 0 { - res.Lines = make([]billingentity.Line, 0, len(invoice.Edges.BillingInvoiceLines)) - for _, line := range invoice.Edges.BillingInvoiceLines { - res.Lines = append(res.Lines, mapInvoiceLineFromDB(line)) + mappedLines, err := slicesx.MapWithErr(invoice.Edges.BillingInvoiceLines, func(line *db.BillingInvoiceLine) (billingentity.Line, error) { + return mapInvoiceLineFromDB(line) + }) + if err != nil { + return billingentity.Invoice{}, err } + + res.Lines = mappedLines } return res, nil diff --git a/openmeter/billing/adapter/invoicelines.go b/openmeter/billing/adapter/invoicelines.go index 59f608af5..819973285 100644 --- a/openmeter/billing/adapter/invoicelines.go +++ b/openmeter/billing/adapter/invoicelines.go @@ -12,21 +12,27 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" + "github.com/openmeterio/openmeter/pkg/slicesx" ) var _ billing.InvoiceLineAdapter = (*adapter)(nil) func (r *adapter) CreateInvoiceLines(ctx context.Context, input billing.CreateInvoiceLinesAdapterInput) (*billing.CreateInvoiceLinesResponse, error) { result := &billing.CreateInvoiceLinesResponse{ - Lines: make([]billingentity.Line, 0, len(input.Lines)), + Lines: make([]billingentity.Line, 0, len(input)), } - for _, line := range input.Lines { + for _, line := range input { + if line.Namespace == "" { + return nil, fmt.Errorf("namespace is required") + } + newEnt := r.db.BillingInvoiceLine.Create(). - SetNamespace(input.Namespace). + SetNamespace(line.Namespace). SetInvoiceID(line.InvoiceID). SetPeriodStart(line.Period.Start). SetPeriodEnd(line.Period.End). + SetNillableParentLineID(line.ParentLineID). SetInvoiceAt(line.InvoiceAt). SetStatus(line.Status). SetType(line.Type). @@ -41,17 +47,25 @@ func (r *adapter) CreateInvoiceLines(ctx context.Context, input billing.CreateIn case billingentity.InvoiceLineTypeManualFee: // Let's create the manual line for the invoice newManualLineConfig, err := r.db.BillingInvoiceManualLineConfig.Create(). - SetNamespace(input.Namespace). + SetNamespace(line.Namespace). SetUnitPrice(line.ManualFee.Price). Save(ctx) if err != nil { return nil, err } - newEnt = newEnt.SetBillingInvoiceManualLines(newManualLineConfig). + newEnt = newEnt.SetManualFeeLine(newManualLineConfig). SetQuantity(line.ManualFee.Quantity) - edges.BillingInvoiceManualLines = newManualLineConfig + edges.ManualFeeLine = newManualLineConfig + case billingentity.InvoiceLineTypeManualUsageBased: + newManualUBPLine, err := r.createManualUsageBasedLine(ctx, line.Namespace, line) + if err != nil { + return nil, err + } + + newEnt = newEnt.SetManualUsageBasedLine(newManualUBPLine) + edges.ManualUsageBasedLine = newManualUBPLine default: return nil, fmt.Errorf("unsupported type: %s", line.Type) } @@ -61,57 +75,244 @@ func (r *adapter) CreateInvoiceLines(ctx context.Context, input billing.CreateIn return nil, err } + if line.ParentLineID != nil { + // Let's fetch the parent line again + parentLineQuery := r.db.BillingInvoiceLine.Query(). + Where(billinginvoiceline.Namespace(line.Namespace)). + Where(billinginvoiceline.ID(*line.ParentLineID)) + + parentLineQuery = r.expandLineItems(parentLineQuery) + + parentLine, err := parentLineQuery.First(ctx) + if err != nil { + return nil, fmt.Errorf("fetching parent line: %w", err) + } + + edges.ParentLine = parentLine + } + savedLine.Edges = edges - result.Lines = append(result.Lines, mapInvoiceLineFromDB(savedLine)) + mappedLine, err := mapInvoiceLineFromDB(savedLine) + if err != nil { + return nil, fmt.Errorf("mapping line [id=%s]: %w", savedLine.ID, err) + } + + result.Lines = append(result.Lines, mappedLine) } return result, nil } +func (r *adapter) createManualUsageBasedLine(ctx context.Context, ns string, line billingentity.Line) (*db.BillingInvoiceManualUsageBasedLineConfig, error) { + lineConfig, err := r.db.BillingInvoiceManualUsageBasedLineConfig.Create(). + SetNamespace(ns). + SetPriceType(line.ManualUsageBased.Price.Type()). + SetPrice(&line.ManualUsageBased.Price). + SetFeatureKey(line.ManualUsageBased.FeatureKey). + Save(ctx) + if err != nil { + return nil, err + } + + return lineConfig, nil +} + func (r *adapter) ListInvoiceLines(ctx context.Context, input billing.ListInvoiceLinesAdapterInput) ([]billingentity.Line, error) { if err := input.Validate(); err != nil { return nil, err } - query := r.db.BillingInvoiceLine.Query(). - Where(billinginvoiceline.Namespace(input.Namespace)) + query := r.db.BillingInvoice.Query(). + Where(billinginvoice.Namespace(input.Namespace)) - if len(input.LineIDs) > 0 { - query = query.Where(billinginvoiceline.IDIn(input.LineIDs...)) + if input.CustomerID != "" { + query = query.Where(billinginvoice.CustomerID(input.CustomerID)) } - if input.InvoiceAtBefore != nil { - query = query.Where(billinginvoiceline.InvoiceAtLT(*input.InvoiceAtBefore)) + if len(input.InvoiceStatuses) > 0 { + query = query.Where(billinginvoice.StatusIn(input.InvoiceStatuses...)) } - query = query.WithBillingInvoice(func(biq *db.BillingInvoiceQuery) { - biq.Where(billinginvoice.Namespace(input.Namespace)) + query = query.WithBillingInvoiceLines(func(q *db.BillingInvoiceLineQuery) { + q = q.Where(billinginvoiceline.Namespace(input.Namespace)) + + if len(input.LineIDs) > 0 { + q = q.Where(billinginvoiceline.IDIn(input.LineIDs...)) + } + + if input.InvoiceAtBefore != nil { + q = q.Where(billinginvoiceline.InvoiceAtLT(*input.InvoiceAtBefore)) + } - if input.CustomerID != "" { - biq.Where(billinginvoice.CustomerID(input.CustomerID)) + if len(input.ParentLineIDs) > 0 { + if input.ParentLineIDsIncludeParent { + q = q.Where( + billinginvoiceline.Or( + billinginvoiceline.ParentLineIDIn(input.ParentLineIDs...), + billinginvoiceline.IDIn(input.ParentLineIDs...), + ), + ) + } else { + q = q.Where(billinginvoiceline.ParentLineIDIn(input.ParentLineIDs...)) + } } - if len(input.InvoiceStatuses) > 0 { - biq.Where(billinginvoice.StatusIn(input.InvoiceStatuses...)) + if len(input.Statuses) > 0 { + q = q.Where(billinginvoiceline.StatusIn(input.Statuses...)) } + + r.expandLineItems(q) }) - dbLines, err := query. - WithBillingInvoiceManualLines(). - All(ctx) + dbInvoices, err := query.All(ctx) if err != nil { return nil, err } - return lo.Map(dbLines, func(line *db.BillingInvoiceLine, _ int) billingentity.Line { + lines := lo.FlatMap(dbInvoices, func(dbInvoice *db.BillingInvoice, _ int) []*db.BillingInvoiceLine { + return dbInvoice.Edges.BillingInvoiceLines + }) + + return slicesx.MapWithErr(lines, func(line *db.BillingInvoiceLine) (billingentity.Line, error) { return mapInvoiceLineFromDB(line) - }), nil + }) +} + +func (r *adapter) expandLineItems(q *db.BillingInvoiceLineQuery) *db.BillingInvoiceLineQuery { + return q.WithManualFeeLine(). + WithManualUsageBasedLine(). + WithParentLine(func(q *db.BillingInvoiceLineQuery) { + // We cannot call ourselve here, as it would create an infinite loop + // but given we are only supporting one level of parent line, we can + // just expand the parent line here + q.WithManualFeeLine(). + WithManualUsageBasedLine() + }) +} + +func (r *adapter) UpdateInvoiceLine(ctx context.Context, input billing.UpdateInvoiceLineAdapterInput) (billingentity.Line, error) { + if err := input.Validate(); err != nil { + return billingentity.Line{}, err + } + + // TODO: Parent line saving + existingLine, err := r.db.BillingInvoiceLine.Query(). + WithManualFeeLine(). + WithManualUsageBasedLine(). + Where(billinginvoiceline.Namespace(input.Namespace)). + Where(billinginvoiceline.ID(input.ID)). + First(ctx) + if err != nil { + return billingentity.Line{}, fmt.Errorf("getting line: %w", err) + } + + if !existingLine.UpdatedAt.Equal(input.UpdatedAt) { + return billingentity.Line{}, billingentity.ConflictError{ + ID: input.ID, + Entity: billingentity.EntityInvoiceLine, + Err: fmt.Errorf("line has been updated since last read"), + } + } + + // Let's update the line + updateLine := r.db.BillingInvoiceLine.UpdateOneID(input.ID). + SetName(input.Name). + SetMetadata(input.Metadata). + SetOrClearDescription(input.Description). + SetInvoiceID(input.InvoiceID). + SetPeriodStart(input.Period.Start). + SetPeriodEnd(input.Period.End). + SetInvoiceAt(input.InvoiceAt). + SetNillableParentLineID(input.ParentLineID). + SetStatus(input.Status). + SetTaxOverrides(input.TaxOverrides) + + edges := db.BillingInvoiceLineEdges{} + + // Let's update the line based on the type + switch input.Type { + case billingentity.InvoiceLineTypeManualFee: + edges.ManualFeeLine, err = r.updateManualFeeLine(ctx, existingLine.Edges.ManualFeeLine.ID, input, updateLine) + if err != nil { + return billingentity.Line{}, err + } + + updateLine = updateLine.SetQuantity(input.ManualFee.Quantity) + case billingentity.InvoiceLineTypeManualUsageBased: + edges.ManualUsageBasedLine, err = r.updateManualUsageBasedLine(ctx, existingLine.Edges.ManualUsageBasedLine.ID, input) + if err != nil { + return billingentity.Line{}, err + } + + updateLine = updateLine.SetOrClearQuantity(input.ManualUsageBased.Quantity) + default: + return billingentity.Line{}, fmt.Errorf("unsupported line type: %s", input.Type) + } + + updatedLine, err := updateLine.Save(ctx) + if err != nil { + return billingentity.Line{}, fmt.Errorf("updating line: %w", err) + } + + if input.ParentLineID != nil { + // Let's fetch the parent line again + q := r.db.BillingInvoiceLine.Query(). + Where(billinginvoiceline.Namespace(input.Namespace)). + Where(billinginvoiceline.ID(*input.ParentLineID)) + + q = r.expandLineItems(q) + + parentLine, err := q.First(ctx) + if err != nil { + return billingentity.Line{}, fmt.Errorf("fetching parent line: %w", err) + } + + edges.ParentLine = parentLine + } + + updatedLine.Edges = edges + + return mapInvoiceLineFromDB(updatedLine) +} + +func (r *adapter) updateManualFeeLine( + ctx context.Context, + configId string, + input billing.UpdateInvoiceLineAdapterInput, + updateLine *db.BillingInvoiceLineUpdateOne, +) (*db.BillingInvoiceManualLineConfig, error) { + updateLine.SetQuantity(input.ManualFee.Quantity) + + updatedConfig, err := r.db.BillingInvoiceManualLineConfig.UpdateOneID(configId). + SetUnitPrice(input.ManualFee.Price). + Save(ctx) + if err != nil { + return nil, fmt.Errorf("updating manual fee line: %w", err) + } + + return updatedConfig, nil +} + +func (r *adapter) updateManualUsageBasedLine( + ctx context.Context, + configId string, + input billing.UpdateInvoiceLineAdapterInput, +) (*db.BillingInvoiceManualUsageBasedLineConfig, error) { + updatedConfig, err := r.db.BillingInvoiceManualUsageBasedLineConfig.UpdateOneID(configId). + SetPriceType(input.ManualUsageBased.Price.Type()). + SetPrice(&input.ManualUsageBased.Price). + Save(ctx) + if err != nil { + return nil, fmt.Errorf("updating manual fee line: %w", err) + } + + return updatedConfig, nil } -func (r *adapter) AssociateLinesToInvoice(ctx context.Context, input billing.AssociateLinesToInvoiceAdapterInput) error { +func (r *adapter) AssociateLinesToInvoice(ctx context.Context, input billing.AssociateLinesToInvoiceAdapterInput) ([]billingentity.Line, error) { if err := input.Validate(); err != nil { - return err + return nil, err } nAffected, err := r.db.BillingInvoiceLine.Update(). @@ -120,17 +321,34 @@ func (r *adapter) AssociateLinesToInvoice(ctx context.Context, input billing.Ass Where(billinginvoiceline.IDIn(input.LineIDs...)). Save(ctx) if err != nil { - return fmt.Errorf("associating lines: %w", err) + return nil, fmt.Errorf("associating lines: %w", err) } if nAffected != len(input.LineIDs) { - return fmt.Errorf("fewer lines were associated (%d) than expected (%d)", nAffected, len(input.LineIDs)) + return nil, fmt.Errorf("not all lines were associated") } - return nil + return r.fetchLines(ctx, input.Invoice.Namespace, input.LineIDs) } -func mapInvoiceLineFromDB(dbLine *db.BillingInvoiceLine) billingentity.Line { +func (r *adapter) fetchLines(ctx context.Context, ns string, lineIDs []string) ([]billingentity.Line, error) { + query := r.db.BillingInvoiceLine.Query(). + Where(billinginvoiceline.Namespace(ns)). + Where(billinginvoiceline.IDIn(lineIDs...)) + + query = r.expandLineItems(query) + + dbLines, err := query.All(ctx) + if err != nil { + return nil, fmt.Errorf("fetching lines: %w", err) + } + + return slicesx.MapWithErr(dbLines, func(line *db.BillingInvoiceLine) (billingentity.Line, error) { + return mapInvoiceLineFromDB(line) + }) +} + +func mapInvoiceLineFromDB(dbLine *db.BillingInvoiceLine) (billingentity.Line, error) { invoiceLine := billingentity.Line{ LineBase: billingentity.LineBase{ Namespace: dbLine.Namespace, @@ -142,12 +360,15 @@ func mapInvoiceLineFromDB(dbLine *db.BillingInvoiceLine) billingentity.Line { Metadata: dbLine.Metadata, InvoiceID: dbLine.InvoiceID, + Status: dbLine.Status, Period: billingentity.Period{ Start: dbLine.PeriodStart, End: dbLine.PeriodEnd, }, + ParentLineID: dbLine.ParentLineID, + InvoiceAt: dbLine.InvoiceAt, Name: dbLine.Name, @@ -159,13 +380,40 @@ func mapInvoiceLineFromDB(dbLine *db.BillingInvoiceLine) billingentity.Line { }, } + if (dbLine.Edges.ParentLine != nil) != (dbLine.ParentLineID != nil) { // XOR + // This happens if the expandLineItems function is not used, please make sure + // it's called in all code pathes + return invoiceLine, fmt.Errorf("inconsistent parent line data") + } + + if dbLine.Edges.ParentLine != nil { + parentLine, err := mapInvoiceLineFromDB(dbLine.Edges.ParentLine) + if err != nil { + return invoiceLine, fmt.Errorf("mapping parent line: %w", err) + } + + invoiceLine.ParentLine = &parentLine + } + switch dbLine.Type { case billingentity.InvoiceLineTypeManualFee: - invoiceLine.ManualFee = &billingentity.ManualFeeLine{ - Price: dbLine.Edges.BillingInvoiceManualLines.UnitPrice, + invoiceLine.ManualFee = billingentity.ManualFeeLine{ + Price: dbLine.Edges.ManualFeeLine.UnitPrice, Quantity: lo.FromPtrOr(dbLine.Quantity, alpacadecimal.Zero), } + case billingentity.InvoiceLineTypeManualUsageBased: + ubpLine := dbLine.Edges.ManualUsageBasedLine + if ubpLine == nil { + return invoiceLine, fmt.Errorf("manual usage based line is missing") + } + invoiceLine.ManualUsageBased = billingentity.ManualUsageBasedLine{ + FeatureKey: ubpLine.FeatureKey, + Price: *ubpLine.Price, + Quantity: dbLine.Quantity, + } + default: + return invoiceLine, fmt.Errorf("unsupported line type[%s]: %s", dbLine.ID, dbLine.Type) } - return invoiceLine + return invoiceLine, nil } diff --git a/openmeter/billing/entity/defaults.go b/openmeter/billing/entity/defaults.go new file mode 100644 index 000000000..6707729cd --- /dev/null +++ b/openmeter/billing/entity/defaults.go @@ -0,0 +1,7 @@ +package billingentity + +import "time" + +const ( + DefaultMeterResolution = time.Minute +) diff --git a/openmeter/billing/entity/errors.go b/openmeter/billing/entity/errors.go index 473710665..964a5736b 100644 --- a/openmeter/billing/entity/errors.go +++ b/openmeter/billing/entity/errors.go @@ -26,6 +26,12 @@ var ( ErrInvoiceCannotAdvance = NewValidationError("invoice_cannot_advance", "invoice cannot advance") ErrInvoiceActionNotAvailable = NewValidationError("invoice_action_not_available", "invoice action not available") + + ErrInvoiceLineFeatureHasNoMeters = NewValidationError("invoice_line_feature_has_no_meters", "usage based invoice line: feature has no meters") + ErrInvoiceCreateNoLines = NewValidationError("invoice_create_no_lines", "the new invoice would have no lines") + ErrInvoiceCreateUBPLineCustomerHasNoSubjects = NewValidationError("invoice_create_ubp_line_customer_has_no_subjects", "creating an usage based line: customer has no subjects") + ErrInvoiceCreateUBPLinePeriodIsEmpty = NewValidationError("invoice_create_ubp_line_period_is_empty", "creating an usage based line: truncated period is empty") + ErrInvoiceLineCurrencyMismatch = NewValidationError("invoice_line_currency_mismatch", "invoice line currency mismatch") ) var _ error = (*NotFoundError)(nil) diff --git a/openmeter/billing/entity/invoice.go b/openmeter/billing/entity/invoice.go index e1954928c..daa3c48d5 100644 --- a/openmeter/billing/entity/invoice.go +++ b/openmeter/billing/entity/invoice.go @@ -221,6 +221,7 @@ type InvoiceCustomer struct { Name string `json:"name"` BillingAddress *models.Address `json:"billingAddress,omitempty"` Timezone *timezone.Timezone `json:"timezone,omitempty"` + Subjects []string `json:"subjects,omitempty"` } func (i *InvoiceCustomer) Validate() error { diff --git a/openmeter/billing/entity/invoiceline.go b/openmeter/billing/entity/invoiceline.go index ce1189110..631d2b26a 100644 --- a/openmeter/billing/entity/invoiceline.go +++ b/openmeter/billing/entity/invoiceline.go @@ -7,6 +7,7 @@ import ( "github.com/alpacahq/alpacadecimal" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" "github.com/openmeterio/openmeter/pkg/currencyx" ) @@ -15,6 +16,8 @@ type InvoiceLineType string const ( // InvoiceLineTypeManualFee is an item that is manually added to the invoice. InvoiceLineTypeManualFee InvoiceLineType = "manual_fee" + // InvoiceLineTypeManualUsageBased is an item that is manually added to the invoice and is usage based. + InvoiceLineTypeManualUsageBased InvoiceLineType = "manual_usage_based" // InvoiceLineTypeFlatFee is an item that is charged at a fixed rate. InvoiceLineTypeFlatFee InvoiceLineType = "flat_fee" // InvoiceLineTypeUsageBased is an item that is charged based on usage. @@ -24,6 +27,7 @@ const ( func (InvoiceLineType) Values() []string { return []string{ string(InvoiceLineTypeManualFee), + string(InvoiceLineTypeManualUsageBased), string(InvoiceLineTypeFlatFee), string(InvoiceLineTypeUsageBased), } @@ -66,6 +70,25 @@ func (p Period) Validate() error { return nil } +func (p Period) Truncate(resolution time.Duration) Period { + return Period{ + Start: p.Start.Truncate(resolution), + End: p.End.Truncate(resolution), + } +} + +func (p Period) Equal(other Period) bool { + return p.Start.Equal(other.Start) && p.End.Equal(other.End) +} + +func (p Period) IsEmpty() bool { + return !p.End.After(p.Start) +} + +func (p Period) Contains(t time.Time) bool { + return t.After(p.Start) && t.Before(p.End) +} + // LineBase represents the common fields for an invoice item. type LineBase struct { Namespace string `json:"namespace"` @@ -90,7 +113,8 @@ type LineBase struct { // TODO: Add discounts etc // Relationships - ParentLine *string `json:"parentLine,omitempty"` + ParentLineID *string `json:"parentLine,omitempty"` + ParentLine *Line `json:"parent,omitempty"` RelatedLines []string `json:"relatedLine,omitempty"` Status InvoiceLineStatus `json:"status"` @@ -112,6 +136,10 @@ func (i LineBase) Validate() error { return errors.New("invoice at is required") } + if i.InvoiceAt.Before(i.Period.Start) { + return errors.New("invoice at must be after period start") + } + if i.Name == "" { return errors.New("name is required") } @@ -136,7 +164,8 @@ type ManualFeeLine struct { type Line struct { LineBase - ManualFee *ManualFeeLine `json:"manualFee,omitempty"` + ManualFee ManualFeeLine `json:"manualFee,omitempty"` + ManualUsageBased ManualUsageBasedLine `json:"manualUsageBased,omitempty"` } func (i Line) Validate() error { @@ -144,19 +173,21 @@ func (i Line) Validate() error { return fmt.Errorf("base: %w", err) } + if i.InvoiceAt.Before(i.Period.Truncate(DefaultMeterResolution).Start) { + return errors.New("invoice at must be after period start") + } + switch i.Type { case InvoiceLineTypeManualFee: return i.ValidateManualFee() + case InvoiceLineTypeManualUsageBased: + return i.ValidateManualUsageBased() default: return fmt.Errorf("unsupported type: %s", i.Type) } } func (i Line) ValidateManualFee() error { - if i.ManualFee == nil { - return errors.New("manual fee is required") - } - if !i.ManualFee.Price.IsPositive() { return errors.New("price should be greater than zero") } @@ -168,3 +199,33 @@ func (i Line) ValidateManualFee() error { // TODO: Validate currency specifics return nil } + +func (i Line) ValidateManualUsageBased() error { + if err := i.ManualUsageBased.Validate(); err != nil { + return fmt.Errorf("manual usage price: %w", err) + } + + if i.InvoiceAt.Before(i.Period.Truncate(DefaultMeterResolution).End) { + return errors.New("invoice at must be after period end for usage based line") + } + + return nil +} + +type ManualUsageBasedLine struct { + Price plan.Price `json:"price"` + FeatureKey string `json:"featureKey"` + Quantity *alpacadecimal.Decimal `json:"quantity"` +} + +func (i ManualUsageBasedLine) Validate() error { + if err := i.Price.Validate(); err != nil { + return fmt.Errorf("price: %w", err) + } + + if i.FeatureKey == "" { + return errors.New("featureKey is required") + } + + return nil +} diff --git a/openmeter/billing/httpdriver/invoiceline.go b/openmeter/billing/httpdriver/invoiceline.go index 69d8e368f..335dd9852 100644 --- a/openmeter/billing/httpdriver/invoiceline.go +++ b/openmeter/billing/httpdriver/invoiceline.go @@ -131,7 +131,7 @@ func mapCreateManualFeeLineToEntity(line api.BillingManualFeeLineCreateItem, ns InvoiceAt: line.InvoiceAt, TaxOverrides: mapTaxConfigToEntity(line.TaxOverrides), }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: price, Quantity: qty, }, @@ -180,10 +180,6 @@ func mapBillingLineToAPI(line billingentity.Line) (api.BillingInvoiceLine, error } func mapManualFeeLineToAPI(line billingentity.Line) (api.BillingInvoiceLine, error) { - if line.ManualFee == nil { - return api.BillingInvoiceLine{}, fmt.Errorf("manual fee line is nil") - } - feeLine := api.BillingManualFeeLine{ Type: api.BillingManualFeeLineTypeManualFee, Id: line.ID, diff --git a/openmeter/billing/invoiceline.go b/openmeter/billing/invoiceline.go index facb49a67..cda60f9cf 100644 --- a/openmeter/billing/invoiceline.go +++ b/openmeter/billing/invoiceline.go @@ -32,21 +32,18 @@ func (c CreateInvoiceLinesInput) Validate() error { return nil } -type CreateInvoiceLinesAdapterInput struct { - Namespace string - Lines []billingentity.Line -} +type CreateInvoiceLinesAdapterInput []billingentity.Line func (c CreateInvoiceLinesAdapterInput) Validate() error { - if c.Namespace == "" { - return errors.New("namespace is required") - } - - for i, line := range c.Lines { + for i, line := range c { if err := line.Validate(); err != nil { return fmt.Errorf("Line[%d]: %w", i, err) } + if line.Namespace == "" { + return fmt.Errorf("Line[%d]: namespace is required", i) + } + if line.InvoiceID == "" { return fmt.Errorf("Line[%d]: invoice id is required", i) } @@ -62,9 +59,12 @@ type CreateInvoiceLinesResponse struct { type ListInvoiceLinesAdapterInput struct { Namespace string - CustomerID string - InvoiceStatuses []billingentity.InvoiceStatus - InvoiceAtBefore *time.Time + CustomerID string + InvoiceStatuses []billingentity.InvoiceStatus + InvoiceAtBefore *time.Time + ParentLineIDs []string + ParentLineIDsIncludeParent bool + Statuses []billingentity.InvoiceLineStatus LineIDs []string } @@ -94,3 +94,5 @@ func (i AssociateLinesToInvoiceAdapterInput) Validate() error { return nil } + +type UpdateInvoiceLineAdapterInput billingentity.Line diff --git a/openmeter/billing/service/invoice.go b/openmeter/billing/service/invoice.go index b127ef514..79143dfec 100644 --- a/openmeter/billing/service/invoice.go +++ b/openmeter/billing/service/invoice.go @@ -11,6 +11,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + lineservice "github.com/openmeterio/openmeter/openmeter/billing/service/lineservice" "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/entutils" @@ -105,14 +106,19 @@ func (s *Service) CreateInvoice(ctx context.Context, input billing.CreateInvoice asof := lo.FromPtrOr(input.AsOf, clock.Now()) + lineSrv, err := s.newLineService(txAdapter) + if err != nil { + return nil, fmt.Errorf("creating line service: %w", err) + } + // let's gather the in-scope lines and validate it - inScopeLines, err := s.gatherInscopeLines(ctx, input, txAdapter, asof) + inScopeLines, err := s.gatherInscopeLines(ctx, input, txAdapter, lineSrv, asof) if err != nil { return nil, err } - sourceInvoiceIDs := lo.Uniq(lo.Map(inScopeLines, func(l billingentity.Line, _ int) string { - return l.InvoiceID + sourceInvoiceIDs := lo.Uniq(lo.Map(inScopeLines, func(l lineservice.LineWithBillablePeriod, _ int) string { + return l.InvoiceID() })) if len(sourceInvoiceIDs) == 0 { @@ -130,8 +136,8 @@ func (s *Service) CreateInvoice(ctx context.Context, input billing.CreateInvoice return nil, fmt.Errorf("locking gathering invoices: %w", err) } - linesByCurrency := lo.GroupBy(inScopeLines, func(line billingentity.Line) currencyx.Code { - return line.Currency + linesByCurrency := lo.GroupBy(inScopeLines, func(line lineservice.LineWithBillablePeriod) currencyx.Code { + return line.Currency() }) createdInvoices := make([]billingentity.InvoiceID, 0, len(linesByCurrency)) @@ -158,7 +164,7 @@ func (s *Service) CreateInvoice(ctx context.Context, input billing.CreateInvoice }) // let's associate the invoice lines to the invoice - err = s.associateLinesToInvoice(ctx, txAdapter, invoice, lines) + err = s.associateLinesToInvoice(ctx, lineSrv, invoice, lines) if err != nil { return nil, fmt.Errorf("associating lines to invoice: %w", err) } @@ -219,7 +225,7 @@ func (s *Service) CreateInvoice(ctx context.Context, input billing.CreateInvoice }) } -func (s *Service) gatherInscopeLines(ctx context.Context, input billing.CreateInvoiceInput, txAdapter billing.Adapter, asOf time.Time) ([]billingentity.Line, error) { +func (s *Service) gatherInscopeLines(ctx context.Context, input billing.CreateInvoiceInput, txAdapter billing.Adapter, lineSrv *lineservice.Service, asOf time.Time) ([]lineservice.LineWithBillablePeriod, error) { if input.IncludePendingLines != nil { inScopeLines, err := txAdapter.ListInvoiceLines(ctx, billing.ListInvoiceLinesAdapterInput{ @@ -232,21 +238,21 @@ func (s *Service) gatherInscopeLines(ctx context.Context, input billing.CreateIn return nil, fmt.Errorf("resolving in scope lines: %w", err) } - // output validation + lines, err := lineSrv.FromEntities(inScopeLines) + if err != nil { + return nil, fmt.Errorf("creating line services: %w", err) + } - // asOf validity - for _, line := range inScopeLines { - if line.InvoiceAt.After(asOf) { - return nil, billingentity.ValidationError{ - Err: fmt.Errorf("line [%s] has invoiceAt [%s] after asOf [%s]", line.ID, line.InvoiceAt, asOf), - } - } + // output validation + resolvedLines, err := lines.ResolveBillablePeriod(ctx, asOf) + if err != nil { + return nil, err } // all lines must be found - if len(inScopeLines) != len(input.IncludePendingLines) { - includedLines := lo.Map(inScopeLines, func(l billingentity.Line, _ int) string { - return l.ID + if len(resolvedLines) != len(input.IncludePendingLines) { + includedLines := lo.Map(resolvedLines, func(l lineservice.LineWithBillablePeriod, _ int) string { + return l.ID() }) missingIDs := lo.Without(input.IncludePendingLines, includedLines...) @@ -254,11 +260,11 @@ func (s *Service) gatherInscopeLines(ctx context.Context, input billing.CreateIn return nil, billingentity.NotFoundError{ ID: strings.Join(missingIDs, ","), Entity: billingentity.EntityInvoiceLine, - Err: fmt.Errorf("some invoice lines are not found"), + Err: fmt.Errorf("some invoice lines are not billable"), } } - return inScopeLines, nil + return resolvedLines, nil } lines, err := txAdapter.ListInvoiceLines(ctx, @@ -269,21 +275,20 @@ func (s *Service) gatherInscopeLines(ctx context.Context, input billing.CreateIn InvoiceStatuses: []billingentity.InvoiceStatus{ billingentity.InvoiceStatusGathering, }, - - InvoiceAtBefore: lo.ToPtr(asOf), + Statuses: []billingentity.InvoiceLineStatus{ + billingentity.InvoiceLineStatusValid, + }, }) if err != nil { return nil, err } - if len(lines) == 0 { - // We haven't requested explicit empty invoice, so we should have some pending lines - return nil, billingentity.ValidationError{ - Err: fmt.Errorf("no pending lines found"), - } + lineSrvs, err := lineSrv.FromEntities(lines) + if err != nil { + return nil, err } - return lines, nil + return lineSrvs.ResolveBillablePeriod(ctx, asOf) } func (s *Service) withLockedInvoiceStateMachine( diff --git a/openmeter/billing/service/invoiceline.go b/openmeter/billing/service/invoiceline.go index 3b98dd1d6..c9fe1bf51 100644 --- a/openmeter/billing/service/invoiceline.go +++ b/openmeter/billing/service/invoiceline.go @@ -4,11 +4,10 @@ import ( "context" "fmt" - "github.com/samber/lo" - "github.com/openmeterio/openmeter/api" "github.com/openmeterio/openmeter/openmeter/billing" billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + lineservice "github.com/openmeterio/openmeter/openmeter/billing/service/lineservice" customerentity "github.com/openmeterio/openmeter/openmeter/customer/entity" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/pagination" @@ -18,6 +17,13 @@ import ( var _ billing.InvoiceLineService = (*Service)(nil) func (s *Service) CreateInvoiceLines(ctx context.Context, input billing.CreateInvoiceLinesInput) (*billing.CreateInvoiceLinesResponse, error) { + for i := range input.Lines { + input.Lines[i].Namespace = input.Namespace + input.Lines[i].Status = billingentity.InvoiceLineStatusValid + } + + // TODO: add validation that if the invoice's subject_keys is empty, do not allow usage based lines + // to be created. if err := input.Validate(); err != nil { return nil, billingentity.ValidationError{ Err: err, @@ -41,33 +47,55 @@ func (s *Service) CreateInvoiceLines(ctx context.Context, input billing.CreateIn return nil, fmt.Errorf("fetching customer profile: %w", err) } + lineSrv, err := s.newLineService(txAdapter) + if err != nil { + return nil, fmt.Errorf("creating line service: %w", err) + } + + lines := make(lineservice.Lines, 0, len(input.Lines)) + // TODO: we should optimize this as this does O(n) queries for invoices per line for i, line := range input.Lines { - updatedLine, err := s.upsertLineInvoice(ctx, txAdapter, line, input, customerProfile) + line.Namespace = input.Namespace + + updateResult, err := s.upsertLineInvoice(ctx, txAdapter, line, input, customerProfile) if err != nil { return nil, fmt.Errorf("upserting line[%d]: %w", i, err) } - input.Lines[i] = updatedLine + lineService, err := lineSrv.FromEntity(updateResult.Line) + if err != nil { + return nil, fmt.Errorf("creating line service[%d]: %w", i, err) + } + + if err := lineService.Validate(ctx, updateResult.Invoice); err != nil { + return nil, fmt.Errorf("validating line[%s]: %w", input.Lines[i].ID, err) + } + + lineService, err = lineService.PrepareForCreate(ctx) + if err != nil { + return nil, fmt.Errorf("modifying line[%s]: %w", input.Lines[i].ID, err) + } + + lines = append(lines, lineService) } // Create the invoice Lines - lines, err := txAdapter.CreateInvoiceLines(ctx, billing.CreateInvoiceLinesAdapterInput{ - Namespace: input.Namespace, - Lines: input.Lines, - }) + createdLines, err := txAdapter.CreateInvoiceLines(ctx, lines.ToEntities()) if err != nil { return nil, fmt.Errorf("creating invoice Line: %w", err) } - return lines, nil + return createdLines, nil }) } -func (s *Service) upsertLineInvoice(ctx context.Context, txAdapter billing.Adapter, line billingentity.Line, input billing.CreateInvoiceLinesInput, customerProfile *billingentity.ProfileWithCustomerDetails) (billingentity.Line, error) { - // Let's set the default values for the line item - line.Status = billingentity.InvoiceLineStatusValid +type upsertLineInvoiceResponse struct { + Line billingentity.Line + Invoice *billingentity.Invoice +} +func (s *Service) upsertLineInvoice(ctx context.Context, txAdapter billing.Adapter, line billingentity.Line, input billing.CreateInvoiceLinesInput, customerProfile *billingentity.ProfileWithCustomerDetails) (*upsertLineInvoiceResponse, error) { if line.InvoiceID != "" { // We would want to attach the line to an existing invoice invoice, err := txAdapter.GetInvoiceById(ctx, billing.GetInvoiceByIdInput{ @@ -77,25 +105,28 @@ func (s *Service) upsertLineInvoice(ctx context.Context, txAdapter billing.Adapt }, }) if err != nil { - return line, billingentity.ValidationError{ + return nil, billingentity.ValidationError{ Err: fmt.Errorf("fetching invoice [%s]: %w", line.InvoiceID, err), } } if !invoice.StatusDetails.Immutable { - return line, billingentity.ValidationError{ + return nil, billingentity.ValidationError{ Err: fmt.Errorf("invoice [%s] is not mutable", line.InvoiceID), } } if invoice.Currency != line.Currency { - return line, billingentity.ValidationError{ + return nil, billingentity.ValidationError{ Err: fmt.Errorf("currency mismatch: invoice [%s] currency is %s, but line currency is %s", line.InvoiceID, invoice.Currency, line.Currency), } } line.InvoiceID = invoice.ID - return line, nil + return &upsertLineInvoiceResponse{ + Line: line, + Invoice: &invoice, + }, nil } // We would want to stage a pending invoice Line @@ -112,7 +143,7 @@ func (s *Service) upsertLineInvoice(ctx context.Context, txAdapter billing.Adapt Order: sortx.OrderAsc, }) if err != nil { - return line, fmt.Errorf("fetching gathering invoices: %w", err) + return nil, fmt.Errorf("fetching gathering invoices: %w", err) } if len(pendingInvoiceList.Items) == 0 { @@ -126,50 +157,103 @@ func (s *Service) upsertLineInvoice(ctx context.Context, txAdapter billing.Adapt Type: billingentity.InvoiceTypeStandard, }) if err != nil { - return line, fmt.Errorf("creating invoice: %w", err) + return nil, fmt.Errorf("creating invoice: %w", err) } line.InvoiceID = invoice.ID - } else { - // Attach to the first pending invoice - line.InvoiceID = pendingInvoiceList.Items[0].ID - - if len(pendingInvoiceList.Items) > 1 { - // Note: Given that we are not using serializable transactions (which is fine), we might - // have multiple gathering invoices for the same customer. - // This is a rare case, but we should log it at least, later we can implement a call that - // merges these invoices (it's fine to just move the Lines to the first invoice) - s.logger.Warn("more than one pending invoice found", "customer", input.CustomerID, "namespace", input.Namespace) - } + + return &upsertLineInvoiceResponse{ + Line: line, + Invoice: &invoice, + }, nil + } + + // Attach to the first pending invoice + line.InvoiceID = pendingInvoiceList.Items[0].ID + + if len(pendingInvoiceList.Items) > 1 { + // Note: Given that we are not using serializable transactions (which is fine), we might + // have multiple gathering invoices for the same customer. + // This is a rare case, but we should log it at least, later we can implement a call that + // merges these invoices (it's fine to just move the Lines to the first invoice) + s.logger.Warn("more than one pending invoice found", "customer", input.CustomerID, "namespace", input.Namespace) } - return line, nil + return &upsertLineInvoiceResponse{ + Line: line, + Invoice: &pendingInvoiceList.Items[0], + }, nil } -func (s *Service) associateLinesToInvoice(ctx context.Context, txAdapter billing.Adapter, invoice billingentity.Invoice, lines []billingentity.Line) error { +func (s *Service) associateLinesToInvoice(ctx context.Context, lineSrv *lineservice.Service, invoice billingentity.Invoice, lines []lineservice.LineWithBillablePeriod) error { for _, line := range lines { - if line.InvoiceID == invoice.ID { + if line.InvoiceID() == invoice.ID { return billingentity.ValidationError{ - Err: fmt.Errorf("line[%s]: line already associated with invoice[%s]", line.ID, invoice.ID), + Err: fmt.Errorf("line[%s]: line already associated with invoice[%s]", line.ID(), invoice.ID), } } } - // Associate the lines to the invoice - err := txAdapter.AssociateLinesToInvoice(ctx, billing.AssociateLinesToInvoiceAdapterInput{ - Invoice: billingentity.InvoiceID{ - ID: invoice.ID, - Namespace: invoice.Namespace, - }, + invoiceLines := make(lineservice.Lines, 0, len(lines)) + // Let's do the line splitting if needed + for _, line := range lines { + if !line.Period().Equal(line.BillablePeriod) { + // We need to split the line into multiple lines + if !line.Period().Start.Equal(line.BillablePeriod.Start) { + return fmt.Errorf("line[%s]: line period start[%s] is not equal to billable period start[%s]", line.ID(), line.Period().Start, line.BillablePeriod.Start) + } - LineIDs: lo.Map(lines, func(l billingentity.Line, _ int) string { - return l.ID - }), - }) + splitLine, err := line.Split(ctx, line.BillablePeriod.End) + if err != nil { + return fmt.Errorf("line[%s]: splitting line: %w", line.ID(), err) + } + + invoiceLines = append(invoiceLines, splitLine.PreSplitAtLine) + } else { + invoiceLines = append(invoiceLines, line) + } + } + + // Validate that the line can be associated with the invoice + var validationErrors error + for _, line := range invoiceLines { + if err := line.Validate(ctx, &invoice); err != nil { + validationErrors = fmt.Errorf("line[%s]: %w", line.ID(), err) + } + } + if validationErrors != nil { + return validationErrors + } + + // Associate the lines to the invoice + invoiceLines, err := lineSrv.AssociateLinesToInvoice(ctx, &invoice, invoiceLines) if err != nil { - return err + return fmt.Errorf("associating lines to invoice: %w", err) + } + + // Let's create the sub lines as per the meters + for _, line := range invoiceLines { + snapshot, err := line.SnapshotQuantity(ctx, &invoice) + if err != nil { + return fmt.Errorf("line[%s]: snapshotting quantity: %w", line.ID(), err) + } + + // TODO[later]: detailed lines + + _, err = snapshot.Line.Save(ctx) + if err != nil { + return fmt.Errorf("line[%s]: saving line: %w", line.ID(), err) + } } - // TODO[later]: Here we need to recalculate any line specific fields for both invoices return nil } + +func (s *Service) newLineService(adapter billing.Adapter) (*lineservice.Service, error) { + return lineservice.New(lineservice.Config{ + BillingAdapter: adapter, + FeatureService: s.featureService, + MeterRepo: s.meterRepo, + StreamingConnector: s.streamingConnector, + }) +} diff --git a/openmeter/billing/service/lineservice/doc.go b/openmeter/billing/service/lineservice/doc.go new file mode 100644 index 000000000..128e702f6 --- /dev/null +++ b/openmeter/billing/service/lineservice/doc.go @@ -0,0 +1,6 @@ +// lineservice package contains the implementation of the LineAdapter interface which acts as a +// adapter between the specific line types and the billing service. +// +// This package allows us to not to have line type specific implementation details in the billing +// service. (Which is already quite complex) +package lineservice diff --git a/openmeter/billing/service/lineservice/linebase.go b/openmeter/billing/service/lineservice/linebase.go new file mode 100644 index 000000000..b6d84fe7b --- /dev/null +++ b/openmeter/billing/service/lineservice/linebase.go @@ -0,0 +1,229 @@ +package lineservice + +import ( + "context" + "fmt" + "time" + + "github.com/samber/lo" + + "github.com/openmeterio/openmeter/openmeter/billing" + billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + "github.com/openmeterio/openmeter/pkg/currencyx" +) + +type UpdateInput struct { + ParentLine *Line + PeriodStart time.Time + PeriodEnd time.Time + InvoiceAt time.Time + Status billingentity.InvoiceLineStatus +} + +func (i UpdateInput) apply(line *billingentity.Line) { + if !i.PeriodStart.IsZero() { + line.Period.Start = i.PeriodStart + } + + if !i.PeriodEnd.IsZero() { + line.Period.End = i.PeriodEnd + } + + if !i.InvoiceAt.IsZero() { + line.InvoiceAt = i.InvoiceAt + } + + if i.Status != "" { + line.Status = i.Status + } +} + +type SplitResult struct { + PreSplitAtLine Line + PostSplitAtLine Line +} + +type LineBase interface { + ToEntity() billingentity.Line + ID() string + InvoiceID() string + Currency() currencyx.Code + Period() billingentity.Period + Status() billingentity.InvoiceLineStatus + HasParent() bool + + CloneForCreate(in UpdateInput) Line + Update(in UpdateInput) Line + Save(context.Context) (Line, error) + + // Split splits a line into two lines at the given time. + // The strategy is that we will have a line with status InvoiceLineStatusSplit and two child + // lines with status InvoiceLineStatusValid. + // + // To make algorithms easier, upon next split, we will not create an imbalanced tree, but rather attach + // the new split line to the existing parent line. + Split(ctx context.Context, at time.Time) (SplitResult, error) + + Service() *Service +} + +var _ LineBase = (*lineBase)(nil) + +type lineBase struct { + line billingentity.Line + service *Service +} + +func (l lineBase) ToEntity() billingentity.Line { + return l.line +} + +func (l lineBase) ID() string { + return l.line.ID +} + +func (l lineBase) InvoiceID() string { + return l.line.InvoiceID +} + +func (l lineBase) Currency() currencyx.Code { + return l.line.Currency +} + +func (l lineBase) Period() billingentity.Period { + return l.line.Period +} + +func (l lineBase) Status() billingentity.InvoiceLineStatus { + return l.line.Status +} + +func (l lineBase) HasParent() bool { + return l.line.ParentLineID != nil +} + +func (l lineBase) Validate(ctx context.Context, invoice *billingentity.Invoice) error { + if l.line.Currency != invoice.Currency || l.line.Currency == "" { + return billingentity.ValidationError{ + Err: billingentity.ErrInvoiceLineCurrencyMismatch, + } + } + + return nil +} + +func (l lineBase) Save(ctx context.Context) (Line, error) { + line, err := l.service.BillingAdapter.UpdateInvoiceLine(ctx, billing.UpdateInvoiceLineAdapterInput(l.line)) + if err != nil { + return nil, fmt.Errorf("updating invoice line: %w", err) + } + + return l.service.FromEntity(line) +} + +func (l lineBase) Service() *Service { + return l.service +} + +func (l lineBase) CloneForCreate(in UpdateInput) Line { + l.line.ID = "" + l.line.CreatedAt = time.Time{} + l.line.UpdatedAt = time.Time{} + + return l.update(in) +} + +func (l lineBase) Update(in UpdateInput) Line { + return l.update(in) +} + +func (l lineBase) update(in UpdateInput) Line { + in.apply(&l.line) + + // Let's update the parent line + if in.ParentLine != nil { + newParentLine := *in.ParentLine + if newParentLine == nil { + l.line.ParentLineID = nil + l.line.ParentLine = nil + } else { + l.line.ParentLineID = lo.ToPtr(newParentLine.ID()) + l.line.ParentLine = lo.ToPtr(newParentLine.ToEntity()) + } + } + + // Let's ignore the error here as we don't allow for any type updates + svc, _ := l.service.FromEntity(l.line) + + return svc +} + +func (l lineBase) Split(ctx context.Context, splitAt time.Time) (SplitResult, error) { + // We only split valid lines; split etc. lines are not supported + if l.line.Status != billingentity.InvoiceLineStatusValid { + return SplitResult{}, fmt.Errorf("line[%s]: line is not valid", l.line.ID) + } + + if !l.line.Period.Contains(splitAt) { + return SplitResult{}, fmt.Errorf("line[%s]: splitAt is not within the line period", l.line.ID) + } + + if !l.HasParent() { + parentLine, err := l.Update(UpdateInput{ + Status: billingentity.InvoiceLineStatusSplit, + }).Save(ctx) + if err != nil { + return SplitResult{}, fmt.Errorf("saving parent line: %w", err) + } + + // Let's create the child lines + preSplitAtLine := l.CloneForCreate(UpdateInput{ + ParentLine: &parentLine, + Status: billingentity.InvoiceLineStatusValid, + PeriodEnd: splitAt, + InvoiceAt: splitAt, + }) + + postSplitAtLine := l.CloneForCreate(UpdateInput{ + ParentLine: &parentLine, + Status: billingentity.InvoiceLineStatusValid, + PeriodStart: splitAt, + }) + + splitLines, err := l.service.CreateLines(ctx, preSplitAtLine, postSplitAtLine) + if err != nil { + return SplitResult{}, fmt.Errorf("creating split lines: %w", err) + } + + return SplitResult{ + PreSplitAtLine: splitLines[0], + PostSplitAtLine: splitLines[1], + }, nil + } + + // We have alredy split the line once, we just need to create a new line and update the existing line + postSplitAtLine := l.CloneForCreate(UpdateInput{ + Status: billingentity.InvoiceLineStatusValid, + PeriodStart: splitAt, + }) + + createdLines, err := l.service.CreateLines(ctx, postSplitAtLine) + if err != nil { + return SplitResult{}, fmt.Errorf("creating split lines: %w", err) + } + + postSplitAtLine = createdLines[0] + + preSplitAtLine, err := l.Update(UpdateInput{ + PeriodEnd: splitAt, + InvoiceAt: splitAt, + }).Save(ctx) + if err != nil { + return SplitResult{}, fmt.Errorf("updating parent line: %w", err) + } + + return SplitResult{ + PreSplitAtLine: preSplitAtLine, + PostSplitAtLine: postSplitAtLine, + }, nil +} diff --git a/openmeter/billing/service/lineservice/manualfeeline.go b/openmeter/billing/service/lineservice/manualfeeline.go new file mode 100644 index 000000000..457724059 --- /dev/null +++ b/openmeter/billing/service/lineservice/manualfeeline.go @@ -0,0 +1,32 @@ +package lineservice + +import ( + "context" + "time" + + billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" +) + +var _ Line = manualFeeLine{} + +type manualFeeLine struct { + lineBase +} + +func (l manualFeeLine) PrepareForCreate(context.Context) (Line, error) { + return l, nil +} + +func (l manualFeeLine) CanBeInvoicedAsOf(_ context.Context, t time.Time) (*billingentity.Period, error) { + if !t.Before(l.line.InvoiceAt) { + return &l.line.Period, nil + } + + return nil, nil +} + +func (l manualFeeLine) SnapshotQuantity(context.Context, *billingentity.Invoice) (*snapshotQuantityResult, error) { + return &snapshotQuantityResult{ + Line: l, + }, nil +} diff --git a/openmeter/billing/service/lineservice/manualusagebasedline.go b/openmeter/billing/service/lineservice/manualusagebasedline.go new file mode 100644 index 000000000..42db45afd --- /dev/null +++ b/openmeter/billing/service/lineservice/manualusagebasedline.go @@ -0,0 +1,138 @@ +package lineservice + +import ( + "context" + "time" + + "github.com/samber/lo" + + billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" + "github.com/openmeterio/openmeter/pkg/models" +) + +var _ Line = manualUsageBasedLine{} + +type manualUsageBasedLine struct { + lineBase +} + +func (l manualUsageBasedLine) PrepareForCreate(context.Context) (Line, error) { + l.line.Period = l.line.Period.Truncate(billingentity.DefaultMeterResolution) + + return l, nil +} + +func (l manualUsageBasedLine) Validate(ctx context.Context, targetInvoice *billingentity.Invoice) error { + if _, err := l.service.resolveFeatureMeter(ctx, l.line.Namespace, l.line.ManualUsageBased.FeatureKey); err != nil { + return err + } + + if err := l.lineBase.Validate(ctx, targetInvoice); err != nil { + return err + } + + if len(targetInvoice.Customer.Subjects) == 0 { + return billingentity.ValidationError{ + Err: billingentity.ErrInvoiceCreateUBPLineCustomerHasNoSubjects, + } + } + + if l.line.LineBase.Period.Truncate(billingentity.DefaultMeterResolution).IsEmpty() { + return billingentity.ValidationError{ + Err: billingentity.ErrInvoiceCreateUBPLinePeriodIsEmpty, + } + } + + return nil +} + +func (l manualUsageBasedLine) CanBeInvoicedAsOf(ctx context.Context, asof time.Time) (*billingentity.Period, error) { + if l.line.ManualUsageBased.Price.Type() == plan.TieredPriceType { + tiered, err := l.line.ManualUsageBased.Price.AsTiered() + if err != nil { + return nil, err + } + + if tiered.Mode == plan.GraduatedTieredPrice { + // Graduated tiers are only billable if we have all the data acquired, as otherwise + // we might overcharge the customer (if we are at tier bundaries) + if !asof.Before(l.line.InvoiceAt) { + return &l.line.Period, nil + } + return nil, nil + } + } + + meterAndFactory, err := l.service.resolveFeatureMeter(ctx, l.line.Namespace, l.line.ManualUsageBased.FeatureKey) + if err != nil { + return nil, err + } + + meter := meterAndFactory.meter + + asOfTruncated := asof.Truncate(billingentity.DefaultMeterResolution) + + switch meter.Aggregation { + case models.MeterAggregationSum, models.MeterAggregationCount, + models.MeterAggregationMax, models.MeterAggregationUniqueCount: + + periodStartTrucated := l.line.Period.Start.Truncate(billingentity.DefaultMeterResolution) + + if !periodStartTrucated.Before(asOfTruncated) { + return nil, nil + } + + candidatePeriod := billingentity.Period{ + Start: periodStartTrucated, + End: asOfTruncated, + } + + if candidatePeriod.IsEmpty() { + return nil, nil + } + + return &candidatePeriod, nil + default: + // Other types need to be billed arrears truncated by window size + if !asOfTruncated.Before(l.line.InvoiceAt) { + return &l.line.Period, nil + } + return nil, nil + } +} + +func (l manualUsageBasedLine) SnapshotQuantity(ctx context.Context, invoice *billingentity.Invoice) (*snapshotQuantityResult, error) { + featureMeter, err := l.service.resolveFeatureMeter(ctx, l.line.Namespace, l.line.ManualUsageBased.FeatureKey) + if err != nil { + return nil, err + } + + usage, err := l.service.getFeatureUsage(ctx, + getFeatureUsageInput{ + Line: &l.line, + ParentLine: l.line.ParentLine, + Feature: featureMeter.feature, + Meter: featureMeter.meter, + Subjects: invoice.Customer.Subjects, + }, + ) + if err != nil { + return nil, err + } + + // TODO: This is causing overwrite as ManualUsageBased is a pointer + updatedLineEntity := l.line + updatedLineEntity.ManualUsageBased.Quantity = lo.ToPtr(usage.LinePeriodQty) + + updatedLine, err := l.service.FromEntity(updatedLineEntity) + if err != nil { + return nil, err + } + + // TODO[later]: yield detailed lines here + + return &snapshotQuantityResult{ + Line: updatedLine, + }, nil +} diff --git a/openmeter/billing/service/lineservice/meters.go b/openmeter/billing/service/lineservice/meters.go new file mode 100644 index 000000000..08495c8ff --- /dev/null +++ b/openmeter/billing/service/lineservice/meters.go @@ -0,0 +1,131 @@ +package lineservice + +import ( + "context" + "fmt" + "slices" + + "github.com/alpacahq/alpacadecimal" + + billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" + "github.com/openmeterio/openmeter/openmeter/streaming" + "github.com/openmeterio/openmeter/pkg/models" +) + +type getFeatureUsageInput struct { + Line *billingentity.Line + ParentLine *billingentity.Line + Meter models.Meter + Feature feature.Feature + Subjects []string +} + +func (i getFeatureUsageInput) Validate() error { + if i.Line == nil { + return fmt.Errorf("line is required") + } + + if i.Line.ParentLineID != nil && i.ParentLine == nil { + return fmt.Errorf("parent line is required for split lines") + } + + if i.Line.ParentLineID == nil && i.ParentLine != nil { + return fmt.Errorf("parent line is not allowed for non-split lines") + } + + if i.ParentLine != nil { + if i.ParentLine.Status != billingentity.InvoiceLineStatusSplit { + return fmt.Errorf("parent line is not split") + } + } + + if slices.Contains([]models.MeterAggregation{ + models.MeterAggregationAvg, + models.MeterAggregationMin, + }, i.Meter.Aggregation) { + if i.ParentLine != nil { + return fmt.Errorf("aggregation %s is not supported for split lines", i.Meter.Aggregation) + } + } + + if len(i.Subjects) == 0 { + return fmt.Errorf("subjects are required") + } + + return nil +} + +type featureUsageResponse struct { + // LinePeriodQty is the quantity of the usage for the line for the period + LinePeriodQty alpacadecimal.Decimal + // PreLinePeriodQty is the quantity of the usage for the line for the period before the current period + PreLinePeriodQty alpacadecimal.Decimal +} + +func (s *Service) getFeatureUsage(ctx context.Context, in getFeatureUsageInput) (*featureUsageResponse, error) { + // Validation + if err := in.Validate(); err != nil { + return nil, err + } + + meterQueryParams := &streaming.QueryParams{ + Aggregation: in.Meter.Aggregation, + FilterSubject: in.Subjects, + From: &in.Line.Period.Start, + To: &in.Line.Period.End, + } + + if in.Feature.MeterGroupByFilters != nil { + meterQueryParams.FilterGroupBy = map[string][]string{} + for k, v := range in.Feature.MeterGroupByFilters { + meterQueryParams.FilterGroupBy[k] = []string{v} + } + } + + meterValues, err := s.StreamingConnector.QueryMeter( + ctx, + in.Line.Namespace, + in.Meter.Slug, + meterQueryParams, + ) + if err != nil { + return nil, fmt.Errorf("querying line[%s] meter[%s]: %w", in.Line.ID, in.Meter.Slug, err) + } + + res := &featureUsageResponse{ + LinePeriodQty: summarizeMeterQueryRow(meterValues), + } + + // If we are the first line in the split, we don't need to calculate the pre period + if in.ParentLine == nil || in.ParentLine.Period.Start.Equal(in.Line.Period.Start) { + return res, nil + } + + // Let's get the usage for the parent line to calculate the pre period + meterQueryParams.From = &in.ParentLine.Period.Start + + meterValues, err = s.StreamingConnector.QueryMeter( + ctx, + in.Line.Namespace, + in.Meter.Slug, + meterQueryParams, + ) + if err != nil { + return nil, fmt.Errorf("querying parent line[%s] meter[%s]: %w", in.ParentLine.ID, in.Meter.Slug, err) + } + + fullPeriodQty := summarizeMeterQueryRow(meterValues) + res.PreLinePeriodQty = fullPeriodQty.Sub(res.LinePeriodQty) + + return res, nil +} + +func summarizeMeterQueryRow(in []models.MeterQueryRow) alpacadecimal.Decimal { + sum := alpacadecimal.Decimal{} + for _, row := range in { + sum = sum.Add(alpacadecimal.NewFromFloat(row.Value)) + } + + return sum +} diff --git a/openmeter/billing/service/lineservice/service.go b/openmeter/billing/service/lineservice/service.go new file mode 100644 index 000000000..69c732c84 --- /dev/null +++ b/openmeter/billing/service/lineservice/service.go @@ -0,0 +1,224 @@ +package lineservice + +import ( + "context" + "fmt" + "time" + + "github.com/samber/lo" + + "github.com/openmeterio/openmeter/openmeter/billing" + billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + "github.com/openmeterio/openmeter/openmeter/meter" + "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" + "github.com/openmeterio/openmeter/openmeter/streaming" + "github.com/openmeterio/openmeter/pkg/slicesx" +) + +type featureCacheItem struct { + feature feature.Feature + meter meter.Meter +} + +type Service struct { + Config + + featureCache map[string]*featureCacheItem +} + +type Config struct { + BillingAdapter billing.Adapter + FeatureService feature.FeatureConnector + MeterRepo meter.Repository + StreamingConnector streaming.Connector +} + +func (c Config) Validate() error { + if c.BillingAdapter == nil { + return fmt.Errorf("adapter is required") + } + + if c.FeatureService == nil { + return fmt.Errorf("feature service is required") + } + + if c.MeterRepo == nil { + return fmt.Errorf("meter repo is required") + } + + if c.StreamingConnector == nil { + return fmt.Errorf("streaming connector is required") + } + + return nil +} + +func New(in Config) (*Service, error) { + if err := in.Validate(); err != nil { + return nil, err + } + + return &Service{ + Config: in, + + featureCache: make(map[string]*featureCacheItem), + }, nil +} + +func (s *Service) FromEntity(line billingentity.Line) (Line, error) { + base := lineBase{ + service: s, + line: line, + } + + switch line.Type { + case billingentity.InvoiceLineTypeManualFee: + return manualFeeLine{ + lineBase: base, + }, nil + case billingentity.InvoiceLineTypeManualUsageBased: + return manualUsageBasedLine{ + lineBase: base, + }, nil + default: + return nil, fmt.Errorf("unsupported line type: %s", line.Type) + } +} + +func (s *Service) FromEntities(line []billingentity.Line) (Lines, error) { + return slicesx.MapWithErr(line, func(l billingentity.Line) (Line, error) { + return s.FromEntity(l) + }) +} + +func (s *Service) resolveFeatureMeter(ctx context.Context, ns string, featureKey string) (*featureCacheItem, error) { + cacheKey := fmt.Sprintf("%s/%s", ns, featureKey) + // Let's cache the results as we might need to resolve the same feature/meter multiple times (Validate, CanBeInvoicedAsOf, UpdateQty) + if entry, found := s.featureCache[cacheKey]; found { + return entry, nil + } + + feat, err := s.FeatureService.GetFeature( + ctx, + ns, + featureKey, + feature.IncludeArchivedFeatureTrue, + ) + if err != nil { + return nil, fmt.Errorf("fetching feature[%s]: %w", featureKey, err) + } + + if feat.MeterSlug == nil { + return nil, billingentity.ValidationError{ + Err: billingentity.ErrInvoiceLineFeatureHasNoMeters, + } + } + + // let's resolve the underlying meter + meter, err := s.MeterRepo.GetMeterByIDOrSlug(ctx, ns, *feat.MeterSlug) + if err != nil { + return nil, fmt.Errorf("fetching meter[%s]: %w", *feat.MeterSlug, err) + } + + ent := &featureCacheItem{ + feature: *feat, + meter: meter, + } + + s.featureCache[cacheKey] = ent + return ent, nil +} + +func (s *Service) CreateLines(ctx context.Context, lines ...Line) (Lines, error) { + if len(lines) == 0 { + return nil, nil + } + + newLines, err := s.BillingAdapter.CreateInvoiceLines( + ctx, + lo.Map(lines, func(line Line, _ int) billingentity.Line { + entity := line.ToEntity() + return entity + }), + ) + if err != nil { + return nil, fmt.Errorf("creating invoice lines: %w", err) + } + + return slicesx.MapWithErr(newLines.Lines, func(line billingentity.Line) (Line, error) { + return s.FromEntity(line) + }) +} + +func (s *Service) AssociateLinesToInvoice(ctx context.Context, invoice *billingentity.Invoice, lines Lines) (Lines, error) { + lineEntities, err := s.BillingAdapter.AssociateLinesToInvoice(ctx, billing.AssociateLinesToInvoiceAdapterInput{ + Invoice: billingentity.InvoiceID{ + ID: invoice.ID, + Namespace: invoice.Namespace, + }, + + LineIDs: lo.Map(lines, func(l Line, _ int) string { + return l.ID() + }), + }) + if err != nil { + return nil, err + } + + return s.FromEntities(lineEntities) +} + +type snapshotQuantityResult struct { + Line Line + // TODO[later]: Detailed lines should be returned here, that we are upserting based on the qty as described in README.md (see `Detailed Lines vs Splitting`) +} + +type Line interface { + LineBase + + Service() *Service + + Validate(ctx context.Context, invoice *billingentity.Invoice) error + CanBeInvoicedAsOf(context.Context, time.Time) (*billingentity.Period, error) + SnapshotQuantity(context.Context, *billingentity.Invoice) (*snapshotQuantityResult, error) + PrepareForCreate(context.Context) (Line, error) +} + +type Lines []Line + +func (s Lines) ToEntities() []billingentity.Line { + return lo.Map(s, func(service Line, _ int) billingentity.Line { + return service.ToEntity() + }) +} + +type LineWithBillablePeriod struct { + Line + BillablePeriod billingentity.Period +} + +func (s Lines) ResolveBillablePeriod(ctx context.Context, asOf time.Time) ([]LineWithBillablePeriod, error) { + out := make([]LineWithBillablePeriod, 0, len(s)) + for _, lineSrv := range s { + billablePeriod, err := lineSrv.CanBeInvoicedAsOf(ctx, asOf) + if err != nil { + return nil, fmt.Errorf("checking if line can be invoiced: %w", err) + } + + if billablePeriod != nil { + out = append(out, LineWithBillablePeriod{ + Line: lineSrv, + BillablePeriod: *billablePeriod, + }) + } + } + + if len(out) == 0 { + // We haven't requested explicit empty invoice, so we should have some pending lines + return nil, billingentity.ValidationError{ + Err: billingentity.ErrInvoiceCreateNoLines, + } + } + + return out, nil +} diff --git a/openmeter/billing/service/service.go b/openmeter/billing/service/service.go index 293cfae4f..151aaee5d 100644 --- a/openmeter/billing/service/service.go +++ b/openmeter/billing/service/service.go @@ -12,25 +12,34 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/customer" customerentity "github.com/openmeterio/openmeter/openmeter/customer/entity" + "github.com/openmeterio/openmeter/openmeter/meter" + "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" + "github.com/openmeterio/openmeter/openmeter/streaming" "github.com/openmeterio/openmeter/pkg/framework/entutils" ) var _ billing.Service = (*Service)(nil) type Service struct { - adapter billing.Adapter - customerService customer.CustomerService - appService app.Service - logger *slog.Logger - invoiceCalculator InvoiceCalculator + adapter billing.Adapter + customerService customer.CustomerService + appService app.Service + logger *slog.Logger + invoiceCalculator InvoiceCalculator + featureService feature.FeatureConnector + meterRepo meter.Repository + streamingConnector streaming.Connector } type Config struct { - Adapter billing.Adapter - CustomerService customer.CustomerService - AppService app.Service - Logger *slog.Logger - InvoiceCalculator InvoiceCalculator + Adapter billing.Adapter + CustomerService customer.CustomerService + AppService app.Service + Logger *slog.Logger + InvoiceCalculator InvoiceCalculator + FeatureService feature.FeatureConnector + MeterRepo meter.Repository + StreamingConnector streaming.Connector } func (c Config) Validate() error { @@ -50,6 +59,19 @@ func (c Config) Validate() error { return errors.New("logger cannot be null") } + // TODO: breaking, we need to update main and cloud accordingly + if c.FeatureService == nil { + return errors.New("feature connector cannot be null") + } + + if c.MeterRepo == nil { + return errors.New("meter repo cannot be null") + } + + if c.StreamingConnector == nil { + return errors.New("streaming connector cannot be null") + } + return nil } @@ -59,11 +81,14 @@ func New(config Config) (*Service, error) { } return &Service{ - adapter: config.Adapter, - customerService: config.CustomerService, - appService: config.AppService, - logger: config.Logger, - invoiceCalculator: lo.CoalesceOrEmpty(config.InvoiceCalculator, NewInvoiceCalculator()), + adapter: config.Adapter, + customerService: config.CustomerService, + appService: config.AppService, + logger: config.Logger, + invoiceCalculator: lo.CoalesceOrEmpty(config.InvoiceCalculator, NewInvoiceCalculator()), + featureService: config.FeatureService, + meterRepo: config.MeterRepo, + streamingConnector: config.StreamingConnector, }, nil } diff --git a/openmeter/ent/db/billinginvoice.go b/openmeter/ent/db/billinginvoice.go index c0ebf7d8e..825f56ce1 100644 --- a/openmeter/ent/db/billinginvoice.go +++ b/openmeter/ent/db/billinginvoice.go @@ -72,6 +72,8 @@ type BillingInvoice struct { CustomerName string `json:"customer_name,omitempty"` // CustomerTimezone holds the value of the "customer_timezone" field. CustomerTimezone *timezone.Timezone `json:"customer_timezone,omitempty"` + // CustomerSubjectKeys holds the value of the "customer_subject_keys" field. + CustomerSubjectKeys []string `json:"customer_subject_keys,omitempty"` // Number holds the value of the "number" field. Number *string `json:"number,omitempty"` // Type holds the value of the "type" field. @@ -224,7 +226,7 @@ func (*BillingInvoice) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { - case billinginvoice.FieldMetadata: + case billinginvoice.FieldMetadata, billinginvoice.FieldCustomerSubjectKeys: values[i] = new([]byte) case billinginvoice.FieldID, billinginvoice.FieldNamespace, billinginvoice.FieldSupplierAddressCountry, billinginvoice.FieldSupplierAddressPostalCode, billinginvoice.FieldSupplierAddressState, billinginvoice.FieldSupplierAddressCity, billinginvoice.FieldSupplierAddressLine1, billinginvoice.FieldSupplierAddressLine2, billinginvoice.FieldSupplierAddressPhoneNumber, billinginvoice.FieldCustomerAddressCountry, billinginvoice.FieldCustomerAddressPostalCode, billinginvoice.FieldCustomerAddressState, billinginvoice.FieldCustomerAddressCity, billinginvoice.FieldCustomerAddressLine1, billinginvoice.FieldCustomerAddressLine2, billinginvoice.FieldCustomerAddressPhoneNumber, billinginvoice.FieldSupplierName, billinginvoice.FieldSupplierTaxCode, billinginvoice.FieldCustomerName, billinginvoice.FieldCustomerTimezone, billinginvoice.FieldNumber, billinginvoice.FieldType, billinginvoice.FieldDescription, billinginvoice.FieldCustomerID, billinginvoice.FieldSourceBillingProfileID, billinginvoice.FieldCurrency, billinginvoice.FieldStatus, billinginvoice.FieldWorkflowConfigID, billinginvoice.FieldTaxAppID, billinginvoice.FieldInvoicingAppID, billinginvoice.FieldPaymentAppID: values[i] = new(sql.NullString) @@ -408,6 +410,14 @@ func (bi *BillingInvoice) assignValues(columns []string, values []any) error { bi.CustomerTimezone = new(timezone.Timezone) *bi.CustomerTimezone = timezone.Timezone(value.String) } + case billinginvoice.FieldCustomerSubjectKeys: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field customer_subject_keys", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &bi.CustomerSubjectKeys); err != nil { + return fmt.Errorf("unmarshal field customer_subject_keys: %w", err) + } + } case billinginvoice.FieldNumber: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field number", values[i]) @@ -697,6 +707,9 @@ func (bi *BillingInvoice) String() string { builder.WriteString(fmt.Sprintf("%v", *v)) } builder.WriteString(", ") + builder.WriteString("customer_subject_keys=") + builder.WriteString(fmt.Sprintf("%v", bi.CustomerSubjectKeys)) + builder.WriteString(", ") if v := bi.Number; v != nil { builder.WriteString("number=") builder.WriteString(*v) diff --git a/openmeter/ent/db/billinginvoice/billinginvoice.go b/openmeter/ent/db/billinginvoice/billinginvoice.go index 972213ea1..b02c6be73 100644 --- a/openmeter/ent/db/billinginvoice/billinginvoice.go +++ b/openmeter/ent/db/billinginvoice/billinginvoice.go @@ -62,6 +62,8 @@ const ( FieldCustomerName = "customer_name" // FieldCustomerTimezone holds the string denoting the customer_timezone field in the database. FieldCustomerTimezone = "customer_timezone" + // FieldCustomerSubjectKeys holds the string denoting the customer_subject_keys field in the database. + FieldCustomerSubjectKeys = "customer_subject_keys" // FieldNumber holds the string denoting the number field in the database. FieldNumber = "number" // FieldType holds the string denoting the type field in the database. @@ -198,6 +200,7 @@ var Columns = []string{ FieldSupplierTaxCode, FieldCustomerName, FieldCustomerTimezone, + FieldCustomerSubjectKeys, FieldNumber, FieldType, FieldDescription, diff --git a/openmeter/ent/db/billinginvoice/where.go b/openmeter/ent/db/billinginvoice/where.go index b1c447dc4..efc291a9c 100644 --- a/openmeter/ent/db/billinginvoice/where.go +++ b/openmeter/ent/db/billinginvoice/where.go @@ -1850,6 +1850,16 @@ func CustomerTimezoneContainsFold(v timezone.Timezone) predicate.BillingInvoice return predicate.BillingInvoice(sql.FieldContainsFold(FieldCustomerTimezone, vc)) } +// CustomerSubjectKeysIsNil applies the IsNil predicate on the "customer_subject_keys" field. +func CustomerSubjectKeysIsNil() predicate.BillingInvoice { + return predicate.BillingInvoice(sql.FieldIsNull(FieldCustomerSubjectKeys)) +} + +// CustomerSubjectKeysNotNil applies the NotNil predicate on the "customer_subject_keys" field. +func CustomerSubjectKeysNotNil() predicate.BillingInvoice { + return predicate.BillingInvoice(sql.FieldNotNull(FieldCustomerSubjectKeys)) +} + // NumberEQ applies the EQ predicate on the "number" field. func NumberEQ(v string) predicate.BillingInvoice { return predicate.BillingInvoice(sql.FieldEQ(FieldNumber, v)) diff --git a/openmeter/ent/db/billinginvoice_create.go b/openmeter/ent/db/billinginvoice_create.go index a43d6f079..28b6cb25d 100644 --- a/openmeter/ent/db/billinginvoice_create.go +++ b/openmeter/ent/db/billinginvoice_create.go @@ -323,6 +323,12 @@ func (bic *BillingInvoiceCreate) SetNillableCustomerTimezone(t *timezone.Timezon return bic } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (bic *BillingInvoiceCreate) SetCustomerSubjectKeys(s []string) *BillingInvoiceCreate { + bic.mutation.SetCustomerSubjectKeys(s) + return bic +} + // SetNumber sets the "number" field. func (bic *BillingInvoiceCreate) SetNumber(s string) *BillingInvoiceCreate { bic.mutation.SetNumber(s) @@ -869,6 +875,10 @@ func (bic *BillingInvoiceCreate) createSpec() (*BillingInvoice, *sqlgraph.Create _spec.SetField(billinginvoice.FieldCustomerTimezone, field.TypeString, value) _node.CustomerTimezone = &value } + if value, ok := bic.mutation.CustomerSubjectKeys(); ok { + _spec.SetField(billinginvoice.FieldCustomerSubjectKeys, field.TypeJSON, value) + _node.CustomerSubjectKeys = value + } if value, ok := bic.mutation.Number(); ok { _spec.SetField(billinginvoice.FieldNumber, field.TypeString, value) _node.Number = &value @@ -1459,6 +1469,24 @@ func (u *BillingInvoiceUpsert) ClearCustomerTimezone() *BillingInvoiceUpsert { return u } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (u *BillingInvoiceUpsert) SetCustomerSubjectKeys(v []string) *BillingInvoiceUpsert { + u.Set(billinginvoice.FieldCustomerSubjectKeys, v) + return u +} + +// UpdateCustomerSubjectKeys sets the "customer_subject_keys" field to the value that was provided on create. +func (u *BillingInvoiceUpsert) UpdateCustomerSubjectKeys() *BillingInvoiceUpsert { + u.SetExcluded(billinginvoice.FieldCustomerSubjectKeys) + return u +} + +// ClearCustomerSubjectKeys clears the value of the "customer_subject_keys" field. +func (u *BillingInvoiceUpsert) ClearCustomerSubjectKeys() *BillingInvoiceUpsert { + u.SetNull(billinginvoice.FieldCustomerSubjectKeys) + return u +} + // SetNumber sets the "number" field. func (u *BillingInvoiceUpsert) SetNumber(v string) *BillingInvoiceUpsert { u.Set(billinginvoice.FieldNumber, v) @@ -2131,6 +2159,27 @@ func (u *BillingInvoiceUpsertOne) ClearCustomerTimezone() *BillingInvoiceUpsertO }) } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (u *BillingInvoiceUpsertOne) SetCustomerSubjectKeys(v []string) *BillingInvoiceUpsertOne { + return u.Update(func(s *BillingInvoiceUpsert) { + s.SetCustomerSubjectKeys(v) + }) +} + +// UpdateCustomerSubjectKeys sets the "customer_subject_keys" field to the value that was provided on create. +func (u *BillingInvoiceUpsertOne) UpdateCustomerSubjectKeys() *BillingInvoiceUpsertOne { + return u.Update(func(s *BillingInvoiceUpsert) { + s.UpdateCustomerSubjectKeys() + }) +} + +// ClearCustomerSubjectKeys clears the value of the "customer_subject_keys" field. +func (u *BillingInvoiceUpsertOne) ClearCustomerSubjectKeys() *BillingInvoiceUpsertOne { + return u.Update(func(s *BillingInvoiceUpsert) { + s.ClearCustomerSubjectKeys() + }) +} + // SetNumber sets the "number" field. func (u *BillingInvoiceUpsertOne) SetNumber(v string) *BillingInvoiceUpsertOne { return u.Update(func(s *BillingInvoiceUpsert) { @@ -3000,6 +3049,27 @@ func (u *BillingInvoiceUpsertBulk) ClearCustomerTimezone() *BillingInvoiceUpsert }) } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (u *BillingInvoiceUpsertBulk) SetCustomerSubjectKeys(v []string) *BillingInvoiceUpsertBulk { + return u.Update(func(s *BillingInvoiceUpsert) { + s.SetCustomerSubjectKeys(v) + }) +} + +// UpdateCustomerSubjectKeys sets the "customer_subject_keys" field to the value that was provided on create. +func (u *BillingInvoiceUpsertBulk) UpdateCustomerSubjectKeys() *BillingInvoiceUpsertBulk { + return u.Update(func(s *BillingInvoiceUpsert) { + s.UpdateCustomerSubjectKeys() + }) +} + +// ClearCustomerSubjectKeys clears the value of the "customer_subject_keys" field. +func (u *BillingInvoiceUpsertBulk) ClearCustomerSubjectKeys() *BillingInvoiceUpsertBulk { + return u.Update(func(s *BillingInvoiceUpsert) { + s.ClearCustomerSubjectKeys() + }) +} + // SetNumber sets the "number" field. func (u *BillingInvoiceUpsertBulk) SetNumber(v string) *BillingInvoiceUpsertBulk { return u.Update(func(s *BillingInvoiceUpsert) { diff --git a/openmeter/ent/db/billinginvoice_update.go b/openmeter/ent/db/billinginvoice_update.go index c4c1e2334..6bb427698 100644 --- a/openmeter/ent/db/billinginvoice_update.go +++ b/openmeter/ent/db/billinginvoice_update.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/dialect/sql/sqljson" "entgo.io/ent/schema/field" billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" @@ -420,6 +421,24 @@ func (biu *BillingInvoiceUpdate) ClearCustomerTimezone() *BillingInvoiceUpdate { return biu } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (biu *BillingInvoiceUpdate) SetCustomerSubjectKeys(s []string) *BillingInvoiceUpdate { + biu.mutation.SetCustomerSubjectKeys(s) + return biu +} + +// AppendCustomerSubjectKeys appends s to the "customer_subject_keys" field. +func (biu *BillingInvoiceUpdate) AppendCustomerSubjectKeys(s []string) *BillingInvoiceUpdate { + biu.mutation.AppendCustomerSubjectKeys(s) + return biu +} + +// ClearCustomerSubjectKeys clears the value of the "customer_subject_keys" field. +func (biu *BillingInvoiceUpdate) ClearCustomerSubjectKeys() *BillingInvoiceUpdate { + biu.mutation.ClearCustomerSubjectKeys() + return biu +} + // SetNumber sets the "number" field. func (biu *BillingInvoiceUpdate) SetNumber(s string) *BillingInvoiceUpdate { biu.mutation.SetNumber(s) @@ -939,6 +958,17 @@ func (biu *BillingInvoiceUpdate) sqlSave(ctx context.Context) (n int, err error) if biu.mutation.CustomerTimezoneCleared() { _spec.ClearField(billinginvoice.FieldCustomerTimezone, field.TypeString) } + if value, ok := biu.mutation.CustomerSubjectKeys(); ok { + _spec.SetField(billinginvoice.FieldCustomerSubjectKeys, field.TypeJSON, value) + } + if value, ok := biu.mutation.AppendedCustomerSubjectKeys(); ok { + _spec.AddModifier(func(u *sql.UpdateBuilder) { + sqljson.Append(u, billinginvoice.FieldCustomerSubjectKeys, value) + }) + } + if biu.mutation.CustomerSubjectKeysCleared() { + _spec.ClearField(billinginvoice.FieldCustomerSubjectKeys, field.TypeJSON) + } if value, ok := biu.mutation.Number(); ok { _spec.SetField(billinginvoice.FieldNumber, field.TypeString, value) } @@ -1518,6 +1548,24 @@ func (biuo *BillingInvoiceUpdateOne) ClearCustomerTimezone() *BillingInvoiceUpda return biuo } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (biuo *BillingInvoiceUpdateOne) SetCustomerSubjectKeys(s []string) *BillingInvoiceUpdateOne { + biuo.mutation.SetCustomerSubjectKeys(s) + return biuo +} + +// AppendCustomerSubjectKeys appends s to the "customer_subject_keys" field. +func (biuo *BillingInvoiceUpdateOne) AppendCustomerSubjectKeys(s []string) *BillingInvoiceUpdateOne { + biuo.mutation.AppendCustomerSubjectKeys(s) + return biuo +} + +// ClearCustomerSubjectKeys clears the value of the "customer_subject_keys" field. +func (biuo *BillingInvoiceUpdateOne) ClearCustomerSubjectKeys() *BillingInvoiceUpdateOne { + biuo.mutation.ClearCustomerSubjectKeys() + return biuo +} + // SetNumber sets the "number" field. func (biuo *BillingInvoiceUpdateOne) SetNumber(s string) *BillingInvoiceUpdateOne { biuo.mutation.SetNumber(s) @@ -2067,6 +2115,17 @@ func (biuo *BillingInvoiceUpdateOne) sqlSave(ctx context.Context) (_node *Billin if biuo.mutation.CustomerTimezoneCleared() { _spec.ClearField(billinginvoice.FieldCustomerTimezone, field.TypeString) } + if value, ok := biuo.mutation.CustomerSubjectKeys(); ok { + _spec.SetField(billinginvoice.FieldCustomerSubjectKeys, field.TypeJSON, value) + } + if value, ok := biuo.mutation.AppendedCustomerSubjectKeys(); ok { + _spec.AddModifier(func(u *sql.UpdateBuilder) { + sqljson.Append(u, billinginvoice.FieldCustomerSubjectKeys, value) + }) + } + if biuo.mutation.CustomerSubjectKeysCleared() { + _spec.ClearField(billinginvoice.FieldCustomerSubjectKeys, field.TypeJSON) + } if value, ok := biuo.mutation.Number(); ok { _spec.SetField(billinginvoice.FieldNumber, field.TypeString, value) } diff --git a/openmeter/ent/db/billinginvoiceline.go b/openmeter/ent/db/billinginvoiceline.go index 125074388..8a61b7e06 100644 --- a/openmeter/ent/db/billinginvoiceline.go +++ b/openmeter/ent/db/billinginvoiceline.go @@ -15,6 +15,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/pkg/currencyx" ) @@ -39,6 +40,8 @@ type BillingInvoiceLine struct { Description *string `json:"description,omitempty"` // InvoiceID holds the value of the "invoice_id" field. InvoiceID string `json:"invoice_id,omitempty"` + // ParentLineID holds the value of the "parent_line_id" field. + ParentLineID *string `json:"parent_line_id,omitempty"` // PeriodStart holds the value of the "period_start" field. PeriodStart time.Time `json:"period_start,omitempty"` // PeriodEnd holds the value of the "period_end" field. @@ -57,20 +60,27 @@ type BillingInvoiceLine struct { TaxOverrides *billingentity.TaxOverrides `json:"tax_overrides,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the BillingInvoiceLineQuery when eager-loading is set. - Edges BillingInvoiceLineEdges `json:"edges"` - manual_line_config_id *string - selectValues sql.SelectValues + Edges BillingInvoiceLineEdges `json:"edges"` + manual_line_config_id *string + manual_usage_based_line_config_id *string + selectValues sql.SelectValues } // BillingInvoiceLineEdges holds the relations/edges for other nodes in the graph. type BillingInvoiceLineEdges struct { // BillingInvoice holds the value of the billing_invoice edge. BillingInvoice *BillingInvoice `json:"billing_invoice,omitempty"` - // BillingInvoiceManualLines holds the value of the billing_invoice_manual_lines edge. - BillingInvoiceManualLines *BillingInvoiceManualLineConfig `json:"billing_invoice_manual_lines,omitempty"` + // ManualFeeLine holds the value of the manual_fee_line edge. + ManualFeeLine *BillingInvoiceManualLineConfig `json:"manual_fee_line,omitempty"` + // ManualUsageBasedLine holds the value of the manual_usage_based_line edge. + ManualUsageBasedLine *BillingInvoiceManualUsageBasedLineConfig `json:"manual_usage_based_line,omitempty"` + // ParentLine holds the value of the parent_line edge. + ParentLine *BillingInvoiceLine `json:"parent_line,omitempty"` + // ChildLines holds the value of the child_lines edge. + ChildLines []*BillingInvoiceLine `json:"child_lines,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [2]bool + loadedTypes [5]bool } // BillingInvoiceOrErr returns the BillingInvoice value or an error if the edge @@ -84,15 +94,46 @@ func (e BillingInvoiceLineEdges) BillingInvoiceOrErr() (*BillingInvoice, error) return nil, &NotLoadedError{edge: "billing_invoice"} } -// BillingInvoiceManualLinesOrErr returns the BillingInvoiceManualLines value or an error if the edge +// ManualFeeLineOrErr returns the ManualFeeLine value or an error if the edge // was not loaded in eager-loading, or loaded but was not found. -func (e BillingInvoiceLineEdges) BillingInvoiceManualLinesOrErr() (*BillingInvoiceManualLineConfig, error) { - if e.BillingInvoiceManualLines != nil { - return e.BillingInvoiceManualLines, nil +func (e BillingInvoiceLineEdges) ManualFeeLineOrErr() (*BillingInvoiceManualLineConfig, error) { + if e.ManualFeeLine != nil { + return e.ManualFeeLine, nil } else if e.loadedTypes[1] { return nil, &NotFoundError{label: billinginvoicemanuallineconfig.Label} } - return nil, &NotLoadedError{edge: "billing_invoice_manual_lines"} + return nil, &NotLoadedError{edge: "manual_fee_line"} +} + +// ManualUsageBasedLineOrErr returns the ManualUsageBasedLine value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e BillingInvoiceLineEdges) ManualUsageBasedLineOrErr() (*BillingInvoiceManualUsageBasedLineConfig, error) { + if e.ManualUsageBasedLine != nil { + return e.ManualUsageBasedLine, nil + } else if e.loadedTypes[2] { + return nil, &NotFoundError{label: billinginvoicemanualusagebasedlineconfig.Label} + } + return nil, &NotLoadedError{edge: "manual_usage_based_line"} +} + +// ParentLineOrErr returns the ParentLine value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e BillingInvoiceLineEdges) ParentLineOrErr() (*BillingInvoiceLine, error) { + if e.ParentLine != nil { + return e.ParentLine, nil + } else if e.loadedTypes[3] { + return nil, &NotFoundError{label: billinginvoiceline.Label} + } + return nil, &NotLoadedError{edge: "parent_line"} +} + +// ChildLinesOrErr returns the ChildLines value or an error if the edge +// was not loaded in eager-loading. +func (e BillingInvoiceLineEdges) ChildLinesOrErr() ([]*BillingInvoiceLine, error) { + if e.loadedTypes[4] { + return e.ChildLines, nil + } + return nil, &NotLoadedError{edge: "child_lines"} } // scanValues returns the types for scanning values from sql.Rows. @@ -104,12 +145,14 @@ func (*BillingInvoiceLine) scanValues(columns []string) ([]any, error) { values[i] = &sql.NullScanner{S: new(alpacadecimal.Decimal)} case billinginvoiceline.FieldMetadata, billinginvoiceline.FieldTaxOverrides: values[i] = new([]byte) - case billinginvoiceline.FieldID, billinginvoiceline.FieldNamespace, billinginvoiceline.FieldName, billinginvoiceline.FieldDescription, billinginvoiceline.FieldInvoiceID, billinginvoiceline.FieldType, billinginvoiceline.FieldStatus, billinginvoiceline.FieldCurrency: + case billinginvoiceline.FieldID, billinginvoiceline.FieldNamespace, billinginvoiceline.FieldName, billinginvoiceline.FieldDescription, billinginvoiceline.FieldInvoiceID, billinginvoiceline.FieldParentLineID, billinginvoiceline.FieldType, billinginvoiceline.FieldStatus, billinginvoiceline.FieldCurrency: values[i] = new(sql.NullString) case billinginvoiceline.FieldCreatedAt, billinginvoiceline.FieldUpdatedAt, billinginvoiceline.FieldDeletedAt, billinginvoiceline.FieldPeriodStart, billinginvoiceline.FieldPeriodEnd, billinginvoiceline.FieldInvoiceAt: values[i] = new(sql.NullTime) case billinginvoiceline.ForeignKeys[0]: // manual_line_config_id values[i] = new(sql.NullString) + case billinginvoiceline.ForeignKeys[1]: // manual_usage_based_line_config_id + values[i] = new(sql.NullString) default: values[i] = new(sql.UnknownType) } @@ -183,6 +226,13 @@ func (bil *BillingInvoiceLine) assignValues(columns []string, values []any) erro } else if value.Valid { bil.InvoiceID = value.String } + case billinginvoiceline.FieldParentLineID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field parent_line_id", values[i]) + } else if value.Valid { + bil.ParentLineID = new(string) + *bil.ParentLineID = value.String + } case billinginvoiceline.FieldPeriodStart: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field period_start", values[i]) @@ -241,6 +291,13 @@ func (bil *BillingInvoiceLine) assignValues(columns []string, values []any) erro bil.manual_line_config_id = new(string) *bil.manual_line_config_id = value.String } + case billinginvoiceline.ForeignKeys[1]: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field manual_usage_based_line_config_id", values[i]) + } else if value.Valid { + bil.manual_usage_based_line_config_id = new(string) + *bil.manual_usage_based_line_config_id = value.String + } default: bil.selectValues.Set(columns[i], values[i]) } @@ -259,9 +316,24 @@ func (bil *BillingInvoiceLine) QueryBillingInvoice() *BillingInvoiceQuery { return NewBillingInvoiceLineClient(bil.config).QueryBillingInvoice(bil) } -// QueryBillingInvoiceManualLines queries the "billing_invoice_manual_lines" edge of the BillingInvoiceLine entity. -func (bil *BillingInvoiceLine) QueryBillingInvoiceManualLines() *BillingInvoiceManualLineConfigQuery { - return NewBillingInvoiceLineClient(bil.config).QueryBillingInvoiceManualLines(bil) +// QueryManualFeeLine queries the "manual_fee_line" edge of the BillingInvoiceLine entity. +func (bil *BillingInvoiceLine) QueryManualFeeLine() *BillingInvoiceManualLineConfigQuery { + return NewBillingInvoiceLineClient(bil.config).QueryManualFeeLine(bil) +} + +// QueryManualUsageBasedLine queries the "manual_usage_based_line" edge of the BillingInvoiceLine entity. +func (bil *BillingInvoiceLine) QueryManualUsageBasedLine() *BillingInvoiceManualUsageBasedLineConfigQuery { + return NewBillingInvoiceLineClient(bil.config).QueryManualUsageBasedLine(bil) +} + +// QueryParentLine queries the "parent_line" edge of the BillingInvoiceLine entity. +func (bil *BillingInvoiceLine) QueryParentLine() *BillingInvoiceLineQuery { + return NewBillingInvoiceLineClient(bil.config).QueryParentLine(bil) +} + +// QueryChildLines queries the "child_lines" edge of the BillingInvoiceLine entity. +func (bil *BillingInvoiceLine) QueryChildLines() *BillingInvoiceLineQuery { + return NewBillingInvoiceLineClient(bil.config).QueryChildLines(bil) } // Update returns a builder for updating this BillingInvoiceLine. @@ -315,6 +387,11 @@ func (bil *BillingInvoiceLine) String() string { builder.WriteString("invoice_id=") builder.WriteString(bil.InvoiceID) builder.WriteString(", ") + if v := bil.ParentLineID; v != nil { + builder.WriteString("parent_line_id=") + builder.WriteString(*v) + } + builder.WriteString(", ") builder.WriteString("period_start=") builder.WriteString(bil.PeriodStart.Format(time.ANSIC)) builder.WriteString(", ") diff --git a/openmeter/ent/db/billinginvoiceline/billinginvoiceline.go b/openmeter/ent/db/billinginvoiceline/billinginvoiceline.go index 7d48882dd..7330ba9e8 100644 --- a/openmeter/ent/db/billinginvoiceline/billinginvoiceline.go +++ b/openmeter/ent/db/billinginvoiceline/billinginvoiceline.go @@ -32,6 +32,8 @@ const ( FieldDescription = "description" // FieldInvoiceID holds the string denoting the invoice_id field in the database. FieldInvoiceID = "invoice_id" + // FieldParentLineID holds the string denoting the parent_line_id field in the database. + FieldParentLineID = "parent_line_id" // FieldPeriodStart holds the string denoting the period_start field in the database. FieldPeriodStart = "period_start" // FieldPeriodEnd holds the string denoting the period_end field in the database. @@ -50,8 +52,14 @@ const ( FieldTaxOverrides = "tax_overrides" // EdgeBillingInvoice holds the string denoting the billing_invoice edge name in mutations. EdgeBillingInvoice = "billing_invoice" - // EdgeBillingInvoiceManualLines holds the string denoting the billing_invoice_manual_lines edge name in mutations. - EdgeBillingInvoiceManualLines = "billing_invoice_manual_lines" + // EdgeManualFeeLine holds the string denoting the manual_fee_line edge name in mutations. + EdgeManualFeeLine = "manual_fee_line" + // EdgeManualUsageBasedLine holds the string denoting the manual_usage_based_line edge name in mutations. + EdgeManualUsageBasedLine = "manual_usage_based_line" + // EdgeParentLine holds the string denoting the parent_line edge name in mutations. + EdgeParentLine = "parent_line" + // EdgeChildLines holds the string denoting the child_lines edge name in mutations. + EdgeChildLines = "child_lines" // Table holds the table name of the billinginvoiceline in the database. Table = "billing_invoice_lines" // BillingInvoiceTable is the table that holds the billing_invoice relation/edge. @@ -61,13 +69,28 @@ const ( BillingInvoiceInverseTable = "billing_invoices" // BillingInvoiceColumn is the table column denoting the billing_invoice relation/edge. BillingInvoiceColumn = "invoice_id" - // BillingInvoiceManualLinesTable is the table that holds the billing_invoice_manual_lines relation/edge. - BillingInvoiceManualLinesTable = "billing_invoice_lines" - // BillingInvoiceManualLinesInverseTable is the table name for the BillingInvoiceManualLineConfig entity. + // ManualFeeLineTable is the table that holds the manual_fee_line relation/edge. + ManualFeeLineTable = "billing_invoice_lines" + // ManualFeeLineInverseTable is the table name for the BillingInvoiceManualLineConfig entity. // It exists in this package in order to avoid circular dependency with the "billinginvoicemanuallineconfig" package. - BillingInvoiceManualLinesInverseTable = "billing_invoice_manual_line_configs" - // BillingInvoiceManualLinesColumn is the table column denoting the billing_invoice_manual_lines relation/edge. - BillingInvoiceManualLinesColumn = "manual_line_config_id" + ManualFeeLineInverseTable = "billing_invoice_manual_line_configs" + // ManualFeeLineColumn is the table column denoting the manual_fee_line relation/edge. + ManualFeeLineColumn = "manual_line_config_id" + // ManualUsageBasedLineTable is the table that holds the manual_usage_based_line relation/edge. + ManualUsageBasedLineTable = "billing_invoice_lines" + // ManualUsageBasedLineInverseTable is the table name for the BillingInvoiceManualUsageBasedLineConfig entity. + // It exists in this package in order to avoid circular dependency with the "billinginvoicemanualusagebasedlineconfig" package. + ManualUsageBasedLineInverseTable = "billing_invoice_manual_usage_based_line_configs" + // ManualUsageBasedLineColumn is the table column denoting the manual_usage_based_line relation/edge. + ManualUsageBasedLineColumn = "manual_usage_based_line_config_id" + // ParentLineTable is the table that holds the parent_line relation/edge. + ParentLineTable = "billing_invoice_lines" + // ParentLineColumn is the table column denoting the parent_line relation/edge. + ParentLineColumn = "parent_line_id" + // ChildLinesTable is the table that holds the child_lines relation/edge. + ChildLinesTable = "billing_invoice_lines" + // ChildLinesColumn is the table column denoting the child_lines relation/edge. + ChildLinesColumn = "parent_line_id" ) // Columns holds all SQL columns for billinginvoiceline fields. @@ -81,6 +104,7 @@ var Columns = []string{ FieldName, FieldDescription, FieldInvoiceID, + FieldParentLineID, FieldPeriodStart, FieldPeriodEnd, FieldInvoiceAt, @@ -95,6 +119,7 @@ var Columns = []string{ // table and are not defined as standalone fields in the schema. var ForeignKeys = []string{ "manual_line_config_id", + "manual_usage_based_line_config_id", } // ValidColumn reports if the column name is valid (part of the table columns). @@ -130,7 +155,7 @@ var ( // TypeValidator is a validator for the "type" field enum values. It is called by the builders before save. func TypeValidator(_type billingentity.InvoiceLineType) error { switch _type { - case "manual_fee", "flat_fee", "usage_based": + case "manual_fee", "manual_usage_based", "flat_fee", "usage_based": return nil default: return fmt.Errorf("billinginvoiceline: invalid enum value for type field: %q", _type) @@ -190,6 +215,11 @@ func ByInvoiceID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldInvoiceID, opts...).ToFunc() } +// ByParentLineID orders the results by the parent_line_id field. +func ByParentLineID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldParentLineID, opts...).ToFunc() +} + // ByPeriodStart orders the results by the period_start field. func ByPeriodStart(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldPeriodStart, opts...).ToFunc() @@ -232,10 +262,38 @@ func ByBillingInvoiceField(field string, opts ...sql.OrderTermOption) OrderOptio } } -// ByBillingInvoiceManualLinesField orders the results by billing_invoice_manual_lines field. -func ByBillingInvoiceManualLinesField(field string, opts ...sql.OrderTermOption) OrderOption { +// ByManualFeeLineField orders the results by manual_fee_line field. +func ByManualFeeLineField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newManualFeeLineStep(), sql.OrderByField(field, opts...)) + } +} + +// ByManualUsageBasedLineField orders the results by manual_usage_based_line field. +func ByManualUsageBasedLineField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newManualUsageBasedLineStep(), sql.OrderByField(field, opts...)) + } +} + +// ByParentLineField orders the results by parent_line field. +func ByParentLineField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newParentLineStep(), sql.OrderByField(field, opts...)) + } +} + +// ByChildLinesCount orders the results by child_lines count. +func ByChildLinesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newChildLinesStep(), opts...) + } +} + +// ByChildLines orders the results by child_lines terms. +func ByChildLines(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { return func(s *sql.Selector) { - sqlgraph.OrderByNeighborTerms(s, newBillingInvoiceManualLinesStep(), sql.OrderByField(field, opts...)) + sqlgraph.OrderByNeighborTerms(s, newChildLinesStep(), append([]sql.OrderTerm{term}, terms...)...) } } func newBillingInvoiceStep() *sqlgraph.Step { @@ -245,10 +303,31 @@ func newBillingInvoiceStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, BillingInvoiceTable, BillingInvoiceColumn), ) } -func newBillingInvoiceManualLinesStep() *sqlgraph.Step { +func newManualFeeLineStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ManualFeeLineInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ManualFeeLineTable, ManualFeeLineColumn), + ) +} +func newManualUsageBasedLineStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ManualUsageBasedLineInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ManualUsageBasedLineTable, ManualUsageBasedLineColumn), + ) +} +func newParentLineStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentLineTable, ParentLineColumn), + ) +} +func newChildLinesStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), - sqlgraph.To(BillingInvoiceManualLinesInverseTable, FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, BillingInvoiceManualLinesTable, BillingInvoiceManualLinesColumn), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildLinesTable, ChildLinesColumn), ) } diff --git a/openmeter/ent/db/billinginvoiceline/where.go b/openmeter/ent/db/billinginvoiceline/where.go index 7b52876e9..bea1c84b9 100644 --- a/openmeter/ent/db/billinginvoiceline/where.go +++ b/openmeter/ent/db/billinginvoiceline/where.go @@ -103,6 +103,11 @@ func InvoiceID(v string) predicate.BillingInvoiceLine { return predicate.BillingInvoiceLine(sql.FieldEQ(FieldInvoiceID, v)) } +// ParentLineID applies equality check predicate on the "parent_line_id" field. It's identical to ParentLineIDEQ. +func ParentLineID(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldEQ(FieldParentLineID, v)) +} + // PeriodStart applies equality check predicate on the "period_start" field. It's identical to PeriodStartEQ. func PeriodStart(v time.Time) predicate.BillingInvoiceLine { return predicate.BillingInvoiceLine(sql.FieldEQ(FieldPeriodStart, v)) @@ -539,6 +544,81 @@ func InvoiceIDContainsFold(v string) predicate.BillingInvoiceLine { return predicate.BillingInvoiceLine(sql.FieldContainsFold(FieldInvoiceID, v)) } +// ParentLineIDEQ applies the EQ predicate on the "parent_line_id" field. +func ParentLineIDEQ(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldEQ(FieldParentLineID, v)) +} + +// ParentLineIDNEQ applies the NEQ predicate on the "parent_line_id" field. +func ParentLineIDNEQ(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldNEQ(FieldParentLineID, v)) +} + +// ParentLineIDIn applies the In predicate on the "parent_line_id" field. +func ParentLineIDIn(vs ...string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldIn(FieldParentLineID, vs...)) +} + +// ParentLineIDNotIn applies the NotIn predicate on the "parent_line_id" field. +func ParentLineIDNotIn(vs ...string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldNotIn(FieldParentLineID, vs...)) +} + +// ParentLineIDGT applies the GT predicate on the "parent_line_id" field. +func ParentLineIDGT(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldGT(FieldParentLineID, v)) +} + +// ParentLineIDGTE applies the GTE predicate on the "parent_line_id" field. +func ParentLineIDGTE(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldGTE(FieldParentLineID, v)) +} + +// ParentLineIDLT applies the LT predicate on the "parent_line_id" field. +func ParentLineIDLT(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldLT(FieldParentLineID, v)) +} + +// ParentLineIDLTE applies the LTE predicate on the "parent_line_id" field. +func ParentLineIDLTE(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldLTE(FieldParentLineID, v)) +} + +// ParentLineIDContains applies the Contains predicate on the "parent_line_id" field. +func ParentLineIDContains(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldContains(FieldParentLineID, v)) +} + +// ParentLineIDHasPrefix applies the HasPrefix predicate on the "parent_line_id" field. +func ParentLineIDHasPrefix(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldHasPrefix(FieldParentLineID, v)) +} + +// ParentLineIDHasSuffix applies the HasSuffix predicate on the "parent_line_id" field. +func ParentLineIDHasSuffix(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldHasSuffix(FieldParentLineID, v)) +} + +// ParentLineIDIsNil applies the IsNil predicate on the "parent_line_id" field. +func ParentLineIDIsNil() predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldIsNull(FieldParentLineID)) +} + +// ParentLineIDNotNil applies the NotNil predicate on the "parent_line_id" field. +func ParentLineIDNotNil() predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldNotNull(FieldParentLineID)) +} + +// ParentLineIDEqualFold applies the EqualFold predicate on the "parent_line_id" field. +func ParentLineIDEqualFold(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldEqualFold(FieldParentLineID, v)) +} + +// ParentLineIDContainsFold applies the ContainsFold predicate on the "parent_line_id" field. +func ParentLineIDContainsFold(v string) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(sql.FieldContainsFold(FieldParentLineID, v)) +} + // PeriodStartEQ applies the EQ predicate on the "period_start" field. func PeriodStartEQ(v time.Time) predicate.BillingInvoiceLine { return predicate.BillingInvoiceLine(sql.FieldEQ(FieldPeriodStart, v)) @@ -886,21 +966,90 @@ func HasBillingInvoiceWith(preds ...predicate.BillingInvoice) predicate.BillingI }) } -// HasBillingInvoiceManualLines applies the HasEdge predicate on the "billing_invoice_manual_lines" edge. -func HasBillingInvoiceManualLines() predicate.BillingInvoiceLine { +// HasManualFeeLine applies the HasEdge predicate on the "manual_fee_line" edge. +func HasManualFeeLine() predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ManualFeeLineTable, ManualFeeLineColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasManualFeeLineWith applies the HasEdge predicate on the "manual_fee_line" edge with a given conditions (other predicates). +func HasManualFeeLineWith(preds ...predicate.BillingInvoiceManualLineConfig) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(func(s *sql.Selector) { + step := newManualFeeLineStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasManualUsageBasedLine applies the HasEdge predicate on the "manual_usage_based_line" edge. +func HasManualUsageBasedLine() predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ManualUsageBasedLineTable, ManualUsageBasedLineColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasManualUsageBasedLineWith applies the HasEdge predicate on the "manual_usage_based_line" edge with a given conditions (other predicates). +func HasManualUsageBasedLineWith(preds ...predicate.BillingInvoiceManualUsageBasedLineConfig) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(func(s *sql.Selector) { + step := newManualUsageBasedLineStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasParentLine applies the HasEdge predicate on the "parent_line" edge. +func HasParentLine() predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentLineTable, ParentLineColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasParentLineWith applies the HasEdge predicate on the "parent_line" edge with a given conditions (other predicates). +func HasParentLineWith(preds ...predicate.BillingInvoiceLine) predicate.BillingInvoiceLine { + return predicate.BillingInvoiceLine(func(s *sql.Selector) { + step := newParentLineStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasChildLines applies the HasEdge predicate on the "child_lines" edge. +func HasChildLines() predicate.BillingInvoiceLine { return predicate.BillingInvoiceLine(func(s *sql.Selector) { step := sqlgraph.NewStep( sqlgraph.From(Table, FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, BillingInvoiceManualLinesTable, BillingInvoiceManualLinesColumn), + sqlgraph.Edge(sqlgraph.O2M, false, ChildLinesTable, ChildLinesColumn), ) sqlgraph.HasNeighbors(s, step) }) } -// HasBillingInvoiceManualLinesWith applies the HasEdge predicate on the "billing_invoice_manual_lines" edge with a given conditions (other predicates). -func HasBillingInvoiceManualLinesWith(preds ...predicate.BillingInvoiceManualLineConfig) predicate.BillingInvoiceLine { +// HasChildLinesWith applies the HasEdge predicate on the "child_lines" edge with a given conditions (other predicates). +func HasChildLinesWith(preds ...predicate.BillingInvoiceLine) predicate.BillingInvoiceLine { return predicate.BillingInvoiceLine(func(s *sql.Selector) { - step := newBillingInvoiceManualLinesStep() + step := newChildLinesStep() sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { for _, p := range preds { p(s) diff --git a/openmeter/ent/db/billinginvoiceline_create.go b/openmeter/ent/db/billinginvoiceline_create.go index 53266856e..aceec44dc 100644 --- a/openmeter/ent/db/billinginvoiceline_create.go +++ b/openmeter/ent/db/billinginvoiceline_create.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/pkg/currencyx" ) @@ -108,6 +109,20 @@ func (bilc *BillingInvoiceLineCreate) SetInvoiceID(s string) *BillingInvoiceLine return bilc } +// SetParentLineID sets the "parent_line_id" field. +func (bilc *BillingInvoiceLineCreate) SetParentLineID(s string) *BillingInvoiceLineCreate { + bilc.mutation.SetParentLineID(s) + return bilc +} + +// SetNillableParentLineID sets the "parent_line_id" field if the given value is not nil. +func (bilc *BillingInvoiceLineCreate) SetNillableParentLineID(s *string) *BillingInvoiceLineCreate { + if s != nil { + bilc.SetParentLineID(*s) + } + return bilc +} + // SetPeriodStart sets the "period_start" field. func (bilc *BillingInvoiceLineCreate) SetPeriodStart(t time.Time) *BillingInvoiceLineCreate { bilc.mutation.SetPeriodStart(t) @@ -189,23 +204,62 @@ func (bilc *BillingInvoiceLineCreate) SetBillingInvoice(b *BillingInvoice) *Bill return bilc.SetBillingInvoiceID(b.ID) } -// SetBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by ID. -func (bilc *BillingInvoiceLineCreate) SetBillingInvoiceManualLinesID(id string) *BillingInvoiceLineCreate { - bilc.mutation.SetBillingInvoiceManualLinesID(id) +// SetManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by ID. +func (bilc *BillingInvoiceLineCreate) SetManualFeeLineID(id string) *BillingInvoiceLineCreate { + bilc.mutation.SetManualFeeLineID(id) return bilc } -// SetNillableBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by ID if the given value is not nil. -func (bilc *BillingInvoiceLineCreate) SetNillableBillingInvoiceManualLinesID(id *string) *BillingInvoiceLineCreate { +// SetNillableManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by ID if the given value is not nil. +func (bilc *BillingInvoiceLineCreate) SetNillableManualFeeLineID(id *string) *BillingInvoiceLineCreate { if id != nil { - bilc = bilc.SetBillingInvoiceManualLinesID(*id) + bilc = bilc.SetManualFeeLineID(*id) } return bilc } -// SetBillingInvoiceManualLines sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity. -func (bilc *BillingInvoiceLineCreate) SetBillingInvoiceManualLines(b *BillingInvoiceManualLineConfig) *BillingInvoiceLineCreate { - return bilc.SetBillingInvoiceManualLinesID(b.ID) +// SetManualFeeLine sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity. +func (bilc *BillingInvoiceLineCreate) SetManualFeeLine(b *BillingInvoiceManualLineConfig) *BillingInvoiceLineCreate { + return bilc.SetManualFeeLineID(b.ID) +} + +// SetManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by ID. +func (bilc *BillingInvoiceLineCreate) SetManualUsageBasedLineID(id string) *BillingInvoiceLineCreate { + bilc.mutation.SetManualUsageBasedLineID(id) + return bilc +} + +// SetNillableManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by ID if the given value is not nil. +func (bilc *BillingInvoiceLineCreate) SetNillableManualUsageBasedLineID(id *string) *BillingInvoiceLineCreate { + if id != nil { + bilc = bilc.SetManualUsageBasedLineID(*id) + } + return bilc +} + +// SetManualUsageBasedLine sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity. +func (bilc *BillingInvoiceLineCreate) SetManualUsageBasedLine(b *BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceLineCreate { + return bilc.SetManualUsageBasedLineID(b.ID) +} + +// SetParentLine sets the "parent_line" edge to the BillingInvoiceLine entity. +func (bilc *BillingInvoiceLineCreate) SetParentLine(b *BillingInvoiceLine) *BillingInvoiceLineCreate { + return bilc.SetParentLineID(b.ID) +} + +// AddChildLineIDs adds the "child_lines" edge to the BillingInvoiceLine entity by IDs. +func (bilc *BillingInvoiceLineCreate) AddChildLineIDs(ids ...string) *BillingInvoiceLineCreate { + bilc.mutation.AddChildLineIDs(ids...) + return bilc +} + +// AddChildLines adds the "child_lines" edges to the BillingInvoiceLine entity. +func (bilc *BillingInvoiceLineCreate) AddChildLines(b ...*BillingInvoiceLine) *BillingInvoiceLineCreate { + ids := make([]string, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return bilc.AddChildLineIDs(ids...) } // Mutation returns the BillingInvoiceLineMutation object of the builder. @@ -428,12 +482,12 @@ func (bilc *BillingInvoiceLineCreate) createSpec() (*BillingInvoiceLine, *sqlgra _node.InvoiceID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } - if nodes := bilc.mutation.BillingInvoiceManualLinesIDs(); len(nodes) > 0 { + if nodes := bilc.mutation.ManualFeeLineIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, Inverse: false, - Table: billinginvoiceline.BillingInvoiceManualLinesTable, - Columns: []string{billinginvoiceline.BillingInvoiceManualLinesColumn}, + Table: billinginvoiceline.ManualFeeLineTable, + Columns: []string{billinginvoiceline.ManualFeeLineColumn}, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanuallineconfig.FieldID, field.TypeString), @@ -445,6 +499,56 @@ func (bilc *BillingInvoiceLineCreate) createSpec() (*BillingInvoiceLine, *sqlgra _node.manual_line_config_id = &nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := bilc.mutation.ManualUsageBasedLineIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: billinginvoiceline.ManualUsageBasedLineTable, + Columns: []string{billinginvoiceline.ManualUsageBasedLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.manual_usage_based_line_config_id = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := bilc.mutation.ParentLineIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: billinginvoiceline.ParentLineTable, + Columns: []string{billinginvoiceline.ParentLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ParentLineID = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := bilc.mutation.ChildLinesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } @@ -587,6 +691,24 @@ func (u *BillingInvoiceLineUpsert) UpdateInvoiceID() *BillingInvoiceLineUpsert { return u } +// SetParentLineID sets the "parent_line_id" field. +func (u *BillingInvoiceLineUpsert) SetParentLineID(v string) *BillingInvoiceLineUpsert { + u.Set(billinginvoiceline.FieldParentLineID, v) + return u +} + +// UpdateParentLineID sets the "parent_line_id" field to the value that was provided on create. +func (u *BillingInvoiceLineUpsert) UpdateParentLineID() *BillingInvoiceLineUpsert { + u.SetExcluded(billinginvoiceline.FieldParentLineID) + return u +} + +// ClearParentLineID clears the value of the "parent_line_id" field. +func (u *BillingInvoiceLineUpsert) ClearParentLineID() *BillingInvoiceLineUpsert { + u.SetNull(billinginvoiceline.FieldParentLineID) + return u +} + // SetPeriodStart sets the "period_start" field. func (u *BillingInvoiceLineUpsert) SetPeriodStart(v time.Time) *BillingInvoiceLineUpsert { u.Set(billinginvoiceline.FieldPeriodStart, v) @@ -623,18 +745,6 @@ func (u *BillingInvoiceLineUpsert) UpdateInvoiceAt() *BillingInvoiceLineUpsert { return u } -// SetType sets the "type" field. -func (u *BillingInvoiceLineUpsert) SetType(v billingentity.InvoiceLineType) *BillingInvoiceLineUpsert { - u.Set(billinginvoiceline.FieldType, v) - return u -} - -// UpdateType sets the "type" field to the value that was provided on create. -func (u *BillingInvoiceLineUpsert) UpdateType() *BillingInvoiceLineUpsert { - u.SetExcluded(billinginvoiceline.FieldType) - return u -} - // SetStatus sets the "status" field. func (u *BillingInvoiceLineUpsert) SetStatus(v billingentity.InvoiceLineStatus) *BillingInvoiceLineUpsert { u.Set(billinginvoiceline.FieldStatus, v) @@ -706,6 +816,9 @@ func (u *BillingInvoiceLineUpsertOne) UpdateNewValues() *BillingInvoiceLineUpser if _, exists := u.create.mutation.CreatedAt(); exists { s.SetIgnore(billinginvoiceline.FieldCreatedAt) } + if _, exists := u.create.mutation.GetType(); exists { + s.SetIgnore(billinginvoiceline.FieldType) + } if _, exists := u.create.mutation.Currency(); exists { s.SetIgnore(billinginvoiceline.FieldCurrency) } @@ -845,6 +958,27 @@ func (u *BillingInvoiceLineUpsertOne) UpdateInvoiceID() *BillingInvoiceLineUpser }) } +// SetParentLineID sets the "parent_line_id" field. +func (u *BillingInvoiceLineUpsertOne) SetParentLineID(v string) *BillingInvoiceLineUpsertOne { + return u.Update(func(s *BillingInvoiceLineUpsert) { + s.SetParentLineID(v) + }) +} + +// UpdateParentLineID sets the "parent_line_id" field to the value that was provided on create. +func (u *BillingInvoiceLineUpsertOne) UpdateParentLineID() *BillingInvoiceLineUpsertOne { + return u.Update(func(s *BillingInvoiceLineUpsert) { + s.UpdateParentLineID() + }) +} + +// ClearParentLineID clears the value of the "parent_line_id" field. +func (u *BillingInvoiceLineUpsertOne) ClearParentLineID() *BillingInvoiceLineUpsertOne { + return u.Update(func(s *BillingInvoiceLineUpsert) { + s.ClearParentLineID() + }) +} + // SetPeriodStart sets the "period_start" field. func (u *BillingInvoiceLineUpsertOne) SetPeriodStart(v time.Time) *BillingInvoiceLineUpsertOne { return u.Update(func(s *BillingInvoiceLineUpsert) { @@ -887,20 +1021,6 @@ func (u *BillingInvoiceLineUpsertOne) UpdateInvoiceAt() *BillingInvoiceLineUpser }) } -// SetType sets the "type" field. -func (u *BillingInvoiceLineUpsertOne) SetType(v billingentity.InvoiceLineType) *BillingInvoiceLineUpsertOne { - return u.Update(func(s *BillingInvoiceLineUpsert) { - s.SetType(v) - }) -} - -// UpdateType sets the "type" field to the value that was provided on create. -func (u *BillingInvoiceLineUpsertOne) UpdateType() *BillingInvoiceLineUpsertOne { - return u.Update(func(s *BillingInvoiceLineUpsert) { - s.UpdateType() - }) -} - // SetStatus sets the "status" field. func (u *BillingInvoiceLineUpsertOne) SetStatus(v billingentity.InvoiceLineStatus) *BillingInvoiceLineUpsertOne { return u.Update(func(s *BillingInvoiceLineUpsert) { @@ -1146,6 +1266,9 @@ func (u *BillingInvoiceLineUpsertBulk) UpdateNewValues() *BillingInvoiceLineUpse if _, exists := b.mutation.CreatedAt(); exists { s.SetIgnore(billinginvoiceline.FieldCreatedAt) } + if _, exists := b.mutation.GetType(); exists { + s.SetIgnore(billinginvoiceline.FieldType) + } if _, exists := b.mutation.Currency(); exists { s.SetIgnore(billinginvoiceline.FieldCurrency) } @@ -1286,6 +1409,27 @@ func (u *BillingInvoiceLineUpsertBulk) UpdateInvoiceID() *BillingInvoiceLineUpse }) } +// SetParentLineID sets the "parent_line_id" field. +func (u *BillingInvoiceLineUpsertBulk) SetParentLineID(v string) *BillingInvoiceLineUpsertBulk { + return u.Update(func(s *BillingInvoiceLineUpsert) { + s.SetParentLineID(v) + }) +} + +// UpdateParentLineID sets the "parent_line_id" field to the value that was provided on create. +func (u *BillingInvoiceLineUpsertBulk) UpdateParentLineID() *BillingInvoiceLineUpsertBulk { + return u.Update(func(s *BillingInvoiceLineUpsert) { + s.UpdateParentLineID() + }) +} + +// ClearParentLineID clears the value of the "parent_line_id" field. +func (u *BillingInvoiceLineUpsertBulk) ClearParentLineID() *BillingInvoiceLineUpsertBulk { + return u.Update(func(s *BillingInvoiceLineUpsert) { + s.ClearParentLineID() + }) +} + // SetPeriodStart sets the "period_start" field. func (u *BillingInvoiceLineUpsertBulk) SetPeriodStart(v time.Time) *BillingInvoiceLineUpsertBulk { return u.Update(func(s *BillingInvoiceLineUpsert) { @@ -1328,20 +1472,6 @@ func (u *BillingInvoiceLineUpsertBulk) UpdateInvoiceAt() *BillingInvoiceLineUpse }) } -// SetType sets the "type" field. -func (u *BillingInvoiceLineUpsertBulk) SetType(v billingentity.InvoiceLineType) *BillingInvoiceLineUpsertBulk { - return u.Update(func(s *BillingInvoiceLineUpsert) { - s.SetType(v) - }) -} - -// UpdateType sets the "type" field to the value that was provided on create. -func (u *BillingInvoiceLineUpsertBulk) UpdateType() *BillingInvoiceLineUpsertBulk { - return u.Update(func(s *BillingInvoiceLineUpsert) { - s.UpdateType() - }) -} - // SetStatus sets the "status" field. func (u *BillingInvoiceLineUpsertBulk) SetStatus(v billingentity.InvoiceLineStatus) *BillingInvoiceLineUpsertBulk { return u.Update(func(s *BillingInvoiceLineUpsert) { diff --git a/openmeter/ent/db/billinginvoiceline_query.go b/openmeter/ent/db/billinginvoiceline_query.go index 828204b46..ef3980286 100644 --- a/openmeter/ent/db/billinginvoiceline_query.go +++ b/openmeter/ent/db/billinginvoiceline_query.go @@ -4,6 +4,7 @@ package db import ( "context" + "database/sql/driver" "fmt" "math" @@ -15,20 +16,24 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" ) // BillingInvoiceLineQuery is the builder for querying BillingInvoiceLine entities. type BillingInvoiceLineQuery struct { config - ctx *QueryContext - order []billinginvoiceline.OrderOption - inters []Interceptor - predicates []predicate.BillingInvoiceLine - withBillingInvoice *BillingInvoiceQuery - withBillingInvoiceManualLines *BillingInvoiceManualLineConfigQuery - withFKs bool - modifiers []func(*sql.Selector) + ctx *QueryContext + order []billinginvoiceline.OrderOption + inters []Interceptor + predicates []predicate.BillingInvoiceLine + withBillingInvoice *BillingInvoiceQuery + withManualFeeLine *BillingInvoiceManualLineConfigQuery + withManualUsageBasedLine *BillingInvoiceManualUsageBasedLineConfigQuery + withParentLine *BillingInvoiceLineQuery + withChildLines *BillingInvoiceLineQuery + withFKs bool + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -87,8 +92,8 @@ func (bilq *BillingInvoiceLineQuery) QueryBillingInvoice() *BillingInvoiceQuery return query } -// QueryBillingInvoiceManualLines chains the current query on the "billing_invoice_manual_lines" edge. -func (bilq *BillingInvoiceLineQuery) QueryBillingInvoiceManualLines() *BillingInvoiceManualLineConfigQuery { +// QueryManualFeeLine chains the current query on the "manual_fee_line" edge. +func (bilq *BillingInvoiceLineQuery) QueryManualFeeLine() *BillingInvoiceManualLineConfigQuery { query := (&BillingInvoiceManualLineConfigClient{config: bilq.config}).Query() query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { if err := bilq.prepareQuery(ctx); err != nil { @@ -101,7 +106,73 @@ func (bilq *BillingInvoiceLineQuery) QueryBillingInvoiceManualLines() *BillingIn step := sqlgraph.NewStep( sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, selector), sqlgraph.To(billinginvoicemanuallineconfig.Table, billinginvoicemanuallineconfig.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, billinginvoiceline.BillingInvoiceManualLinesTable, billinginvoiceline.BillingInvoiceManualLinesColumn), + sqlgraph.Edge(sqlgraph.M2O, false, billinginvoiceline.ManualFeeLineTable, billinginvoiceline.ManualFeeLineColumn), + ) + fromU = sqlgraph.SetNeighbors(bilq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryManualUsageBasedLine chains the current query on the "manual_usage_based_line" edge. +func (bilq *BillingInvoiceLineQuery) QueryManualUsageBasedLine() *BillingInvoiceManualUsageBasedLineConfigQuery { + query := (&BillingInvoiceManualUsageBasedLineConfigClient{config: bilq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := bilq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := bilq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, selector), + sqlgraph.To(billinginvoicemanualusagebasedlineconfig.Table, billinginvoicemanualusagebasedlineconfig.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, billinginvoiceline.ManualUsageBasedLineTable, billinginvoiceline.ManualUsageBasedLineColumn), + ) + fromU = sqlgraph.SetNeighbors(bilq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryParentLine chains the current query on the "parent_line" edge. +func (bilq *BillingInvoiceLineQuery) QueryParentLine() *BillingInvoiceLineQuery { + query := (&BillingInvoiceLineClient{config: bilq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := bilq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := bilq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, selector), + sqlgraph.To(billinginvoiceline.Table, billinginvoiceline.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, billinginvoiceline.ParentLineTable, billinginvoiceline.ParentLineColumn), + ) + fromU = sqlgraph.SetNeighbors(bilq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryChildLines chains the current query on the "child_lines" edge. +func (bilq *BillingInvoiceLineQuery) QueryChildLines() *BillingInvoiceLineQuery { + query := (&BillingInvoiceLineClient{config: bilq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := bilq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := bilq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, selector), + sqlgraph.To(billinginvoiceline.Table, billinginvoiceline.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, billinginvoiceline.ChildLinesTable, billinginvoiceline.ChildLinesColumn), ) fromU = sqlgraph.SetNeighbors(bilq.driver.Dialect(), step) return fromU, nil @@ -296,13 +367,16 @@ func (bilq *BillingInvoiceLineQuery) Clone() *BillingInvoiceLineQuery { return nil } return &BillingInvoiceLineQuery{ - config: bilq.config, - ctx: bilq.ctx.Clone(), - order: append([]billinginvoiceline.OrderOption{}, bilq.order...), - inters: append([]Interceptor{}, bilq.inters...), - predicates: append([]predicate.BillingInvoiceLine{}, bilq.predicates...), - withBillingInvoice: bilq.withBillingInvoice.Clone(), - withBillingInvoiceManualLines: bilq.withBillingInvoiceManualLines.Clone(), + config: bilq.config, + ctx: bilq.ctx.Clone(), + order: append([]billinginvoiceline.OrderOption{}, bilq.order...), + inters: append([]Interceptor{}, bilq.inters...), + predicates: append([]predicate.BillingInvoiceLine{}, bilq.predicates...), + withBillingInvoice: bilq.withBillingInvoice.Clone(), + withManualFeeLine: bilq.withManualFeeLine.Clone(), + withManualUsageBasedLine: bilq.withManualUsageBasedLine.Clone(), + withParentLine: bilq.withParentLine.Clone(), + withChildLines: bilq.withChildLines.Clone(), // clone intermediate query. sql: bilq.sql.Clone(), path: bilq.path, @@ -320,14 +394,47 @@ func (bilq *BillingInvoiceLineQuery) WithBillingInvoice(opts ...func(*BillingInv return bilq } -// WithBillingInvoiceManualLines tells the query-builder to eager-load the nodes that are connected to -// the "billing_invoice_manual_lines" edge. The optional arguments are used to configure the query builder of the edge. -func (bilq *BillingInvoiceLineQuery) WithBillingInvoiceManualLines(opts ...func(*BillingInvoiceManualLineConfigQuery)) *BillingInvoiceLineQuery { +// WithManualFeeLine tells the query-builder to eager-load the nodes that are connected to +// the "manual_fee_line" edge. The optional arguments are used to configure the query builder of the edge. +func (bilq *BillingInvoiceLineQuery) WithManualFeeLine(opts ...func(*BillingInvoiceManualLineConfigQuery)) *BillingInvoiceLineQuery { query := (&BillingInvoiceManualLineConfigClient{config: bilq.config}).Query() for _, opt := range opts { opt(query) } - bilq.withBillingInvoiceManualLines = query + bilq.withManualFeeLine = query + return bilq +} + +// WithManualUsageBasedLine tells the query-builder to eager-load the nodes that are connected to +// the "manual_usage_based_line" edge. The optional arguments are used to configure the query builder of the edge. +func (bilq *BillingInvoiceLineQuery) WithManualUsageBasedLine(opts ...func(*BillingInvoiceManualUsageBasedLineConfigQuery)) *BillingInvoiceLineQuery { + query := (&BillingInvoiceManualUsageBasedLineConfigClient{config: bilq.config}).Query() + for _, opt := range opts { + opt(query) + } + bilq.withManualUsageBasedLine = query + return bilq +} + +// WithParentLine tells the query-builder to eager-load the nodes that are connected to +// the "parent_line" edge. The optional arguments are used to configure the query builder of the edge. +func (bilq *BillingInvoiceLineQuery) WithParentLine(opts ...func(*BillingInvoiceLineQuery)) *BillingInvoiceLineQuery { + query := (&BillingInvoiceLineClient{config: bilq.config}).Query() + for _, opt := range opts { + opt(query) + } + bilq.withParentLine = query + return bilq +} + +// WithChildLines tells the query-builder to eager-load the nodes that are connected to +// the "child_lines" edge. The optional arguments are used to configure the query builder of the edge. +func (bilq *BillingInvoiceLineQuery) WithChildLines(opts ...func(*BillingInvoiceLineQuery)) *BillingInvoiceLineQuery { + query := (&BillingInvoiceLineClient{config: bilq.config}).Query() + for _, opt := range opts { + opt(query) + } + bilq.withChildLines = query return bilq } @@ -410,12 +517,15 @@ func (bilq *BillingInvoiceLineQuery) sqlAll(ctx context.Context, hooks ...queryH nodes = []*BillingInvoiceLine{} withFKs = bilq.withFKs _spec = bilq.querySpec() - loadedTypes = [2]bool{ + loadedTypes = [5]bool{ bilq.withBillingInvoice != nil, - bilq.withBillingInvoiceManualLines != nil, + bilq.withManualFeeLine != nil, + bilq.withManualUsageBasedLine != nil, + bilq.withParentLine != nil, + bilq.withChildLines != nil, } ) - if bilq.withBillingInvoiceManualLines != nil { + if bilq.withManualFeeLine != nil || bilq.withManualUsageBasedLine != nil { withFKs = true } if withFKs { @@ -448,9 +558,30 @@ func (bilq *BillingInvoiceLineQuery) sqlAll(ctx context.Context, hooks ...queryH return nil, err } } - if query := bilq.withBillingInvoiceManualLines; query != nil { - if err := bilq.loadBillingInvoiceManualLines(ctx, query, nodes, nil, - func(n *BillingInvoiceLine, e *BillingInvoiceManualLineConfig) { n.Edges.BillingInvoiceManualLines = e }); err != nil { + if query := bilq.withManualFeeLine; query != nil { + if err := bilq.loadManualFeeLine(ctx, query, nodes, nil, + func(n *BillingInvoiceLine, e *BillingInvoiceManualLineConfig) { n.Edges.ManualFeeLine = e }); err != nil { + return nil, err + } + } + if query := bilq.withManualUsageBasedLine; query != nil { + if err := bilq.loadManualUsageBasedLine(ctx, query, nodes, nil, + func(n *BillingInvoiceLine, e *BillingInvoiceManualUsageBasedLineConfig) { + n.Edges.ManualUsageBasedLine = e + }); err != nil { + return nil, err + } + } + if query := bilq.withParentLine; query != nil { + if err := bilq.loadParentLine(ctx, query, nodes, nil, + func(n *BillingInvoiceLine, e *BillingInvoiceLine) { n.Edges.ParentLine = e }); err != nil { + return nil, err + } + } + if query := bilq.withChildLines; query != nil { + if err := bilq.loadChildLines(ctx, query, nodes, + func(n *BillingInvoiceLine) { n.Edges.ChildLines = []*BillingInvoiceLine{} }, + func(n *BillingInvoiceLine, e *BillingInvoiceLine) { n.Edges.ChildLines = append(n.Edges.ChildLines, e) }); err != nil { return nil, err } } @@ -486,7 +617,7 @@ func (bilq *BillingInvoiceLineQuery) loadBillingInvoice(ctx context.Context, que } return nil } -func (bilq *BillingInvoiceLineQuery) loadBillingInvoiceManualLines(ctx context.Context, query *BillingInvoiceManualLineConfigQuery, nodes []*BillingInvoiceLine, init func(*BillingInvoiceLine), assign func(*BillingInvoiceLine, *BillingInvoiceManualLineConfig)) error { +func (bilq *BillingInvoiceLineQuery) loadManualFeeLine(ctx context.Context, query *BillingInvoiceManualLineConfigQuery, nodes []*BillingInvoiceLine, init func(*BillingInvoiceLine), assign func(*BillingInvoiceLine, *BillingInvoiceManualLineConfig)) error { ids := make([]string, 0, len(nodes)) nodeids := make(map[string][]*BillingInvoiceLine) for i := range nodes { @@ -518,6 +649,104 @@ func (bilq *BillingInvoiceLineQuery) loadBillingInvoiceManualLines(ctx context.C } return nil } +func (bilq *BillingInvoiceLineQuery) loadManualUsageBasedLine(ctx context.Context, query *BillingInvoiceManualUsageBasedLineConfigQuery, nodes []*BillingInvoiceLine, init func(*BillingInvoiceLine), assign func(*BillingInvoiceLine, *BillingInvoiceManualUsageBasedLineConfig)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*BillingInvoiceLine) + for i := range nodes { + if nodes[i].manual_usage_based_line_config_id == nil { + continue + } + fk := *nodes[i].manual_usage_based_line_config_id + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(billinginvoicemanualusagebasedlineconfig.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "manual_usage_based_line_config_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (bilq *BillingInvoiceLineQuery) loadParentLine(ctx context.Context, query *BillingInvoiceLineQuery, nodes []*BillingInvoiceLine, init func(*BillingInvoiceLine), assign func(*BillingInvoiceLine, *BillingInvoiceLine)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*BillingInvoiceLine) + for i := range nodes { + if nodes[i].ParentLineID == nil { + continue + } + fk := *nodes[i].ParentLineID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(billinginvoiceline.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "parent_line_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (bilq *BillingInvoiceLineQuery) loadChildLines(ctx context.Context, query *BillingInvoiceLineQuery, nodes []*BillingInvoiceLine, init func(*BillingInvoiceLine), assign func(*BillingInvoiceLine, *BillingInvoiceLine)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*BillingInvoiceLine) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + query.withFKs = true + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(billinginvoiceline.FieldParentLineID) + } + query.Where(predicate.BillingInvoiceLine(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(billinginvoiceline.ChildLinesColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.ParentLineID + if fk == nil { + return fmt.Errorf(`foreign-key "parent_line_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "parent_line_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} func (bilq *BillingInvoiceLineQuery) sqlCount(ctx context.Context) (int, error) { _spec := bilq.querySpec() @@ -550,6 +779,9 @@ func (bilq *BillingInvoiceLineQuery) querySpec() *sqlgraph.QuerySpec { if bilq.withBillingInvoice != nil { _spec.Node.AddColumnOnce(billinginvoiceline.FieldInvoiceID) } + if bilq.withParentLine != nil { + _spec.Node.AddColumnOnce(billinginvoiceline.FieldParentLineID) + } } if ps := bilq.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/openmeter/ent/db/billinginvoiceline_update.go b/openmeter/ent/db/billinginvoiceline_update.go index 55b221181..8a57772dc 100644 --- a/openmeter/ent/db/billinginvoiceline_update.go +++ b/openmeter/ent/db/billinginvoiceline_update.go @@ -16,6 +16,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" ) @@ -118,6 +119,26 @@ func (bilu *BillingInvoiceLineUpdate) SetNillableInvoiceID(s *string) *BillingIn return bilu } +// SetParentLineID sets the "parent_line_id" field. +func (bilu *BillingInvoiceLineUpdate) SetParentLineID(s string) *BillingInvoiceLineUpdate { + bilu.mutation.SetParentLineID(s) + return bilu +} + +// SetNillableParentLineID sets the "parent_line_id" field if the given value is not nil. +func (bilu *BillingInvoiceLineUpdate) SetNillableParentLineID(s *string) *BillingInvoiceLineUpdate { + if s != nil { + bilu.SetParentLineID(*s) + } + return bilu +} + +// ClearParentLineID clears the value of the "parent_line_id" field. +func (bilu *BillingInvoiceLineUpdate) ClearParentLineID() *BillingInvoiceLineUpdate { + bilu.mutation.ClearParentLineID() + return bilu +} + // SetPeriodStart sets the "period_start" field. func (bilu *BillingInvoiceLineUpdate) SetPeriodStart(t time.Time) *BillingInvoiceLineUpdate { bilu.mutation.SetPeriodStart(t) @@ -160,20 +181,6 @@ func (bilu *BillingInvoiceLineUpdate) SetNillableInvoiceAt(t *time.Time) *Billin return bilu } -// SetType sets the "type" field. -func (bilu *BillingInvoiceLineUpdate) SetType(blt billingentity.InvoiceLineType) *BillingInvoiceLineUpdate { - bilu.mutation.SetType(blt) - return bilu -} - -// SetNillableType sets the "type" field if the given value is not nil. -func (bilu *BillingInvoiceLineUpdate) SetNillableType(blt *billingentity.InvoiceLineType) *BillingInvoiceLineUpdate { - if blt != nil { - bilu.SetType(*blt) - } - return bilu -} - // SetStatus sets the "status" field. func (bilu *BillingInvoiceLineUpdate) SetStatus(bls billingentity.InvoiceLineStatus) *BillingInvoiceLineUpdate { bilu.mutation.SetStatus(bls) @@ -231,23 +238,62 @@ func (bilu *BillingInvoiceLineUpdate) SetBillingInvoice(b *BillingInvoice) *Bill return bilu.SetBillingInvoiceID(b.ID) } -// SetBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by ID. -func (bilu *BillingInvoiceLineUpdate) SetBillingInvoiceManualLinesID(id string) *BillingInvoiceLineUpdate { - bilu.mutation.SetBillingInvoiceManualLinesID(id) +// SetManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by ID. +func (bilu *BillingInvoiceLineUpdate) SetManualFeeLineID(id string) *BillingInvoiceLineUpdate { + bilu.mutation.SetManualFeeLineID(id) + return bilu +} + +// SetNillableManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by ID if the given value is not nil. +func (bilu *BillingInvoiceLineUpdate) SetNillableManualFeeLineID(id *string) *BillingInvoiceLineUpdate { + if id != nil { + bilu = bilu.SetManualFeeLineID(*id) + } + return bilu +} + +// SetManualFeeLine sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity. +func (bilu *BillingInvoiceLineUpdate) SetManualFeeLine(b *BillingInvoiceManualLineConfig) *BillingInvoiceLineUpdate { + return bilu.SetManualFeeLineID(b.ID) +} + +// SetManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by ID. +func (bilu *BillingInvoiceLineUpdate) SetManualUsageBasedLineID(id string) *BillingInvoiceLineUpdate { + bilu.mutation.SetManualUsageBasedLineID(id) return bilu } -// SetNillableBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by ID if the given value is not nil. -func (bilu *BillingInvoiceLineUpdate) SetNillableBillingInvoiceManualLinesID(id *string) *BillingInvoiceLineUpdate { +// SetNillableManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by ID if the given value is not nil. +func (bilu *BillingInvoiceLineUpdate) SetNillableManualUsageBasedLineID(id *string) *BillingInvoiceLineUpdate { if id != nil { - bilu = bilu.SetBillingInvoiceManualLinesID(*id) + bilu = bilu.SetManualUsageBasedLineID(*id) } return bilu } -// SetBillingInvoiceManualLines sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity. -func (bilu *BillingInvoiceLineUpdate) SetBillingInvoiceManualLines(b *BillingInvoiceManualLineConfig) *BillingInvoiceLineUpdate { - return bilu.SetBillingInvoiceManualLinesID(b.ID) +// SetManualUsageBasedLine sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity. +func (bilu *BillingInvoiceLineUpdate) SetManualUsageBasedLine(b *BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceLineUpdate { + return bilu.SetManualUsageBasedLineID(b.ID) +} + +// SetParentLine sets the "parent_line" edge to the BillingInvoiceLine entity. +func (bilu *BillingInvoiceLineUpdate) SetParentLine(b *BillingInvoiceLine) *BillingInvoiceLineUpdate { + return bilu.SetParentLineID(b.ID) +} + +// AddChildLineIDs adds the "child_lines" edge to the BillingInvoiceLine entity by IDs. +func (bilu *BillingInvoiceLineUpdate) AddChildLineIDs(ids ...string) *BillingInvoiceLineUpdate { + bilu.mutation.AddChildLineIDs(ids...) + return bilu +} + +// AddChildLines adds the "child_lines" edges to the BillingInvoiceLine entity. +func (bilu *BillingInvoiceLineUpdate) AddChildLines(b ...*BillingInvoiceLine) *BillingInvoiceLineUpdate { + ids := make([]string, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return bilu.AddChildLineIDs(ids...) } // Mutation returns the BillingInvoiceLineMutation object of the builder. @@ -261,12 +307,45 @@ func (bilu *BillingInvoiceLineUpdate) ClearBillingInvoice() *BillingInvoiceLineU return bilu } -// ClearBillingInvoiceManualLines clears the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity. -func (bilu *BillingInvoiceLineUpdate) ClearBillingInvoiceManualLines() *BillingInvoiceLineUpdate { - bilu.mutation.ClearBillingInvoiceManualLines() +// ClearManualFeeLine clears the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity. +func (bilu *BillingInvoiceLineUpdate) ClearManualFeeLine() *BillingInvoiceLineUpdate { + bilu.mutation.ClearManualFeeLine() + return bilu +} + +// ClearManualUsageBasedLine clears the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity. +func (bilu *BillingInvoiceLineUpdate) ClearManualUsageBasedLine() *BillingInvoiceLineUpdate { + bilu.mutation.ClearManualUsageBasedLine() + return bilu +} + +// ClearParentLine clears the "parent_line" edge to the BillingInvoiceLine entity. +func (bilu *BillingInvoiceLineUpdate) ClearParentLine() *BillingInvoiceLineUpdate { + bilu.mutation.ClearParentLine() + return bilu +} + +// ClearChildLines clears all "child_lines" edges to the BillingInvoiceLine entity. +func (bilu *BillingInvoiceLineUpdate) ClearChildLines() *BillingInvoiceLineUpdate { + bilu.mutation.ClearChildLines() + return bilu +} + +// RemoveChildLineIDs removes the "child_lines" edge to BillingInvoiceLine entities by IDs. +func (bilu *BillingInvoiceLineUpdate) RemoveChildLineIDs(ids ...string) *BillingInvoiceLineUpdate { + bilu.mutation.RemoveChildLineIDs(ids...) return bilu } +// RemoveChildLines removes "child_lines" edges to BillingInvoiceLine entities. +func (bilu *BillingInvoiceLineUpdate) RemoveChildLines(b ...*BillingInvoiceLine) *BillingInvoiceLineUpdate { + ids := make([]string, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return bilu.RemoveChildLineIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (bilu *BillingInvoiceLineUpdate) Save(ctx context.Context) (int, error) { bilu.defaults() @@ -305,11 +384,6 @@ func (bilu *BillingInvoiceLineUpdate) defaults() { // check runs all checks and user-defined validators on the builder. func (bilu *BillingInvoiceLineUpdate) check() error { - if v, ok := bilu.mutation.GetType(); ok { - if err := billinginvoiceline.TypeValidator(v); err != nil { - return &ValidationError{Name: "type", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceLine.type": %w`, err)} - } - } if v, ok := bilu.mutation.Status(); ok { if err := billinginvoiceline.StatusValidator(v); err != nil { return &ValidationError{Name: "status", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceLine.status": %w`, err)} @@ -366,9 +440,6 @@ func (bilu *BillingInvoiceLineUpdate) sqlSave(ctx context.Context) (n int, err e if value, ok := bilu.mutation.InvoiceAt(); ok { _spec.SetField(billinginvoiceline.FieldInvoiceAt, field.TypeTime, value) } - if value, ok := bilu.mutation.GetType(); ok { - _spec.SetField(billinginvoiceline.FieldType, field.TypeEnum, value) - } if value, ok := bilu.mutation.Status(); ok { _spec.SetField(billinginvoiceline.FieldStatus, field.TypeEnum, value) } @@ -413,12 +484,12 @@ func (bilu *BillingInvoiceLineUpdate) sqlSave(ctx context.Context) (n int, err e } _spec.Edges.Add = append(_spec.Edges.Add, edge) } - if bilu.mutation.BillingInvoiceManualLinesCleared() { + if bilu.mutation.ManualFeeLineCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, Inverse: false, - Table: billinginvoiceline.BillingInvoiceManualLinesTable, - Columns: []string{billinginvoiceline.BillingInvoiceManualLinesColumn}, + Table: billinginvoiceline.ManualFeeLineTable, + Columns: []string{billinginvoiceline.ManualFeeLineColumn}, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanuallineconfig.FieldID, field.TypeString), @@ -426,12 +497,12 @@ func (bilu *BillingInvoiceLineUpdate) sqlSave(ctx context.Context) (n int, err e } _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } - if nodes := bilu.mutation.BillingInvoiceManualLinesIDs(); len(nodes) > 0 { + if nodes := bilu.mutation.ManualFeeLineIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, Inverse: false, - Table: billinginvoiceline.BillingInvoiceManualLinesTable, - Columns: []string{billinginvoiceline.BillingInvoiceManualLinesColumn}, + Table: billinginvoiceline.ManualFeeLineTable, + Columns: []string{billinginvoiceline.ManualFeeLineColumn}, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanuallineconfig.FieldID, field.TypeString), @@ -442,6 +513,109 @@ func (bilu *BillingInvoiceLineUpdate) sqlSave(ctx context.Context) (n int, err e } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if bilu.mutation.ManualUsageBasedLineCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: billinginvoiceline.ManualUsageBasedLineTable, + Columns: []string{billinginvoiceline.ManualUsageBasedLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := bilu.mutation.ManualUsageBasedLineIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: billinginvoiceline.ManualUsageBasedLineTable, + Columns: []string{billinginvoiceline.ManualUsageBasedLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if bilu.mutation.ParentLineCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: billinginvoiceline.ParentLineTable, + Columns: []string{billinginvoiceline.ParentLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := bilu.mutation.ParentLineIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: billinginvoiceline.ParentLineTable, + Columns: []string{billinginvoiceline.ParentLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if bilu.mutation.ChildLinesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := bilu.mutation.RemovedChildLinesIDs(); len(nodes) > 0 && !bilu.mutation.ChildLinesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := bilu.mutation.ChildLinesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, bilu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{billinginvoiceline.Label} @@ -548,6 +722,26 @@ func (biluo *BillingInvoiceLineUpdateOne) SetNillableInvoiceID(s *string) *Billi return biluo } +// SetParentLineID sets the "parent_line_id" field. +func (biluo *BillingInvoiceLineUpdateOne) SetParentLineID(s string) *BillingInvoiceLineUpdateOne { + biluo.mutation.SetParentLineID(s) + return biluo +} + +// SetNillableParentLineID sets the "parent_line_id" field if the given value is not nil. +func (biluo *BillingInvoiceLineUpdateOne) SetNillableParentLineID(s *string) *BillingInvoiceLineUpdateOne { + if s != nil { + biluo.SetParentLineID(*s) + } + return biluo +} + +// ClearParentLineID clears the value of the "parent_line_id" field. +func (biluo *BillingInvoiceLineUpdateOne) ClearParentLineID() *BillingInvoiceLineUpdateOne { + biluo.mutation.ClearParentLineID() + return biluo +} + // SetPeriodStart sets the "period_start" field. func (biluo *BillingInvoiceLineUpdateOne) SetPeriodStart(t time.Time) *BillingInvoiceLineUpdateOne { biluo.mutation.SetPeriodStart(t) @@ -590,20 +784,6 @@ func (biluo *BillingInvoiceLineUpdateOne) SetNillableInvoiceAt(t *time.Time) *Bi return biluo } -// SetType sets the "type" field. -func (biluo *BillingInvoiceLineUpdateOne) SetType(blt billingentity.InvoiceLineType) *BillingInvoiceLineUpdateOne { - biluo.mutation.SetType(blt) - return biluo -} - -// SetNillableType sets the "type" field if the given value is not nil. -func (biluo *BillingInvoiceLineUpdateOne) SetNillableType(blt *billingentity.InvoiceLineType) *BillingInvoiceLineUpdateOne { - if blt != nil { - biluo.SetType(*blt) - } - return biluo -} - // SetStatus sets the "status" field. func (biluo *BillingInvoiceLineUpdateOne) SetStatus(bls billingentity.InvoiceLineStatus) *BillingInvoiceLineUpdateOne { biluo.mutation.SetStatus(bls) @@ -661,23 +841,62 @@ func (biluo *BillingInvoiceLineUpdateOne) SetBillingInvoice(b *BillingInvoice) * return biluo.SetBillingInvoiceID(b.ID) } -// SetBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by ID. -func (biluo *BillingInvoiceLineUpdateOne) SetBillingInvoiceManualLinesID(id string) *BillingInvoiceLineUpdateOne { - biluo.mutation.SetBillingInvoiceManualLinesID(id) +// SetManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by ID. +func (biluo *BillingInvoiceLineUpdateOne) SetManualFeeLineID(id string) *BillingInvoiceLineUpdateOne { + biluo.mutation.SetManualFeeLineID(id) + return biluo +} + +// SetNillableManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by ID if the given value is not nil. +func (biluo *BillingInvoiceLineUpdateOne) SetNillableManualFeeLineID(id *string) *BillingInvoiceLineUpdateOne { + if id != nil { + biluo = biluo.SetManualFeeLineID(*id) + } + return biluo +} + +// SetManualFeeLine sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity. +func (biluo *BillingInvoiceLineUpdateOne) SetManualFeeLine(b *BillingInvoiceManualLineConfig) *BillingInvoiceLineUpdateOne { + return biluo.SetManualFeeLineID(b.ID) +} + +// SetManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by ID. +func (biluo *BillingInvoiceLineUpdateOne) SetManualUsageBasedLineID(id string) *BillingInvoiceLineUpdateOne { + biluo.mutation.SetManualUsageBasedLineID(id) return biluo } -// SetNillableBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by ID if the given value is not nil. -func (biluo *BillingInvoiceLineUpdateOne) SetNillableBillingInvoiceManualLinesID(id *string) *BillingInvoiceLineUpdateOne { +// SetNillableManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by ID if the given value is not nil. +func (biluo *BillingInvoiceLineUpdateOne) SetNillableManualUsageBasedLineID(id *string) *BillingInvoiceLineUpdateOne { if id != nil { - biluo = biluo.SetBillingInvoiceManualLinesID(*id) + biluo = biluo.SetManualUsageBasedLineID(*id) } return biluo } -// SetBillingInvoiceManualLines sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity. -func (biluo *BillingInvoiceLineUpdateOne) SetBillingInvoiceManualLines(b *BillingInvoiceManualLineConfig) *BillingInvoiceLineUpdateOne { - return biluo.SetBillingInvoiceManualLinesID(b.ID) +// SetManualUsageBasedLine sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity. +func (biluo *BillingInvoiceLineUpdateOne) SetManualUsageBasedLine(b *BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceLineUpdateOne { + return biluo.SetManualUsageBasedLineID(b.ID) +} + +// SetParentLine sets the "parent_line" edge to the BillingInvoiceLine entity. +func (biluo *BillingInvoiceLineUpdateOne) SetParentLine(b *BillingInvoiceLine) *BillingInvoiceLineUpdateOne { + return biluo.SetParentLineID(b.ID) +} + +// AddChildLineIDs adds the "child_lines" edge to the BillingInvoiceLine entity by IDs. +func (biluo *BillingInvoiceLineUpdateOne) AddChildLineIDs(ids ...string) *BillingInvoiceLineUpdateOne { + biluo.mutation.AddChildLineIDs(ids...) + return biluo +} + +// AddChildLines adds the "child_lines" edges to the BillingInvoiceLine entity. +func (biluo *BillingInvoiceLineUpdateOne) AddChildLines(b ...*BillingInvoiceLine) *BillingInvoiceLineUpdateOne { + ids := make([]string, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return biluo.AddChildLineIDs(ids...) } // Mutation returns the BillingInvoiceLineMutation object of the builder. @@ -691,12 +910,45 @@ func (biluo *BillingInvoiceLineUpdateOne) ClearBillingInvoice() *BillingInvoiceL return biluo } -// ClearBillingInvoiceManualLines clears the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity. -func (biluo *BillingInvoiceLineUpdateOne) ClearBillingInvoiceManualLines() *BillingInvoiceLineUpdateOne { - biluo.mutation.ClearBillingInvoiceManualLines() +// ClearManualFeeLine clears the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity. +func (biluo *BillingInvoiceLineUpdateOne) ClearManualFeeLine() *BillingInvoiceLineUpdateOne { + biluo.mutation.ClearManualFeeLine() + return biluo +} + +// ClearManualUsageBasedLine clears the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity. +func (biluo *BillingInvoiceLineUpdateOne) ClearManualUsageBasedLine() *BillingInvoiceLineUpdateOne { + biluo.mutation.ClearManualUsageBasedLine() + return biluo +} + +// ClearParentLine clears the "parent_line" edge to the BillingInvoiceLine entity. +func (biluo *BillingInvoiceLineUpdateOne) ClearParentLine() *BillingInvoiceLineUpdateOne { + biluo.mutation.ClearParentLine() + return biluo +} + +// ClearChildLines clears all "child_lines" edges to the BillingInvoiceLine entity. +func (biluo *BillingInvoiceLineUpdateOne) ClearChildLines() *BillingInvoiceLineUpdateOne { + biluo.mutation.ClearChildLines() + return biluo +} + +// RemoveChildLineIDs removes the "child_lines" edge to BillingInvoiceLine entities by IDs. +func (biluo *BillingInvoiceLineUpdateOne) RemoveChildLineIDs(ids ...string) *BillingInvoiceLineUpdateOne { + biluo.mutation.RemoveChildLineIDs(ids...) return biluo } +// RemoveChildLines removes "child_lines" edges to BillingInvoiceLine entities. +func (biluo *BillingInvoiceLineUpdateOne) RemoveChildLines(b ...*BillingInvoiceLine) *BillingInvoiceLineUpdateOne { + ids := make([]string, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return biluo.RemoveChildLineIDs(ids...) +} + // Where appends a list predicates to the BillingInvoiceLineUpdate builder. func (biluo *BillingInvoiceLineUpdateOne) Where(ps ...predicate.BillingInvoiceLine) *BillingInvoiceLineUpdateOne { biluo.mutation.Where(ps...) @@ -748,11 +1000,6 @@ func (biluo *BillingInvoiceLineUpdateOne) defaults() { // check runs all checks and user-defined validators on the builder. func (biluo *BillingInvoiceLineUpdateOne) check() error { - if v, ok := biluo.mutation.GetType(); ok { - if err := billinginvoiceline.TypeValidator(v); err != nil { - return &ValidationError{Name: "type", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceLine.type": %w`, err)} - } - } if v, ok := biluo.mutation.Status(); ok { if err := billinginvoiceline.StatusValidator(v); err != nil { return &ValidationError{Name: "status", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceLine.status": %w`, err)} @@ -826,9 +1073,6 @@ func (biluo *BillingInvoiceLineUpdateOne) sqlSave(ctx context.Context) (_node *B if value, ok := biluo.mutation.InvoiceAt(); ok { _spec.SetField(billinginvoiceline.FieldInvoiceAt, field.TypeTime, value) } - if value, ok := biluo.mutation.GetType(); ok { - _spec.SetField(billinginvoiceline.FieldType, field.TypeEnum, value) - } if value, ok := biluo.mutation.Status(); ok { _spec.SetField(billinginvoiceline.FieldStatus, field.TypeEnum, value) } @@ -873,12 +1117,12 @@ func (biluo *BillingInvoiceLineUpdateOne) sqlSave(ctx context.Context) (_node *B } _spec.Edges.Add = append(_spec.Edges.Add, edge) } - if biluo.mutation.BillingInvoiceManualLinesCleared() { + if biluo.mutation.ManualFeeLineCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, Inverse: false, - Table: billinginvoiceline.BillingInvoiceManualLinesTable, - Columns: []string{billinginvoiceline.BillingInvoiceManualLinesColumn}, + Table: billinginvoiceline.ManualFeeLineTable, + Columns: []string{billinginvoiceline.ManualFeeLineColumn}, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanuallineconfig.FieldID, field.TypeString), @@ -886,12 +1130,12 @@ func (biluo *BillingInvoiceLineUpdateOne) sqlSave(ctx context.Context) (_node *B } _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } - if nodes := biluo.mutation.BillingInvoiceManualLinesIDs(); len(nodes) > 0 { + if nodes := biluo.mutation.ManualFeeLineIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, Inverse: false, - Table: billinginvoiceline.BillingInvoiceManualLinesTable, - Columns: []string{billinginvoiceline.BillingInvoiceManualLinesColumn}, + Table: billinginvoiceline.ManualFeeLineTable, + Columns: []string{billinginvoiceline.ManualFeeLineColumn}, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanuallineconfig.FieldID, field.TypeString), @@ -902,6 +1146,109 @@ func (biluo *BillingInvoiceLineUpdateOne) sqlSave(ctx context.Context) (_node *B } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if biluo.mutation.ManualUsageBasedLineCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: billinginvoiceline.ManualUsageBasedLineTable, + Columns: []string{billinginvoiceline.ManualUsageBasedLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := biluo.mutation.ManualUsageBasedLineIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: billinginvoiceline.ManualUsageBasedLineTable, + Columns: []string{billinginvoiceline.ManualUsageBasedLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if biluo.mutation.ParentLineCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: billinginvoiceline.ParentLineTable, + Columns: []string{billinginvoiceline.ParentLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := biluo.mutation.ParentLineIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: billinginvoiceline.ParentLineTable, + Columns: []string{billinginvoiceline.ParentLineColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if biluo.mutation.ChildLinesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := biluo.mutation.RemovedChildLinesIDs(); len(nodes) > 0 && !biluo.mutation.ChildLinesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := biluo.mutation.ChildLinesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: billinginvoiceline.ChildLinesTable, + Columns: []string{billinginvoiceline.ChildLinesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(billinginvoiceline.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &BillingInvoiceLine{config: biluo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig.go new file mode 100644 index 000000000..eba2cd1bd --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig.go @@ -0,0 +1,137 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" +) + +// BillingInvoiceManualUsageBasedLineConfig is the model entity for the BillingInvoiceManualUsageBasedLineConfig schema. +type BillingInvoiceManualUsageBasedLineConfig struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Namespace holds the value of the "namespace" field. + Namespace string `json:"namespace,omitempty"` + // PriceType holds the value of the "price_type" field. + PriceType plan.PriceType `json:"price_type,omitempty"` + // FeatureKey holds the value of the "feature_key" field. + FeatureKey string `json:"feature_key,omitempty"` + // Price holds the value of the "price" field. + Price *plan.Price `json:"price,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*BillingInvoiceManualUsageBasedLineConfig) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case billinginvoicemanualusagebasedlineconfig.FieldID, billinginvoicemanualusagebasedlineconfig.FieldNamespace, billinginvoicemanualusagebasedlineconfig.FieldPriceType, billinginvoicemanualusagebasedlineconfig.FieldFeatureKey: + values[i] = new(sql.NullString) + case billinginvoicemanualusagebasedlineconfig.FieldPrice: + values[i] = billinginvoicemanualusagebasedlineconfig.ValueScanner.Price.ScanValue() + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the BillingInvoiceManualUsageBasedLineConfig fields. +func (bimublc *BillingInvoiceManualUsageBasedLineConfig) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case billinginvoicemanualusagebasedlineconfig.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + bimublc.ID = value.String + } + case billinginvoicemanualusagebasedlineconfig.FieldNamespace: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field namespace", values[i]) + } else if value.Valid { + bimublc.Namespace = value.String + } + case billinginvoicemanualusagebasedlineconfig.FieldPriceType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field price_type", values[i]) + } else if value.Valid { + bimublc.PriceType = plan.PriceType(value.String) + } + case billinginvoicemanualusagebasedlineconfig.FieldFeatureKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field feature_key", values[i]) + } else if value.Valid { + bimublc.FeatureKey = value.String + } + case billinginvoicemanualusagebasedlineconfig.FieldPrice: + if value, err := billinginvoicemanualusagebasedlineconfig.ValueScanner.Price.FromValue(values[i]); err != nil { + return err + } else { + bimublc.Price = value + } + default: + bimublc.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the BillingInvoiceManualUsageBasedLineConfig. +// This includes values selected through modifiers, order, etc. +func (bimublc *BillingInvoiceManualUsageBasedLineConfig) Value(name string) (ent.Value, error) { + return bimublc.selectValues.Get(name) +} + +// Update returns a builder for updating this BillingInvoiceManualUsageBasedLineConfig. +// Note that you need to call BillingInvoiceManualUsageBasedLineConfig.Unwrap() before calling this method if this BillingInvoiceManualUsageBasedLineConfig +// was returned from a transaction, and the transaction was committed or rolled back. +func (bimublc *BillingInvoiceManualUsageBasedLineConfig) Update() *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + return NewBillingInvoiceManualUsageBasedLineConfigClient(bimublc.config).UpdateOne(bimublc) +} + +// Unwrap unwraps the BillingInvoiceManualUsageBasedLineConfig entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (bimublc *BillingInvoiceManualUsageBasedLineConfig) Unwrap() *BillingInvoiceManualUsageBasedLineConfig { + _tx, ok := bimublc.config.driver.(*txDriver) + if !ok { + panic("db: BillingInvoiceManualUsageBasedLineConfig is not a transactional entity") + } + bimublc.config.driver = _tx.drv + return bimublc +} + +// String implements the fmt.Stringer. +func (bimublc *BillingInvoiceManualUsageBasedLineConfig) String() string { + var builder strings.Builder + builder.WriteString("BillingInvoiceManualUsageBasedLineConfig(") + builder.WriteString(fmt.Sprintf("id=%v, ", bimublc.ID)) + builder.WriteString("namespace=") + builder.WriteString(bimublc.Namespace) + builder.WriteString(", ") + builder.WriteString("price_type=") + builder.WriteString(fmt.Sprintf("%v", bimublc.PriceType)) + builder.WriteString(", ") + builder.WriteString("feature_key=") + builder.WriteString(bimublc.FeatureKey) + builder.WriteString(", ") + builder.WriteString("price=") + builder.WriteString(fmt.Sprintf("%v", bimublc.Price)) + builder.WriteByte(')') + return builder.String() +} + +// BillingInvoiceManualUsageBasedLineConfigs is a parsable slice of BillingInvoiceManualUsageBasedLineConfig. +type BillingInvoiceManualUsageBasedLineConfigs []*BillingInvoiceManualUsageBasedLineConfig diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig/billinginvoicemanualusagebasedlineconfig.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig/billinginvoicemanualusagebasedlineconfig.go new file mode 100644 index 000000000..27540f2f5 --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig/billinginvoicemanualusagebasedlineconfig.go @@ -0,0 +1,98 @@ +// Code generated by ent, DO NOT EDIT. + +package billinginvoicemanualusagebasedlineconfig + +import ( + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" +) + +const ( + // Label holds the string label denoting the billinginvoicemanualusagebasedlineconfig type in the database. + Label = "billing_invoice_manual_usage_based_line_config" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldNamespace holds the string denoting the namespace field in the database. + FieldNamespace = "namespace" + // FieldPriceType holds the string denoting the price_type field in the database. + FieldPriceType = "price_type" + // FieldFeatureKey holds the string denoting the feature_key field in the database. + FieldFeatureKey = "feature_key" + // FieldPrice holds the string denoting the price field in the database. + FieldPrice = "price" + // Table holds the table name of the billinginvoicemanualusagebasedlineconfig in the database. + Table = "billing_invoice_manual_usage_based_line_configs" +) + +// Columns holds all SQL columns for billinginvoicemanualusagebasedlineconfig fields. +var Columns = []string{ + FieldID, + FieldNamespace, + FieldPriceType, + FieldFeatureKey, + FieldPrice, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. + NamespaceValidator func(string) error + // FeatureKeyValidator is a validator for the "feature_key" field. It is called by the builders before save. + FeatureKeyValidator func(string) error + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() string + // ValueScanner of all BillingInvoiceManualUsageBasedLineConfig fields. + ValueScanner struct { + Price field.TypeValueScanner[*plan.Price] + } +) + +// PriceTypeValidator is a validator for the "price_type" field enum values. It is called by the builders before save. +func PriceTypeValidator(pt plan.PriceType) error { + switch pt { + case "flat", "unit", "tiered": + return nil + default: + return fmt.Errorf("billinginvoicemanualusagebasedlineconfig: invalid enum value for price_type field: %q", pt) + } +} + +// OrderOption defines the ordering options for the BillingInvoiceManualUsageBasedLineConfig queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByNamespace orders the results by the namespace field. +func ByNamespace(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldNamespace, opts...).ToFunc() +} + +// ByPriceType orders the results by the price_type field. +func ByPriceType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPriceType, opts...).ToFunc() +} + +// ByFeatureKey orders the results by the feature_key field. +func ByFeatureKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFeatureKey, opts...).ToFunc() +} + +// ByPrice orders the results by the price field. +func ByPrice(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPrice, opts...).ToFunc() +} diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig/where.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig/where.go new file mode 100644 index 000000000..4a2c3c1f7 --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig/where.go @@ -0,0 +1,249 @@ +// Code generated by ent, DO NOT EDIT. + +package billinginvoicemanualusagebasedlineconfig + +import ( + "entgo.io/ent/dialect/sql" + "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldContainsFold(FieldID, id)) +} + +// Namespace applies equality check predicate on the "namespace" field. It's identical to NamespaceEQ. +func Namespace(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldNamespace, v)) +} + +// FeatureKey applies equality check predicate on the "feature_key" field. It's identical to FeatureKeyEQ. +func FeatureKey(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldFeatureKey, v)) +} + +// NamespaceEQ applies the EQ predicate on the "namespace" field. +func NamespaceEQ(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldNamespace, v)) +} + +// NamespaceNEQ applies the NEQ predicate on the "namespace" field. +func NamespaceNEQ(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNEQ(FieldNamespace, v)) +} + +// NamespaceIn applies the In predicate on the "namespace" field. +func NamespaceIn(vs ...string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldIn(FieldNamespace, vs...)) +} + +// NamespaceNotIn applies the NotIn predicate on the "namespace" field. +func NamespaceNotIn(vs ...string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNotIn(FieldNamespace, vs...)) +} + +// NamespaceGT applies the GT predicate on the "namespace" field. +func NamespaceGT(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldGT(FieldNamespace, v)) +} + +// NamespaceGTE applies the GTE predicate on the "namespace" field. +func NamespaceGTE(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldGTE(FieldNamespace, v)) +} + +// NamespaceLT applies the LT predicate on the "namespace" field. +func NamespaceLT(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldLT(FieldNamespace, v)) +} + +// NamespaceLTE applies the LTE predicate on the "namespace" field. +func NamespaceLTE(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldLTE(FieldNamespace, v)) +} + +// NamespaceContains applies the Contains predicate on the "namespace" field. +func NamespaceContains(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldContains(FieldNamespace, v)) +} + +// NamespaceHasPrefix applies the HasPrefix predicate on the "namespace" field. +func NamespaceHasPrefix(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldHasPrefix(FieldNamespace, v)) +} + +// NamespaceHasSuffix applies the HasSuffix predicate on the "namespace" field. +func NamespaceHasSuffix(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldHasSuffix(FieldNamespace, v)) +} + +// NamespaceEqualFold applies the EqualFold predicate on the "namespace" field. +func NamespaceEqualFold(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEqualFold(FieldNamespace, v)) +} + +// NamespaceContainsFold applies the ContainsFold predicate on the "namespace" field. +func NamespaceContainsFold(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldContainsFold(FieldNamespace, v)) +} + +// PriceTypeEQ applies the EQ predicate on the "price_type" field. +func PriceTypeEQ(v plan.PriceType) predicate.BillingInvoiceManualUsageBasedLineConfig { + vc := v + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldPriceType, vc)) +} + +// PriceTypeNEQ applies the NEQ predicate on the "price_type" field. +func PriceTypeNEQ(v plan.PriceType) predicate.BillingInvoiceManualUsageBasedLineConfig { + vc := v + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNEQ(FieldPriceType, vc)) +} + +// PriceTypeIn applies the In predicate on the "price_type" field. +func PriceTypeIn(vs ...plan.PriceType) predicate.BillingInvoiceManualUsageBasedLineConfig { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldIn(FieldPriceType, v...)) +} + +// PriceTypeNotIn applies the NotIn predicate on the "price_type" field. +func PriceTypeNotIn(vs ...plan.PriceType) predicate.BillingInvoiceManualUsageBasedLineConfig { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNotIn(FieldPriceType, v...)) +} + +// FeatureKeyEQ applies the EQ predicate on the "feature_key" field. +func FeatureKeyEQ(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEQ(FieldFeatureKey, v)) +} + +// FeatureKeyNEQ applies the NEQ predicate on the "feature_key" field. +func FeatureKeyNEQ(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNEQ(FieldFeatureKey, v)) +} + +// FeatureKeyIn applies the In predicate on the "feature_key" field. +func FeatureKeyIn(vs ...string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldIn(FieldFeatureKey, vs...)) +} + +// FeatureKeyNotIn applies the NotIn predicate on the "feature_key" field. +func FeatureKeyNotIn(vs ...string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldNotIn(FieldFeatureKey, vs...)) +} + +// FeatureKeyGT applies the GT predicate on the "feature_key" field. +func FeatureKeyGT(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldGT(FieldFeatureKey, v)) +} + +// FeatureKeyGTE applies the GTE predicate on the "feature_key" field. +func FeatureKeyGTE(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldGTE(FieldFeatureKey, v)) +} + +// FeatureKeyLT applies the LT predicate on the "feature_key" field. +func FeatureKeyLT(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldLT(FieldFeatureKey, v)) +} + +// FeatureKeyLTE applies the LTE predicate on the "feature_key" field. +func FeatureKeyLTE(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldLTE(FieldFeatureKey, v)) +} + +// FeatureKeyContains applies the Contains predicate on the "feature_key" field. +func FeatureKeyContains(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldContains(FieldFeatureKey, v)) +} + +// FeatureKeyHasPrefix applies the HasPrefix predicate on the "feature_key" field. +func FeatureKeyHasPrefix(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldHasPrefix(FieldFeatureKey, v)) +} + +// FeatureKeyHasSuffix applies the HasSuffix predicate on the "feature_key" field. +func FeatureKeyHasSuffix(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldHasSuffix(FieldFeatureKey, v)) +} + +// FeatureKeyEqualFold applies the EqualFold predicate on the "feature_key" field. +func FeatureKeyEqualFold(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldEqualFold(FieldFeatureKey, v)) +} + +// FeatureKeyContainsFold applies the ContainsFold predicate on the "feature_key" field. +func FeatureKeyContainsFold(v string) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.FieldContainsFold(FieldFeatureKey, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.BillingInvoiceManualUsageBasedLineConfig) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.BillingInvoiceManualUsageBasedLineConfig) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.BillingInvoiceManualUsageBasedLineConfig) predicate.BillingInvoiceManualUsageBasedLineConfig { + return predicate.BillingInvoiceManualUsageBasedLineConfig(sql.NotPredicates(p)) +} diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_create.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_create.go new file mode 100644 index 000000000..b26ffa31a --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_create.go @@ -0,0 +1,629 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" +) + +// BillingInvoiceManualUsageBasedLineConfigCreate is the builder for creating a BillingInvoiceManualUsageBasedLineConfig entity. +type BillingInvoiceManualUsageBasedLineConfigCreate struct { + config + mutation *BillingInvoiceManualUsageBasedLineConfigMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetNamespace sets the "namespace" field. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SetNamespace(s string) *BillingInvoiceManualUsageBasedLineConfigCreate { + bimublcc.mutation.SetNamespace(s) + return bimublcc +} + +// SetPriceType sets the "price_type" field. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SetPriceType(pt plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigCreate { + bimublcc.mutation.SetPriceType(pt) + return bimublcc +} + +// SetFeatureKey sets the "feature_key" field. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SetFeatureKey(s string) *BillingInvoiceManualUsageBasedLineConfigCreate { + bimublcc.mutation.SetFeatureKey(s) + return bimublcc +} + +// SetPrice sets the "price" field. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SetPrice(pl *plan.Price) *BillingInvoiceManualUsageBasedLineConfigCreate { + bimublcc.mutation.SetPrice(pl) + return bimublcc +} + +// SetID sets the "id" field. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SetID(s string) *BillingInvoiceManualUsageBasedLineConfigCreate { + bimublcc.mutation.SetID(s) + return bimublcc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SetNillableID(s *string) *BillingInvoiceManualUsageBasedLineConfigCreate { + if s != nil { + bimublcc.SetID(*s) + } + return bimublcc +} + +// Mutation returns the BillingInvoiceManualUsageBasedLineConfigMutation object of the builder. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) Mutation() *BillingInvoiceManualUsageBasedLineConfigMutation { + return bimublcc.mutation +} + +// Save creates the BillingInvoiceManualUsageBasedLineConfig in the database. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) Save(ctx context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + bimublcc.defaults() + return withHooks(ctx, bimublcc.sqlSave, bimublcc.mutation, bimublcc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) SaveX(ctx context.Context) *BillingInvoiceManualUsageBasedLineConfig { + v, err := bimublcc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) Exec(ctx context.Context) error { + _, err := bimublcc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) ExecX(ctx context.Context) { + if err := bimublcc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) defaults() { + if _, ok := bimublcc.mutation.ID(); !ok { + v := billinginvoicemanualusagebasedlineconfig.DefaultID() + bimublcc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) check() error { + if _, ok := bimublcc.mutation.Namespace(); !ok { + return &ValidationError{Name: "namespace", err: errors.New(`db: missing required field "BillingInvoiceManualUsageBasedLineConfig.namespace"`)} + } + if v, ok := bimublcc.mutation.Namespace(); ok { + if err := billinginvoicemanualusagebasedlineconfig.NamespaceValidator(v); err != nil { + return &ValidationError{Name: "namespace", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.namespace": %w`, err)} + } + } + if _, ok := bimublcc.mutation.PriceType(); !ok { + return &ValidationError{Name: "price_type", err: errors.New(`db: missing required field "BillingInvoiceManualUsageBasedLineConfig.price_type"`)} + } + if v, ok := bimublcc.mutation.PriceType(); ok { + if err := billinginvoicemanualusagebasedlineconfig.PriceTypeValidator(v); err != nil { + return &ValidationError{Name: "price_type", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.price_type": %w`, err)} + } + } + if _, ok := bimublcc.mutation.FeatureKey(); !ok { + return &ValidationError{Name: "feature_key", err: errors.New(`db: missing required field "BillingInvoiceManualUsageBasedLineConfig.feature_key"`)} + } + if v, ok := bimublcc.mutation.FeatureKey(); ok { + if err := billinginvoicemanualusagebasedlineconfig.FeatureKeyValidator(v); err != nil { + return &ValidationError{Name: "feature_key", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.feature_key": %w`, err)} + } + } + if _, ok := bimublcc.mutation.Price(); !ok { + return &ValidationError{Name: "price", err: errors.New(`db: missing required field "BillingInvoiceManualUsageBasedLineConfig.price"`)} + } + if v, ok := bimublcc.mutation.Price(); ok { + if err := v.Validate(); err != nil { + return &ValidationError{Name: "price", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.price": %w`, err)} + } + } + return nil +} + +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) sqlSave(ctx context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + if err := bimublcc.check(); err != nil { + return nil, err + } + _node, _spec, err := bimublcc.createSpec() + if err != nil { + return nil, err + } + if err := sqlgraph.CreateNode(ctx, bimublcc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected BillingInvoiceManualUsageBasedLineConfig.ID type: %T", _spec.ID.Value) + } + } + bimublcc.mutation.id = &_node.ID + bimublcc.mutation.done = true + return _node, nil +} + +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) createSpec() (*BillingInvoiceManualUsageBasedLineConfig, *sqlgraph.CreateSpec, error) { + var ( + _node = &BillingInvoiceManualUsageBasedLineConfig{config: bimublcc.config} + _spec = sqlgraph.NewCreateSpec(billinginvoicemanualusagebasedlineconfig.Table, sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString)) + ) + _spec.OnConflict = bimublcc.conflict + if id, ok := bimublcc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := bimublcc.mutation.Namespace(); ok { + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldNamespace, field.TypeString, value) + _node.Namespace = value + } + if value, ok := bimublcc.mutation.PriceType(); ok { + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldPriceType, field.TypeEnum, value) + _node.PriceType = value + } + if value, ok := bimublcc.mutation.FeatureKey(); ok { + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldFeatureKey, field.TypeString, value) + _node.FeatureKey = value + } + if value, ok := bimublcc.mutation.Price(); ok { + vv, err := billinginvoicemanualusagebasedlineconfig.ValueScanner.Price.Value(value) + if err != nil { + return nil, nil, err + } + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldPrice, field.TypeString, vv) + _node.Price = value + } + return _node, _spec, nil +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// SetNamespace(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BillingInvoiceManualUsageBasedLineConfigUpsert) { +// SetNamespace(v+v). +// }). +// Exec(ctx) +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) OnConflict(opts ...sql.ConflictOption) *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + bimublcc.conflict = opts + return &BillingInvoiceManualUsageBasedLineConfigUpsertOne{ + create: bimublcc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (bimublcc *BillingInvoiceManualUsageBasedLineConfigCreate) OnConflictColumns(columns ...string) *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + bimublcc.conflict = append(bimublcc.conflict, sql.ConflictColumns(columns...)) + return &BillingInvoiceManualUsageBasedLineConfigUpsertOne{ + create: bimublcc, + } +} + +type ( + // BillingInvoiceManualUsageBasedLineConfigUpsertOne is the builder for "upsert"-ing + // one BillingInvoiceManualUsageBasedLineConfig node. + BillingInvoiceManualUsageBasedLineConfigUpsertOne struct { + create *BillingInvoiceManualUsageBasedLineConfigCreate + } + + // BillingInvoiceManualUsageBasedLineConfigUpsert is the "OnConflict" setter. + BillingInvoiceManualUsageBasedLineConfigUpsert struct { + *sql.UpdateSet + } +) + +// SetPriceType sets the "price_type" field. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsert) SetPriceType(v plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpsert { + u.Set(billinginvoicemanualusagebasedlineconfig.FieldPriceType, v) + return u +} + +// UpdatePriceType sets the "price_type" field to the value that was provided on create. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsert) UpdatePriceType() *BillingInvoiceManualUsageBasedLineConfigUpsert { + u.SetExcluded(billinginvoicemanualusagebasedlineconfig.FieldPriceType) + return u +} + +// SetPrice sets the "price" field. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsert) SetPrice(v *plan.Price) *BillingInvoiceManualUsageBasedLineConfigUpsert { + u.Set(billinginvoicemanualusagebasedlineconfig.FieldPrice, v) + return u +} + +// UpdatePrice sets the "price" field to the value that was provided on create. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsert) UpdatePrice() *BillingInvoiceManualUsageBasedLineConfigUpsert { + u.SetExcluded(billinginvoicemanualusagebasedlineconfig.FieldPrice) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) UpdateNewValues() *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldID) + } + if _, exists := u.create.mutation.Namespace(); exists { + s.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldNamespace) + } + if _, exists := u.create.mutation.FeatureKey(); exists { + s.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldFeatureKey) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) Ignore() *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) DoNothing() *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BillingInvoiceManualUsageBasedLineConfigCreate.OnConflict +// documentation for more info. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) Update(set func(*BillingInvoiceManualUsageBasedLineConfigUpsert)) *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BillingInvoiceManualUsageBasedLineConfigUpsert{UpdateSet: update}) + })) + return u +} + +// SetPriceType sets the "price_type" field. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) SetPriceType(v plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.SetPriceType(v) + }) +} + +// UpdatePriceType sets the "price_type" field to the value that was provided on create. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) UpdatePriceType() *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.UpdatePriceType() + }) +} + +// SetPrice sets the "price" field. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) SetPrice(v *plan.Price) *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.SetPrice(v) + }) +} + +// UpdatePrice sets the "price" field to the value that was provided on create. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) UpdatePrice() *BillingInvoiceManualUsageBasedLineConfigUpsertOne { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.UpdatePrice() + }) +} + +// Exec executes the query. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("db: missing options for BillingInvoiceManualUsageBasedLineConfigCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) ID(ctx context.Context) (id string, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("db: BillingInvoiceManualUsageBasedLineConfigUpsertOne.ID is not supported by MySQL driver. Use BillingInvoiceManualUsageBasedLineConfigUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertOne) IDX(ctx context.Context) string { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// BillingInvoiceManualUsageBasedLineConfigCreateBulk is the builder for creating many BillingInvoiceManualUsageBasedLineConfig entities in bulk. +type BillingInvoiceManualUsageBasedLineConfigCreateBulk struct { + config + err error + builders []*BillingInvoiceManualUsageBasedLineConfigCreate + conflict []sql.ConflictOption +} + +// Save creates the BillingInvoiceManualUsageBasedLineConfig entities in the database. +func (bimublccb *BillingInvoiceManualUsageBasedLineConfigCreateBulk) Save(ctx context.Context) ([]*BillingInvoiceManualUsageBasedLineConfig, error) { + if bimublccb.err != nil { + return nil, bimublccb.err + } + specs := make([]*sqlgraph.CreateSpec, len(bimublccb.builders)) + nodes := make([]*BillingInvoiceManualUsageBasedLineConfig, len(bimublccb.builders)) + mutators := make([]Mutator, len(bimublccb.builders)) + for i := range bimublccb.builders { + func(i int, root context.Context) { + builder := bimublccb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*BillingInvoiceManualUsageBasedLineConfigMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i], err = builder.createSpec() + if err != nil { + return nil, err + } + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, bimublccb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = bimublccb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, bimublccb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, bimublccb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (bimublccb *BillingInvoiceManualUsageBasedLineConfigCreateBulk) SaveX(ctx context.Context) []*BillingInvoiceManualUsageBasedLineConfig { + v, err := bimublccb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (bimublccb *BillingInvoiceManualUsageBasedLineConfigCreateBulk) Exec(ctx context.Context) error { + _, err := bimublccb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bimublccb *BillingInvoiceManualUsageBasedLineConfigCreateBulk) ExecX(ctx context.Context) { + if err := bimublccb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BillingInvoiceManualUsageBasedLineConfig.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BillingInvoiceManualUsageBasedLineConfigUpsert) { +// SetNamespace(v+v). +// }). +// Exec(ctx) +func (bimublccb *BillingInvoiceManualUsageBasedLineConfigCreateBulk) OnConflict(opts ...sql.ConflictOption) *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + bimublccb.conflict = opts + return &BillingInvoiceManualUsageBasedLineConfigUpsertBulk{ + create: bimublccb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (bimublccb *BillingInvoiceManualUsageBasedLineConfigCreateBulk) OnConflictColumns(columns ...string) *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + bimublccb.conflict = append(bimublccb.conflict, sql.ConflictColumns(columns...)) + return &BillingInvoiceManualUsageBasedLineConfigUpsertBulk{ + create: bimublccb, + } +} + +// BillingInvoiceManualUsageBasedLineConfigUpsertBulk is the builder for "upsert"-ing +// a bulk of BillingInvoiceManualUsageBasedLineConfig nodes. +type BillingInvoiceManualUsageBasedLineConfigUpsertBulk struct { + create *BillingInvoiceManualUsageBasedLineConfigCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) UpdateNewValues() *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldID) + } + if _, exists := b.mutation.Namespace(); exists { + s.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldNamespace) + } + if _, exists := b.mutation.FeatureKey(); exists { + s.SetIgnore(billinginvoicemanualusagebasedlineconfig.FieldFeatureKey) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BillingInvoiceManualUsageBasedLineConfig.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) Ignore() *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) DoNothing() *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BillingInvoiceManualUsageBasedLineConfigCreateBulk.OnConflict +// documentation for more info. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) Update(set func(*BillingInvoiceManualUsageBasedLineConfigUpsert)) *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BillingInvoiceManualUsageBasedLineConfigUpsert{UpdateSet: update}) + })) + return u +} + +// SetPriceType sets the "price_type" field. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) SetPriceType(v plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.SetPriceType(v) + }) +} + +// UpdatePriceType sets the "price_type" field to the value that was provided on create. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) UpdatePriceType() *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.UpdatePriceType() + }) +} + +// SetPrice sets the "price" field. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) SetPrice(v *plan.Price) *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.SetPrice(v) + }) +} + +// UpdatePrice sets the "price" field to the value that was provided on create. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) UpdatePrice() *BillingInvoiceManualUsageBasedLineConfigUpsertBulk { + return u.Update(func(s *BillingInvoiceManualUsageBasedLineConfigUpsert) { + s.UpdatePrice() + }) +} + +// Exec executes the query. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("db: OnConflict was set for builder %d. Set it on the BillingInvoiceManualUsageBasedLineConfigCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("db: missing options for BillingInvoiceManualUsageBasedLineConfigCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BillingInvoiceManualUsageBasedLineConfigUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_delete.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_delete.go new file mode 100644 index 000000000..5e454fd57 --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" +) + +// BillingInvoiceManualUsageBasedLineConfigDelete is the builder for deleting a BillingInvoiceManualUsageBasedLineConfig entity. +type BillingInvoiceManualUsageBasedLineConfigDelete struct { + config + hooks []Hook + mutation *BillingInvoiceManualUsageBasedLineConfigMutation +} + +// Where appends a list predicates to the BillingInvoiceManualUsageBasedLineConfigDelete builder. +func (bimublcd *BillingInvoiceManualUsageBasedLineConfigDelete) Where(ps ...predicate.BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigDelete { + bimublcd.mutation.Where(ps...) + return bimublcd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (bimublcd *BillingInvoiceManualUsageBasedLineConfigDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, bimublcd.sqlExec, bimublcd.mutation, bimublcd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (bimublcd *BillingInvoiceManualUsageBasedLineConfigDelete) ExecX(ctx context.Context) int { + n, err := bimublcd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (bimublcd *BillingInvoiceManualUsageBasedLineConfigDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(billinginvoicemanualusagebasedlineconfig.Table, sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString)) + if ps := bimublcd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, bimublcd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + bimublcd.mutation.done = true + return affected, err +} + +// BillingInvoiceManualUsageBasedLineConfigDeleteOne is the builder for deleting a single BillingInvoiceManualUsageBasedLineConfig entity. +type BillingInvoiceManualUsageBasedLineConfigDeleteOne struct { + bimublcd *BillingInvoiceManualUsageBasedLineConfigDelete +} + +// Where appends a list predicates to the BillingInvoiceManualUsageBasedLineConfigDelete builder. +func (bimublcdo *BillingInvoiceManualUsageBasedLineConfigDeleteOne) Where(ps ...predicate.BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigDeleteOne { + bimublcdo.bimublcd.mutation.Where(ps...) + return bimublcdo +} + +// Exec executes the deletion query. +func (bimublcdo *BillingInvoiceManualUsageBasedLineConfigDeleteOne) Exec(ctx context.Context) error { + n, err := bimublcdo.bimublcd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (bimublcdo *BillingInvoiceManualUsageBasedLineConfigDeleteOne) ExecX(ctx context.Context) { + if err := bimublcdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_query.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_query.go new file mode 100644 index 000000000..0a43adb90 --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_query.go @@ -0,0 +1,564 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" +) + +// BillingInvoiceManualUsageBasedLineConfigQuery is the builder for querying BillingInvoiceManualUsageBasedLineConfig entities. +type BillingInvoiceManualUsageBasedLineConfigQuery struct { + config + ctx *QueryContext + order []billinginvoicemanualusagebasedlineconfig.OrderOption + inters []Interceptor + predicates []predicate.BillingInvoiceManualUsageBasedLineConfig + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the BillingInvoiceManualUsageBasedLineConfigQuery builder. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Where(ps ...predicate.BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigQuery { + bimublcq.predicates = append(bimublcq.predicates, ps...) + return bimublcq +} + +// Limit the number of records to be returned by this query. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Limit(limit int) *BillingInvoiceManualUsageBasedLineConfigQuery { + bimublcq.ctx.Limit = &limit + return bimublcq +} + +// Offset to start from. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Offset(offset int) *BillingInvoiceManualUsageBasedLineConfigQuery { + bimublcq.ctx.Offset = &offset + return bimublcq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Unique(unique bool) *BillingInvoiceManualUsageBasedLineConfigQuery { + bimublcq.ctx.Unique = &unique + return bimublcq +} + +// Order specifies how the records should be ordered. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Order(o ...billinginvoicemanualusagebasedlineconfig.OrderOption) *BillingInvoiceManualUsageBasedLineConfigQuery { + bimublcq.order = append(bimublcq.order, o...) + return bimublcq +} + +// First returns the first BillingInvoiceManualUsageBasedLineConfig entity from the query. +// Returns a *NotFoundError when no BillingInvoiceManualUsageBasedLineConfig was found. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) First(ctx context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + nodes, err := bimublcq.Limit(1).All(setContextOp(ctx, bimublcq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) FirstX(ctx context.Context) *BillingInvoiceManualUsageBasedLineConfig { + node, err := bimublcq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first BillingInvoiceManualUsageBasedLineConfig ID from the query. +// Returns a *NotFoundError when no BillingInvoiceManualUsageBasedLineConfig ID was found. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = bimublcq.Limit(1).IDs(setContextOp(ctx, bimublcq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) FirstIDX(ctx context.Context) string { + id, err := bimublcq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single BillingInvoiceManualUsageBasedLineConfig entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one BillingInvoiceManualUsageBasedLineConfig entity is found. +// Returns a *NotFoundError when no BillingInvoiceManualUsageBasedLineConfig entities are found. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Only(ctx context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + nodes, err := bimublcq.Limit(2).All(setContextOp(ctx, bimublcq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + default: + return nil, &NotSingularError{billinginvoicemanualusagebasedlineconfig.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) OnlyX(ctx context.Context) *BillingInvoiceManualUsageBasedLineConfig { + node, err := bimublcq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only BillingInvoiceManualUsageBasedLineConfig ID in the query. +// Returns a *NotSingularError when more than one BillingInvoiceManualUsageBasedLineConfig ID is found. +// Returns a *NotFoundError when no entities are found. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = bimublcq.Limit(2).IDs(setContextOp(ctx, bimublcq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + default: + err = &NotSingularError{billinginvoicemanualusagebasedlineconfig.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) OnlyIDX(ctx context.Context) string { + id, err := bimublcq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of BillingInvoiceManualUsageBasedLineConfigs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) All(ctx context.Context) ([]*BillingInvoiceManualUsageBasedLineConfig, error) { + ctx = setContextOp(ctx, bimublcq.ctx, ent.OpQueryAll) + if err := bimublcq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*BillingInvoiceManualUsageBasedLineConfig, *BillingInvoiceManualUsageBasedLineConfigQuery]() + return withInterceptors[[]*BillingInvoiceManualUsageBasedLineConfig](ctx, bimublcq, qr, bimublcq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) AllX(ctx context.Context) []*BillingInvoiceManualUsageBasedLineConfig { + nodes, err := bimublcq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of BillingInvoiceManualUsageBasedLineConfig IDs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) IDs(ctx context.Context) (ids []string, err error) { + if bimublcq.ctx.Unique == nil && bimublcq.path != nil { + bimublcq.Unique(true) + } + ctx = setContextOp(ctx, bimublcq.ctx, ent.OpQueryIDs) + if err = bimublcq.Select(billinginvoicemanualusagebasedlineconfig.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) IDsX(ctx context.Context) []string { + ids, err := bimublcq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, bimublcq.ctx, ent.OpQueryCount) + if err := bimublcq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, bimublcq, querierCount[*BillingInvoiceManualUsageBasedLineConfigQuery](), bimublcq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) CountX(ctx context.Context) int { + count, err := bimublcq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, bimublcq.ctx, ent.OpQueryExist) + switch _, err := bimublcq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("db: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) ExistX(ctx context.Context) bool { + exist, err := bimublcq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the BillingInvoiceManualUsageBasedLineConfigQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Clone() *BillingInvoiceManualUsageBasedLineConfigQuery { + if bimublcq == nil { + return nil + } + return &BillingInvoiceManualUsageBasedLineConfigQuery{ + config: bimublcq.config, + ctx: bimublcq.ctx.Clone(), + order: append([]billinginvoicemanualusagebasedlineconfig.OrderOption{}, bimublcq.order...), + inters: append([]Interceptor{}, bimublcq.inters...), + predicates: append([]predicate.BillingInvoiceManualUsageBasedLineConfig{}, bimublcq.predicates...), + // clone intermediate query. + sql: bimublcq.sql.Clone(), + path: bimublcq.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Namespace string `json:"namespace,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.BillingInvoiceManualUsageBasedLineConfig.Query(). +// GroupBy(billinginvoicemanualusagebasedlineconfig.FieldNamespace). +// Aggregate(db.Count()). +// Scan(ctx, &v) +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) GroupBy(field string, fields ...string) *BillingInvoiceManualUsageBasedLineConfigGroupBy { + bimublcq.ctx.Fields = append([]string{field}, fields...) + grbuild := &BillingInvoiceManualUsageBasedLineConfigGroupBy{build: bimublcq} + grbuild.flds = &bimublcq.ctx.Fields + grbuild.label = billinginvoicemanualusagebasedlineconfig.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Namespace string `json:"namespace,omitempty"` +// } +// +// client.BillingInvoiceManualUsageBasedLineConfig.Query(). +// Select(billinginvoicemanualusagebasedlineconfig.FieldNamespace). +// Scan(ctx, &v) +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Select(fields ...string) *BillingInvoiceManualUsageBasedLineConfigSelect { + bimublcq.ctx.Fields = append(bimublcq.ctx.Fields, fields...) + sbuild := &BillingInvoiceManualUsageBasedLineConfigSelect{BillingInvoiceManualUsageBasedLineConfigQuery: bimublcq} + sbuild.label = billinginvoicemanualusagebasedlineconfig.Label + sbuild.flds, sbuild.scan = &bimublcq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a BillingInvoiceManualUsageBasedLineConfigSelect configured with the given aggregations. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) Aggregate(fns ...AggregateFunc) *BillingInvoiceManualUsageBasedLineConfigSelect { + return bimublcq.Select().Aggregate(fns...) +} + +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) prepareQuery(ctx context.Context) error { + for _, inter := range bimublcq.inters { + if inter == nil { + return fmt.Errorf("db: uninitialized interceptor (forgotten import db/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, bimublcq); err != nil { + return err + } + } + } + for _, f := range bimublcq.ctx.Fields { + if !billinginvoicemanualusagebasedlineconfig.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("db: invalid field %q for query", f)} + } + } + if bimublcq.path != nil { + prev, err := bimublcq.path(ctx) + if err != nil { + return err + } + bimublcq.sql = prev + } + return nil +} + +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*BillingInvoiceManualUsageBasedLineConfig, error) { + var ( + nodes = []*BillingInvoiceManualUsageBasedLineConfig{} + _spec = bimublcq.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*BillingInvoiceManualUsageBasedLineConfig).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &BillingInvoiceManualUsageBasedLineConfig{config: bimublcq.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(bimublcq.modifiers) > 0 { + _spec.Modifiers = bimublcq.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, bimublcq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) sqlCount(ctx context.Context) (int, error) { + _spec := bimublcq.querySpec() + if len(bimublcq.modifiers) > 0 { + _spec.Modifiers = bimublcq.modifiers + } + _spec.Node.Columns = bimublcq.ctx.Fields + if len(bimublcq.ctx.Fields) > 0 { + _spec.Unique = bimublcq.ctx.Unique != nil && *bimublcq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, bimublcq.driver, _spec) +} + +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(billinginvoicemanualusagebasedlineconfig.Table, billinginvoicemanualusagebasedlineconfig.Columns, sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString)) + _spec.From = bimublcq.sql + if unique := bimublcq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if bimublcq.path != nil { + _spec.Unique = true + } + if fields := bimublcq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, billinginvoicemanualusagebasedlineconfig.FieldID) + for i := range fields { + if fields[i] != billinginvoicemanualusagebasedlineconfig.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := bimublcq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := bimublcq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := bimublcq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := bimublcq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(bimublcq.driver.Dialect()) + t1 := builder.Table(billinginvoicemanualusagebasedlineconfig.Table) + columns := bimublcq.ctx.Fields + if len(columns) == 0 { + columns = billinginvoicemanualusagebasedlineconfig.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if bimublcq.sql != nil { + selector = bimublcq.sql + selector.Select(selector.Columns(columns...)...) + } + if bimublcq.ctx.Unique != nil && *bimublcq.ctx.Unique { + selector.Distinct() + } + for _, m := range bimublcq.modifiers { + m(selector) + } + for _, p := range bimublcq.predicates { + p(selector) + } + for _, p := range bimublcq.order { + p(selector) + } + if offset := bimublcq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := bimublcq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) ForUpdate(opts ...sql.LockOption) *BillingInvoiceManualUsageBasedLineConfigQuery { + if bimublcq.driver.Dialect() == dialect.Postgres { + bimublcq.Unique(false) + } + bimublcq.modifiers = append(bimublcq.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return bimublcq +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (bimublcq *BillingInvoiceManualUsageBasedLineConfigQuery) ForShare(opts ...sql.LockOption) *BillingInvoiceManualUsageBasedLineConfigQuery { + if bimublcq.driver.Dialect() == dialect.Postgres { + bimublcq.Unique(false) + } + bimublcq.modifiers = append(bimublcq.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return bimublcq +} + +// BillingInvoiceManualUsageBasedLineConfigGroupBy is the group-by builder for BillingInvoiceManualUsageBasedLineConfig entities. +type BillingInvoiceManualUsageBasedLineConfigGroupBy struct { + selector + build *BillingInvoiceManualUsageBasedLineConfigQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (bimublcgb *BillingInvoiceManualUsageBasedLineConfigGroupBy) Aggregate(fns ...AggregateFunc) *BillingInvoiceManualUsageBasedLineConfigGroupBy { + bimublcgb.fns = append(bimublcgb.fns, fns...) + return bimublcgb +} + +// Scan applies the selector query and scans the result into the given value. +func (bimublcgb *BillingInvoiceManualUsageBasedLineConfigGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, bimublcgb.build.ctx, ent.OpQueryGroupBy) + if err := bimublcgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BillingInvoiceManualUsageBasedLineConfigQuery, *BillingInvoiceManualUsageBasedLineConfigGroupBy](ctx, bimublcgb.build, bimublcgb, bimublcgb.build.inters, v) +} + +func (bimublcgb *BillingInvoiceManualUsageBasedLineConfigGroupBy) sqlScan(ctx context.Context, root *BillingInvoiceManualUsageBasedLineConfigQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(bimublcgb.fns)) + for _, fn := range bimublcgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*bimublcgb.flds)+len(bimublcgb.fns)) + for _, f := range *bimublcgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*bimublcgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := bimublcgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// BillingInvoiceManualUsageBasedLineConfigSelect is the builder for selecting fields of BillingInvoiceManualUsageBasedLineConfig entities. +type BillingInvoiceManualUsageBasedLineConfigSelect struct { + *BillingInvoiceManualUsageBasedLineConfigQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (bimublcs *BillingInvoiceManualUsageBasedLineConfigSelect) Aggregate(fns ...AggregateFunc) *BillingInvoiceManualUsageBasedLineConfigSelect { + bimublcs.fns = append(bimublcs.fns, fns...) + return bimublcs +} + +// Scan applies the selector query and scans the result into the given value. +func (bimublcs *BillingInvoiceManualUsageBasedLineConfigSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, bimublcs.ctx, ent.OpQuerySelect) + if err := bimublcs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BillingInvoiceManualUsageBasedLineConfigQuery, *BillingInvoiceManualUsageBasedLineConfigSelect](ctx, bimublcs.BillingInvoiceManualUsageBasedLineConfigQuery, bimublcs, bimublcs.inters, v) +} + +func (bimublcs *BillingInvoiceManualUsageBasedLineConfigSelect) sqlScan(ctx context.Context, root *BillingInvoiceManualUsageBasedLineConfigQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(bimublcs.fns)) + for _, fn := range bimublcs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*bimublcs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := bimublcs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_update.go b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_update.go new file mode 100644 index 000000000..722bfd8f2 --- /dev/null +++ b/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig_update.go @@ -0,0 +1,272 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" +) + +// BillingInvoiceManualUsageBasedLineConfigUpdate is the builder for updating BillingInvoiceManualUsageBasedLineConfig entities. +type BillingInvoiceManualUsageBasedLineConfigUpdate struct { + config + hooks []Hook + mutation *BillingInvoiceManualUsageBasedLineConfigMutation +} + +// Where appends a list predicates to the BillingInvoiceManualUsageBasedLineConfigUpdate builder. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) Where(ps ...predicate.BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigUpdate { + bimublcu.mutation.Where(ps...) + return bimublcu +} + +// SetPriceType sets the "price_type" field. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) SetPriceType(pt plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpdate { + bimublcu.mutation.SetPriceType(pt) + return bimublcu +} + +// SetNillablePriceType sets the "price_type" field if the given value is not nil. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) SetNillablePriceType(pt *plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpdate { + if pt != nil { + bimublcu.SetPriceType(*pt) + } + return bimublcu +} + +// SetPrice sets the "price" field. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) SetPrice(pl *plan.Price) *BillingInvoiceManualUsageBasedLineConfigUpdate { + bimublcu.mutation.SetPrice(pl) + return bimublcu +} + +// Mutation returns the BillingInvoiceManualUsageBasedLineConfigMutation object of the builder. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) Mutation() *BillingInvoiceManualUsageBasedLineConfigMutation { + return bimublcu.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, bimublcu.sqlSave, bimublcu.mutation, bimublcu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) SaveX(ctx context.Context) int { + affected, err := bimublcu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) Exec(ctx context.Context) error { + _, err := bimublcu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) ExecX(ctx context.Context) { + if err := bimublcu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) check() error { + if v, ok := bimublcu.mutation.PriceType(); ok { + if err := billinginvoicemanualusagebasedlineconfig.PriceTypeValidator(v); err != nil { + return &ValidationError{Name: "price_type", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.price_type": %w`, err)} + } + } + if v, ok := bimublcu.mutation.Price(); ok { + if err := v.Validate(); err != nil { + return &ValidationError{Name: "price", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.price": %w`, err)} + } + } + return nil +} + +func (bimublcu *BillingInvoiceManualUsageBasedLineConfigUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := bimublcu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(billinginvoicemanualusagebasedlineconfig.Table, billinginvoicemanualusagebasedlineconfig.Columns, sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString)) + if ps := bimublcu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := bimublcu.mutation.PriceType(); ok { + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldPriceType, field.TypeEnum, value) + } + if value, ok := bimublcu.mutation.Price(); ok { + vv, err := billinginvoicemanualusagebasedlineconfig.ValueScanner.Price.Value(value) + if err != nil { + return 0, err + } + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldPrice, field.TypeString, vv) + } + if n, err = sqlgraph.UpdateNodes(ctx, bimublcu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + bimublcu.mutation.done = true + return n, nil +} + +// BillingInvoiceManualUsageBasedLineConfigUpdateOne is the builder for updating a single BillingInvoiceManualUsageBasedLineConfig entity. +type BillingInvoiceManualUsageBasedLineConfigUpdateOne struct { + config + fields []string + hooks []Hook + mutation *BillingInvoiceManualUsageBasedLineConfigMutation +} + +// SetPriceType sets the "price_type" field. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) SetPriceType(pt plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + bimublcuo.mutation.SetPriceType(pt) + return bimublcuo +} + +// SetNillablePriceType sets the "price_type" field if the given value is not nil. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) SetNillablePriceType(pt *plan.PriceType) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + if pt != nil { + bimublcuo.SetPriceType(*pt) + } + return bimublcuo +} + +// SetPrice sets the "price" field. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) SetPrice(pl *plan.Price) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + bimublcuo.mutation.SetPrice(pl) + return bimublcuo +} + +// Mutation returns the BillingInvoiceManualUsageBasedLineConfigMutation object of the builder. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) Mutation() *BillingInvoiceManualUsageBasedLineConfigMutation { + return bimublcuo.mutation +} + +// Where appends a list predicates to the BillingInvoiceManualUsageBasedLineConfigUpdate builder. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) Where(ps ...predicate.BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + bimublcuo.mutation.Where(ps...) + return bimublcuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) Select(field string, fields ...string) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + bimublcuo.fields = append([]string{field}, fields...) + return bimublcuo +} + +// Save executes the query and returns the updated BillingInvoiceManualUsageBasedLineConfig entity. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) Save(ctx context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + return withHooks(ctx, bimublcuo.sqlSave, bimublcuo.mutation, bimublcuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) SaveX(ctx context.Context) *BillingInvoiceManualUsageBasedLineConfig { + node, err := bimublcuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) Exec(ctx context.Context) error { + _, err := bimublcuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) ExecX(ctx context.Context) { + if err := bimublcuo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) check() error { + if v, ok := bimublcuo.mutation.PriceType(); ok { + if err := billinginvoicemanualusagebasedlineconfig.PriceTypeValidator(v); err != nil { + return &ValidationError{Name: "price_type", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.price_type": %w`, err)} + } + } + if v, ok := bimublcuo.mutation.Price(); ok { + if err := v.Validate(); err != nil { + return &ValidationError{Name: "price", err: fmt.Errorf(`db: validator failed for field "BillingInvoiceManualUsageBasedLineConfig.price": %w`, err)} + } + } + return nil +} + +func (bimublcuo *BillingInvoiceManualUsageBasedLineConfigUpdateOne) sqlSave(ctx context.Context) (_node *BillingInvoiceManualUsageBasedLineConfig, err error) { + if err := bimublcuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(billinginvoicemanualusagebasedlineconfig.Table, billinginvoicemanualusagebasedlineconfig.Columns, sqlgraph.NewFieldSpec(billinginvoicemanualusagebasedlineconfig.FieldID, field.TypeString)) + id, ok := bimublcuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`db: missing "BillingInvoiceManualUsageBasedLineConfig.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := bimublcuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, billinginvoicemanualusagebasedlineconfig.FieldID) + for _, f := range fields { + if !billinginvoicemanualusagebasedlineconfig.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("db: invalid field %q for query", f)} + } + if f != billinginvoicemanualusagebasedlineconfig.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := bimublcuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := bimublcuo.mutation.PriceType(); ok { + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldPriceType, field.TypeEnum, value) + } + if value, ok := bimublcuo.mutation.Price(); ok { + vv, err := billinginvoicemanualusagebasedlineconfig.ValueScanner.Price.Value(value) + if err != nil { + return nil, err + } + _spec.SetField(billinginvoicemanualusagebasedlineconfig.FieldPrice, field.TypeString, vv) + } + _node = &BillingInvoiceManualUsageBasedLineConfig{config: bimublcuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, bimublcuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{billinginvoicemanualusagebasedlineconfig.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + bimublcuo.mutation.done = true + return _node, nil +} diff --git a/openmeter/ent/db/client.go b/openmeter/ent/db/client.go index aa0274b37..c9fa42a07 100644 --- a/openmeter/ent/db/client.go +++ b/openmeter/ent/db/client.go @@ -24,6 +24,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicevalidationissue" "github.com/openmeterio/openmeter/openmeter/ent/db/billingprofile" "github.com/openmeterio/openmeter/openmeter/ent/db/billingworkflowconfig" @@ -67,6 +68,8 @@ type Client struct { BillingInvoiceLine *BillingInvoiceLineClient // BillingInvoiceManualLineConfig is the client for interacting with the BillingInvoiceManualLineConfig builders. BillingInvoiceManualLineConfig *BillingInvoiceManualLineConfigClient + // BillingInvoiceManualUsageBasedLineConfig is the client for interacting with the BillingInvoiceManualUsageBasedLineConfig builders. + BillingInvoiceManualUsageBasedLineConfig *BillingInvoiceManualUsageBasedLineConfigClient // BillingInvoiceValidationIssue is the client for interacting with the BillingInvoiceValidationIssue builders. BillingInvoiceValidationIssue *BillingInvoiceValidationIssueClient // BillingProfile is the client for interacting with the BillingProfile builders. @@ -119,6 +122,7 @@ func (c *Client) init() { c.BillingInvoice = NewBillingInvoiceClient(c.config) c.BillingInvoiceLine = NewBillingInvoiceLineClient(c.config) c.BillingInvoiceManualLineConfig = NewBillingInvoiceManualLineConfigClient(c.config) + c.BillingInvoiceManualUsageBasedLineConfig = NewBillingInvoiceManualUsageBasedLineConfigClient(c.config) c.BillingInvoiceValidationIssue = NewBillingInvoiceValidationIssueClient(c.config) c.BillingProfile = NewBillingProfileClient(c.config) c.BillingWorkflowConfig = NewBillingWorkflowConfigClient(c.config) @@ -225,33 +229,34 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { cfg := c.config cfg.driver = tx return &Tx{ - ctx: ctx, - config: cfg, - App: NewAppClient(cfg), - AppCustomer: NewAppCustomerClient(cfg), - AppStripe: NewAppStripeClient(cfg), - AppStripeCustomer: NewAppStripeCustomerClient(cfg), - BalanceSnapshot: NewBalanceSnapshotClient(cfg), - BillingCustomerOverride: NewBillingCustomerOverrideClient(cfg), - BillingInvoice: NewBillingInvoiceClient(cfg), - BillingInvoiceLine: NewBillingInvoiceLineClient(cfg), - BillingInvoiceManualLineConfig: NewBillingInvoiceManualLineConfigClient(cfg), - BillingInvoiceValidationIssue: NewBillingInvoiceValidationIssueClient(cfg), - BillingProfile: NewBillingProfileClient(cfg), - BillingWorkflowConfig: NewBillingWorkflowConfigClient(cfg), - Customer: NewCustomerClient(cfg), - CustomerSubjects: NewCustomerSubjectsClient(cfg), - Entitlement: NewEntitlementClient(cfg), - Feature: NewFeatureClient(cfg), - Grant: NewGrantClient(cfg), - NotificationChannel: NewNotificationChannelClient(cfg), - NotificationEvent: NewNotificationEventClient(cfg), - NotificationEventDeliveryStatus: NewNotificationEventDeliveryStatusClient(cfg), - NotificationRule: NewNotificationRuleClient(cfg), - Plan: NewPlanClient(cfg), - PlanPhase: NewPlanPhaseClient(cfg), - PlanRateCard: NewPlanRateCardClient(cfg), - UsageReset: NewUsageResetClient(cfg), + ctx: ctx, + config: cfg, + App: NewAppClient(cfg), + AppCustomer: NewAppCustomerClient(cfg), + AppStripe: NewAppStripeClient(cfg), + AppStripeCustomer: NewAppStripeCustomerClient(cfg), + BalanceSnapshot: NewBalanceSnapshotClient(cfg), + BillingCustomerOverride: NewBillingCustomerOverrideClient(cfg), + BillingInvoice: NewBillingInvoiceClient(cfg), + BillingInvoiceLine: NewBillingInvoiceLineClient(cfg), + BillingInvoiceManualLineConfig: NewBillingInvoiceManualLineConfigClient(cfg), + BillingInvoiceManualUsageBasedLineConfig: NewBillingInvoiceManualUsageBasedLineConfigClient(cfg), + BillingInvoiceValidationIssue: NewBillingInvoiceValidationIssueClient(cfg), + BillingProfile: NewBillingProfileClient(cfg), + BillingWorkflowConfig: NewBillingWorkflowConfigClient(cfg), + Customer: NewCustomerClient(cfg), + CustomerSubjects: NewCustomerSubjectsClient(cfg), + Entitlement: NewEntitlementClient(cfg), + Feature: NewFeatureClient(cfg), + Grant: NewGrantClient(cfg), + NotificationChannel: NewNotificationChannelClient(cfg), + NotificationEvent: NewNotificationEventClient(cfg), + NotificationEventDeliveryStatus: NewNotificationEventDeliveryStatusClient(cfg), + NotificationRule: NewNotificationRuleClient(cfg), + Plan: NewPlanClient(cfg), + PlanPhase: NewPlanPhaseClient(cfg), + PlanRateCard: NewPlanRateCardClient(cfg), + UsageReset: NewUsageResetClient(cfg), }, nil } @@ -269,33 +274,34 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) cfg := c.config cfg.driver = &txDriver{tx: tx, drv: c.driver} return &Tx{ - ctx: ctx, - config: cfg, - App: NewAppClient(cfg), - AppCustomer: NewAppCustomerClient(cfg), - AppStripe: NewAppStripeClient(cfg), - AppStripeCustomer: NewAppStripeCustomerClient(cfg), - BalanceSnapshot: NewBalanceSnapshotClient(cfg), - BillingCustomerOverride: NewBillingCustomerOverrideClient(cfg), - BillingInvoice: NewBillingInvoiceClient(cfg), - BillingInvoiceLine: NewBillingInvoiceLineClient(cfg), - BillingInvoiceManualLineConfig: NewBillingInvoiceManualLineConfigClient(cfg), - BillingInvoiceValidationIssue: NewBillingInvoiceValidationIssueClient(cfg), - BillingProfile: NewBillingProfileClient(cfg), - BillingWorkflowConfig: NewBillingWorkflowConfigClient(cfg), - Customer: NewCustomerClient(cfg), - CustomerSubjects: NewCustomerSubjectsClient(cfg), - Entitlement: NewEntitlementClient(cfg), - Feature: NewFeatureClient(cfg), - Grant: NewGrantClient(cfg), - NotificationChannel: NewNotificationChannelClient(cfg), - NotificationEvent: NewNotificationEventClient(cfg), - NotificationEventDeliveryStatus: NewNotificationEventDeliveryStatusClient(cfg), - NotificationRule: NewNotificationRuleClient(cfg), - Plan: NewPlanClient(cfg), - PlanPhase: NewPlanPhaseClient(cfg), - PlanRateCard: NewPlanRateCardClient(cfg), - UsageReset: NewUsageResetClient(cfg), + ctx: ctx, + config: cfg, + App: NewAppClient(cfg), + AppCustomer: NewAppCustomerClient(cfg), + AppStripe: NewAppStripeClient(cfg), + AppStripeCustomer: NewAppStripeCustomerClient(cfg), + BalanceSnapshot: NewBalanceSnapshotClient(cfg), + BillingCustomerOverride: NewBillingCustomerOverrideClient(cfg), + BillingInvoice: NewBillingInvoiceClient(cfg), + BillingInvoiceLine: NewBillingInvoiceLineClient(cfg), + BillingInvoiceManualLineConfig: NewBillingInvoiceManualLineConfigClient(cfg), + BillingInvoiceManualUsageBasedLineConfig: NewBillingInvoiceManualUsageBasedLineConfigClient(cfg), + BillingInvoiceValidationIssue: NewBillingInvoiceValidationIssueClient(cfg), + BillingProfile: NewBillingProfileClient(cfg), + BillingWorkflowConfig: NewBillingWorkflowConfigClient(cfg), + Customer: NewCustomerClient(cfg), + CustomerSubjects: NewCustomerSubjectsClient(cfg), + Entitlement: NewEntitlementClient(cfg), + Feature: NewFeatureClient(cfg), + Grant: NewGrantClient(cfg), + NotificationChannel: NewNotificationChannelClient(cfg), + NotificationEvent: NewNotificationEventClient(cfg), + NotificationEventDeliveryStatus: NewNotificationEventDeliveryStatusClient(cfg), + NotificationRule: NewNotificationRuleClient(cfg), + Plan: NewPlanClient(cfg), + PlanPhase: NewPlanPhaseClient(cfg), + PlanRateCard: NewPlanRateCardClient(cfg), + UsageReset: NewUsageResetClient(cfg), }, nil } @@ -327,11 +333,11 @@ func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ c.App, c.AppCustomer, c.AppStripe, c.AppStripeCustomer, c.BalanceSnapshot, c.BillingCustomerOverride, c.BillingInvoice, c.BillingInvoiceLine, - c.BillingInvoiceManualLineConfig, c.BillingInvoiceValidationIssue, - c.BillingProfile, c.BillingWorkflowConfig, c.Customer, c.CustomerSubjects, - c.Entitlement, c.Feature, c.Grant, c.NotificationChannel, c.NotificationEvent, - c.NotificationEventDeliveryStatus, c.NotificationRule, c.Plan, c.PlanPhase, - c.PlanRateCard, c.UsageReset, + c.BillingInvoiceManualLineConfig, c.BillingInvoiceManualUsageBasedLineConfig, + c.BillingInvoiceValidationIssue, c.BillingProfile, c.BillingWorkflowConfig, + c.Customer, c.CustomerSubjects, c.Entitlement, c.Feature, c.Grant, + c.NotificationChannel, c.NotificationEvent, c.NotificationEventDeliveryStatus, + c.NotificationRule, c.Plan, c.PlanPhase, c.PlanRateCard, c.UsageReset, } { n.Use(hooks...) } @@ -343,11 +349,11 @@ func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ c.App, c.AppCustomer, c.AppStripe, c.AppStripeCustomer, c.BalanceSnapshot, c.BillingCustomerOverride, c.BillingInvoice, c.BillingInvoiceLine, - c.BillingInvoiceManualLineConfig, c.BillingInvoiceValidationIssue, - c.BillingProfile, c.BillingWorkflowConfig, c.Customer, c.CustomerSubjects, - c.Entitlement, c.Feature, c.Grant, c.NotificationChannel, c.NotificationEvent, - c.NotificationEventDeliveryStatus, c.NotificationRule, c.Plan, c.PlanPhase, - c.PlanRateCard, c.UsageReset, + c.BillingInvoiceManualLineConfig, c.BillingInvoiceManualUsageBasedLineConfig, + c.BillingInvoiceValidationIssue, c.BillingProfile, c.BillingWorkflowConfig, + c.Customer, c.CustomerSubjects, c.Entitlement, c.Feature, c.Grant, + c.NotificationChannel, c.NotificationEvent, c.NotificationEventDeliveryStatus, + c.NotificationRule, c.Plan, c.PlanPhase, c.PlanRateCard, c.UsageReset, } { n.Intercept(interceptors...) } @@ -374,6 +380,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.BillingInvoiceLine.mutate(ctx, m) case *BillingInvoiceManualLineConfigMutation: return c.BillingInvoiceManualLineConfig.mutate(ctx, m) + case *BillingInvoiceManualUsageBasedLineConfigMutation: + return c.BillingInvoiceManualUsageBasedLineConfig.mutate(ctx, m) case *BillingInvoiceValidationIssueMutation: return c.BillingInvoiceValidationIssue.mutate(ctx, m) case *BillingProfileMutation: @@ -1850,15 +1858,63 @@ func (c *BillingInvoiceLineClient) QueryBillingInvoice(bil *BillingInvoiceLine) return query } -// QueryBillingInvoiceManualLines queries the billing_invoice_manual_lines edge of a BillingInvoiceLine. -func (c *BillingInvoiceLineClient) QueryBillingInvoiceManualLines(bil *BillingInvoiceLine) *BillingInvoiceManualLineConfigQuery { +// QueryManualFeeLine queries the manual_fee_line edge of a BillingInvoiceLine. +func (c *BillingInvoiceLineClient) QueryManualFeeLine(bil *BillingInvoiceLine) *BillingInvoiceManualLineConfigQuery { query := (&BillingInvoiceManualLineConfigClient{config: c.config}).Query() query.path = func(context.Context) (fromV *sql.Selector, _ error) { id := bil.ID step := sqlgraph.NewStep( sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, id), sqlgraph.To(billinginvoicemanuallineconfig.Table, billinginvoicemanuallineconfig.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, billinginvoiceline.BillingInvoiceManualLinesTable, billinginvoiceline.BillingInvoiceManualLinesColumn), + sqlgraph.Edge(sqlgraph.M2O, false, billinginvoiceline.ManualFeeLineTable, billinginvoiceline.ManualFeeLineColumn), + ) + fromV = sqlgraph.Neighbors(bil.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryManualUsageBasedLine queries the manual_usage_based_line edge of a BillingInvoiceLine. +func (c *BillingInvoiceLineClient) QueryManualUsageBasedLine(bil *BillingInvoiceLine) *BillingInvoiceManualUsageBasedLineConfigQuery { + query := (&BillingInvoiceManualUsageBasedLineConfigClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := bil.ID + step := sqlgraph.NewStep( + sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, id), + sqlgraph.To(billinginvoicemanualusagebasedlineconfig.Table, billinginvoicemanualusagebasedlineconfig.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, billinginvoiceline.ManualUsageBasedLineTable, billinginvoiceline.ManualUsageBasedLineColumn), + ) + fromV = sqlgraph.Neighbors(bil.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryParentLine queries the parent_line edge of a BillingInvoiceLine. +func (c *BillingInvoiceLineClient) QueryParentLine(bil *BillingInvoiceLine) *BillingInvoiceLineQuery { + query := (&BillingInvoiceLineClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := bil.ID + step := sqlgraph.NewStep( + sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, id), + sqlgraph.To(billinginvoiceline.Table, billinginvoiceline.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, billinginvoiceline.ParentLineTable, billinginvoiceline.ParentLineColumn), + ) + fromV = sqlgraph.Neighbors(bil.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryChildLines queries the child_lines edge of a BillingInvoiceLine. +func (c *BillingInvoiceLineClient) QueryChildLines(bil *BillingInvoiceLine) *BillingInvoiceLineQuery { + query := (&BillingInvoiceLineClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := bil.ID + step := sqlgraph.NewStep( + sqlgraph.From(billinginvoiceline.Table, billinginvoiceline.FieldID, id), + sqlgraph.To(billinginvoiceline.Table, billinginvoiceline.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, billinginvoiceline.ChildLinesTable, billinginvoiceline.ChildLinesColumn), ) fromV = sqlgraph.Neighbors(bil.driver.Dialect(), step) return fromV, nil @@ -2024,6 +2080,139 @@ func (c *BillingInvoiceManualLineConfigClient) mutate(ctx context.Context, m *Bi } } +// BillingInvoiceManualUsageBasedLineConfigClient is a client for the BillingInvoiceManualUsageBasedLineConfig schema. +type BillingInvoiceManualUsageBasedLineConfigClient struct { + config +} + +// NewBillingInvoiceManualUsageBasedLineConfigClient returns a client for the BillingInvoiceManualUsageBasedLineConfig from the given config. +func NewBillingInvoiceManualUsageBasedLineConfigClient(c config) *BillingInvoiceManualUsageBasedLineConfigClient { + return &BillingInvoiceManualUsageBasedLineConfigClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `billinginvoicemanualusagebasedlineconfig.Hooks(f(g(h())))`. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Use(hooks ...Hook) { + c.hooks.BillingInvoiceManualUsageBasedLineConfig = append(c.hooks.BillingInvoiceManualUsageBasedLineConfig, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `billinginvoicemanualusagebasedlineconfig.Intercept(f(g(h())))`. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Intercept(interceptors ...Interceptor) { + c.inters.BillingInvoiceManualUsageBasedLineConfig = append(c.inters.BillingInvoiceManualUsageBasedLineConfig, interceptors...) +} + +// Create returns a builder for creating a BillingInvoiceManualUsageBasedLineConfig entity. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Create() *BillingInvoiceManualUsageBasedLineConfigCreate { + mutation := newBillingInvoiceManualUsageBasedLineConfigMutation(c.config, OpCreate) + return &BillingInvoiceManualUsageBasedLineConfigCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of BillingInvoiceManualUsageBasedLineConfig entities. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) CreateBulk(builders ...*BillingInvoiceManualUsageBasedLineConfigCreate) *BillingInvoiceManualUsageBasedLineConfigCreateBulk { + return &BillingInvoiceManualUsageBasedLineConfigCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) MapCreateBulk(slice any, setFunc func(*BillingInvoiceManualUsageBasedLineConfigCreate, int)) *BillingInvoiceManualUsageBasedLineConfigCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &BillingInvoiceManualUsageBasedLineConfigCreateBulk{err: fmt.Errorf("calling to BillingInvoiceManualUsageBasedLineConfigClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*BillingInvoiceManualUsageBasedLineConfigCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &BillingInvoiceManualUsageBasedLineConfigCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for BillingInvoiceManualUsageBasedLineConfig. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Update() *BillingInvoiceManualUsageBasedLineConfigUpdate { + mutation := newBillingInvoiceManualUsageBasedLineConfigMutation(c.config, OpUpdate) + return &BillingInvoiceManualUsageBasedLineConfigUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) UpdateOne(bimublc *BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + mutation := newBillingInvoiceManualUsageBasedLineConfigMutation(c.config, OpUpdateOne, withBillingInvoiceManualUsageBasedLineConfig(bimublc)) + return &BillingInvoiceManualUsageBasedLineConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) UpdateOneID(id string) *BillingInvoiceManualUsageBasedLineConfigUpdateOne { + mutation := newBillingInvoiceManualUsageBasedLineConfigMutation(c.config, OpUpdateOne, withBillingInvoiceManualUsageBasedLineConfigID(id)) + return &BillingInvoiceManualUsageBasedLineConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for BillingInvoiceManualUsageBasedLineConfig. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Delete() *BillingInvoiceManualUsageBasedLineConfigDelete { + mutation := newBillingInvoiceManualUsageBasedLineConfigMutation(c.config, OpDelete) + return &BillingInvoiceManualUsageBasedLineConfigDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) DeleteOne(bimublc *BillingInvoiceManualUsageBasedLineConfig) *BillingInvoiceManualUsageBasedLineConfigDeleteOne { + return c.DeleteOneID(bimublc.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) DeleteOneID(id string) *BillingInvoiceManualUsageBasedLineConfigDeleteOne { + builder := c.Delete().Where(billinginvoicemanualusagebasedlineconfig.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &BillingInvoiceManualUsageBasedLineConfigDeleteOne{builder} +} + +// Query returns a query builder for BillingInvoiceManualUsageBasedLineConfig. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Query() *BillingInvoiceManualUsageBasedLineConfigQuery { + return &BillingInvoiceManualUsageBasedLineConfigQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeBillingInvoiceManualUsageBasedLineConfig}, + inters: c.Interceptors(), + } +} + +// Get returns a BillingInvoiceManualUsageBasedLineConfig entity by its id. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Get(ctx context.Context, id string) (*BillingInvoiceManualUsageBasedLineConfig, error) { + return c.Query().Where(billinginvoicemanualusagebasedlineconfig.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) GetX(ctx context.Context, id string) *BillingInvoiceManualUsageBasedLineConfig { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Hooks() []Hook { + return c.hooks.BillingInvoiceManualUsageBasedLineConfig +} + +// Interceptors returns the client interceptors. +func (c *BillingInvoiceManualUsageBasedLineConfigClient) Interceptors() []Interceptor { + return c.inters.BillingInvoiceManualUsageBasedLineConfig +} + +func (c *BillingInvoiceManualUsageBasedLineConfigClient) mutate(ctx context.Context, m *BillingInvoiceManualUsageBasedLineConfigMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&BillingInvoiceManualUsageBasedLineConfigCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&BillingInvoiceManualUsageBasedLineConfigUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&BillingInvoiceManualUsageBasedLineConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&BillingInvoiceManualUsageBasedLineConfigDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("db: unknown BillingInvoiceManualUsageBasedLineConfig mutation op: %q", m.Op()) + } +} + // BillingInvoiceValidationIssueClient is a client for the BillingInvoiceValidationIssue schema. type BillingInvoiceValidationIssueClient struct { config @@ -4685,18 +4874,20 @@ type ( hooks struct { App, AppCustomer, AppStripe, AppStripeCustomer, BalanceSnapshot, BillingCustomerOverride, BillingInvoice, BillingInvoiceLine, - BillingInvoiceManualLineConfig, BillingInvoiceValidationIssue, BillingProfile, - BillingWorkflowConfig, Customer, CustomerSubjects, Entitlement, Feature, Grant, - NotificationChannel, NotificationEvent, NotificationEventDeliveryStatus, - NotificationRule, Plan, PlanPhase, PlanRateCard, UsageReset []ent.Hook + BillingInvoiceManualLineConfig, BillingInvoiceManualUsageBasedLineConfig, + BillingInvoiceValidationIssue, BillingProfile, BillingWorkflowConfig, Customer, + CustomerSubjects, Entitlement, Feature, Grant, NotificationChannel, + NotificationEvent, NotificationEventDeliveryStatus, NotificationRule, Plan, + PlanPhase, PlanRateCard, UsageReset []ent.Hook } inters struct { App, AppCustomer, AppStripe, AppStripeCustomer, BalanceSnapshot, BillingCustomerOverride, BillingInvoice, BillingInvoiceLine, - BillingInvoiceManualLineConfig, BillingInvoiceValidationIssue, BillingProfile, - BillingWorkflowConfig, Customer, CustomerSubjects, Entitlement, Feature, Grant, - NotificationChannel, NotificationEvent, NotificationEventDeliveryStatus, - NotificationRule, Plan, PlanPhase, PlanRateCard, UsageReset []ent.Interceptor + BillingInvoiceManualLineConfig, BillingInvoiceManualUsageBasedLineConfig, + BillingInvoiceValidationIssue, BillingProfile, BillingWorkflowConfig, Customer, + CustomerSubjects, Entitlement, Feature, Grant, NotificationChannel, + NotificationEvent, NotificationEventDeliveryStatus, NotificationRule, Plan, + PlanPhase, PlanRateCard, UsageReset []ent.Interceptor } ) diff --git a/openmeter/ent/db/ent.go b/openmeter/ent/db/ent.go index 2292943d2..b1e1c9086 100644 --- a/openmeter/ent/db/ent.go +++ b/openmeter/ent/db/ent.go @@ -21,6 +21,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicevalidationissue" "github.com/openmeterio/openmeter/openmeter/ent/db/billingprofile" "github.com/openmeterio/openmeter/openmeter/ent/db/billingworkflowconfig" @@ -99,31 +100,32 @@ var ( func checkColumn(table, column string) error { initCheck.Do(func() { columnCheck = sql.NewColumnCheck(map[string]func(string) bool{ - app.Table: app.ValidColumn, - appcustomer.Table: appcustomer.ValidColumn, - appstripe.Table: appstripe.ValidColumn, - appstripecustomer.Table: appstripecustomer.ValidColumn, - balancesnapshot.Table: balancesnapshot.ValidColumn, - billingcustomeroverride.Table: billingcustomeroverride.ValidColumn, - billinginvoice.Table: billinginvoice.ValidColumn, - billinginvoiceline.Table: billinginvoiceline.ValidColumn, - billinginvoicemanuallineconfig.Table: billinginvoicemanuallineconfig.ValidColumn, - billinginvoicevalidationissue.Table: billinginvoicevalidationissue.ValidColumn, - billingprofile.Table: billingprofile.ValidColumn, - billingworkflowconfig.Table: billingworkflowconfig.ValidColumn, - customer.Table: customer.ValidColumn, - customersubjects.Table: customersubjects.ValidColumn, - entitlement.Table: entitlement.ValidColumn, - feature.Table: feature.ValidColumn, - dbgrant.Table: dbgrant.ValidColumn, - notificationchannel.Table: notificationchannel.ValidColumn, - notificationevent.Table: notificationevent.ValidColumn, - notificationeventdeliverystatus.Table: notificationeventdeliverystatus.ValidColumn, - notificationrule.Table: notificationrule.ValidColumn, - dbplan.Table: dbplan.ValidColumn, - planphase.Table: planphase.ValidColumn, - planratecard.Table: planratecard.ValidColumn, - usagereset.Table: usagereset.ValidColumn, + app.Table: app.ValidColumn, + appcustomer.Table: appcustomer.ValidColumn, + appstripe.Table: appstripe.ValidColumn, + appstripecustomer.Table: appstripecustomer.ValidColumn, + balancesnapshot.Table: balancesnapshot.ValidColumn, + billingcustomeroverride.Table: billingcustomeroverride.ValidColumn, + billinginvoice.Table: billinginvoice.ValidColumn, + billinginvoiceline.Table: billinginvoiceline.ValidColumn, + billinginvoicemanuallineconfig.Table: billinginvoicemanuallineconfig.ValidColumn, + billinginvoicemanualusagebasedlineconfig.Table: billinginvoicemanualusagebasedlineconfig.ValidColumn, + billinginvoicevalidationissue.Table: billinginvoicevalidationissue.ValidColumn, + billingprofile.Table: billingprofile.ValidColumn, + billingworkflowconfig.Table: billingworkflowconfig.ValidColumn, + customer.Table: customer.ValidColumn, + customersubjects.Table: customersubjects.ValidColumn, + entitlement.Table: entitlement.ValidColumn, + feature.Table: feature.ValidColumn, + dbgrant.Table: dbgrant.ValidColumn, + notificationchannel.Table: notificationchannel.ValidColumn, + notificationevent.Table: notificationevent.ValidColumn, + notificationeventdeliverystatus.Table: notificationeventdeliverystatus.ValidColumn, + notificationrule.Table: notificationrule.ValidColumn, + dbplan.Table: dbplan.ValidColumn, + planphase.Table: planphase.ValidColumn, + planratecard.Table: planratecard.ValidColumn, + usagereset.Table: usagereset.ValidColumn, }) }) return columnCheck(table, column) diff --git a/openmeter/ent/db/expose.go b/openmeter/ent/db/expose.go index eea6fc7c2..7424e21cf 100644 --- a/openmeter/ent/db/expose.go +++ b/openmeter/ent/db/expose.go @@ -107,6 +107,8 @@ func NewTxClientFromRawConfig(ctx context.Context, cfg entutils.RawEntConfig) *T BillingInvoiceManualLineConfig: NewBillingInvoiceManualLineConfigClient(config), + BillingInvoiceManualUsageBasedLineConfig: NewBillingInvoiceManualUsageBasedLineConfigClient(config), + BillingInvoiceValidationIssue: NewBillingInvoiceValidationIssueClient(config), BillingProfile: NewBillingProfileClient(config), diff --git a/openmeter/ent/db/hook/hook.go b/openmeter/ent/db/hook/hook.go index 5a81df8af..d6ae7bd2a 100644 --- a/openmeter/ent/db/hook/hook.go +++ b/openmeter/ent/db/hook/hook.go @@ -117,6 +117,18 @@ func (f BillingInvoiceManualLineConfigFunc) Mutate(ctx context.Context, m db.Mut return nil, fmt.Errorf("unexpected mutation type %T. expect *db.BillingInvoiceManualLineConfigMutation", m) } +// The BillingInvoiceManualUsageBasedLineConfigFunc type is an adapter to allow the use of ordinary +// function as BillingInvoiceManualUsageBasedLineConfig mutator. +type BillingInvoiceManualUsageBasedLineConfigFunc func(context.Context, *db.BillingInvoiceManualUsageBasedLineConfigMutation) (db.Value, error) + +// Mutate calls f(ctx, m). +func (f BillingInvoiceManualUsageBasedLineConfigFunc) Mutate(ctx context.Context, m db.Mutation) (db.Value, error) { + if mv, ok := m.(*db.BillingInvoiceManualUsageBasedLineConfigMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *db.BillingInvoiceManualUsageBasedLineConfigMutation", m) +} + // The BillingInvoiceValidationIssueFunc type is an adapter to allow the use of ordinary // function as BillingInvoiceValidationIssue mutator. type BillingInvoiceValidationIssueFunc func(context.Context, *db.BillingInvoiceValidationIssueMutation) (db.Value, error) diff --git a/openmeter/ent/db/migrate/schema.go b/openmeter/ent/db/migrate/schema.go index a4453419f..dc9e66442 100644 --- a/openmeter/ent/db/migrate/schema.go +++ b/openmeter/ent/db/migrate/schema.go @@ -322,6 +322,7 @@ var ( {Name: "supplier_tax_code", Type: field.TypeString, Nullable: true}, {Name: "customer_name", Type: field.TypeString}, {Name: "customer_timezone", Type: field.TypeString, Nullable: true}, + {Name: "customer_subject_keys", Type: field.TypeJSON, Nullable: true}, {Name: "number", Type: field.TypeString, Nullable: true}, {Name: "type", Type: field.TypeEnum, Enums: []string{"standard", "credit-note"}}, {Name: "description", Type: field.TypeString, Nullable: true}, @@ -348,37 +349,37 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "billing_invoices_apps_billing_invoice_tax_app", - Columns: []*schema.Column{BillingInvoicesColumns[35]}, + Columns: []*schema.Column{BillingInvoicesColumns[36]}, RefColumns: []*schema.Column{AppsColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "billing_invoices_apps_billing_invoice_invoicing_app", - Columns: []*schema.Column{BillingInvoicesColumns[36]}, + Columns: []*schema.Column{BillingInvoicesColumns[37]}, RefColumns: []*schema.Column{AppsColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "billing_invoices_apps_billing_invoice_payment_app", - Columns: []*schema.Column{BillingInvoicesColumns[37]}, + Columns: []*schema.Column{BillingInvoicesColumns[38]}, RefColumns: []*schema.Column{AppsColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "billing_invoices_billing_profiles_billing_invoices", - Columns: []*schema.Column{BillingInvoicesColumns[38]}, + Columns: []*schema.Column{BillingInvoicesColumns[39]}, RefColumns: []*schema.Column{BillingProfilesColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "billing_invoices_billing_workflow_configs_billing_invoices", - Columns: []*schema.Column{BillingInvoicesColumns[39]}, + Columns: []*schema.Column{BillingInvoicesColumns[40]}, RefColumns: []*schema.Column{BillingWorkflowConfigsColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "billing_invoices_customers_billing_invoice", - Columns: []*schema.Column{BillingInvoicesColumns[40]}, + Columns: []*schema.Column{BillingInvoicesColumns[41]}, RefColumns: []*schema.Column{CustomersColumns[0]}, OnDelete: schema.NoAction, }, @@ -402,7 +403,7 @@ var ( { Name: "billinginvoice_namespace_customer_id", Unique: false, - Columns: []*schema.Column{BillingInvoicesColumns[1], BillingInvoicesColumns[40]}, + Columns: []*schema.Column{BillingInvoicesColumns[1], BillingInvoicesColumns[41]}, }, }, } @@ -419,13 +420,15 @@ var ( {Name: "period_start", Type: field.TypeTime}, {Name: "period_end", Type: field.TypeTime}, {Name: "invoice_at", Type: field.TypeTime}, - {Name: "type", Type: field.TypeEnum, Enums: []string{"manual_fee", "flat_fee", "usage_based"}}, + {Name: "type", Type: field.TypeEnum, Enums: []string{"manual_fee", "manual_usage_based", "flat_fee", "usage_based"}}, {Name: "status", Type: field.TypeEnum, Enums: []string{"valid", "split"}}, {Name: "currency", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(3)"}}, {Name: "quantity", Type: field.TypeOther, Nullable: true, SchemaType: map[string]string{"postgres": "numeric"}}, {Name: "tax_overrides", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "invoice_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "manual_line_config_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, + {Name: "manual_usage_based_line_config_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, + {Name: "parent_line_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, } // BillingInvoiceLinesTable holds the schema information for the "billing_invoice_lines" table. BillingInvoiceLinesTable = &schema.Table{ @@ -437,12 +440,24 @@ var ( Symbol: "billing_invoice_lines_billing_invoices_billing_invoice_lines", Columns: []*schema.Column{BillingInvoiceLinesColumns[16]}, RefColumns: []*schema.Column{BillingInvoicesColumns[0]}, - OnDelete: schema.NoAction, + OnDelete: schema.Cascade, }, { - Symbol: "billing_invoice_lines_billing_invoice_manual_line_configs_billing_invoice_manual_lines", + Symbol: "billing_invoice_lines_billing_invoice_manual_line_configs_manual_fee_line", Columns: []*schema.Column{BillingInvoiceLinesColumns[17]}, RefColumns: []*schema.Column{BillingInvoiceManualLineConfigsColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "billing_invoice_lines_billing_invoice_manual_usage_based_line_configs_manual_usage_based_line", + Columns: []*schema.Column{BillingInvoiceLinesColumns[18]}, + RefColumns: []*schema.Column{BillingInvoiceManualUsageBasedLineConfigsColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "billing_invoice_lines_billing_invoice_lines_child_lines", + Columns: []*schema.Column{BillingInvoiceLinesColumns[19]}, + RefColumns: []*schema.Column{BillingInvoiceLinesColumns[0]}, OnDelete: schema.SetNull, }, }, @@ -467,6 +482,11 @@ var ( Unique: false, Columns: []*schema.Column{BillingInvoiceLinesColumns[1], BillingInvoiceLinesColumns[16]}, }, + { + Name: "billinginvoiceline_namespace_parent_line_id", + Unique: false, + Columns: []*schema.Column{BillingInvoiceLinesColumns[1], BillingInvoiceLinesColumns[19]}, + }, }, } // BillingInvoiceManualLineConfigsColumns holds the columns for the "billing_invoice_manual_line_configs" table. @@ -493,6 +513,32 @@ var ( }, }, } + // BillingInvoiceManualUsageBasedLineConfigsColumns holds the columns for the "billing_invoice_manual_usage_based_line_configs" table. + BillingInvoiceManualUsageBasedLineConfigsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, Unique: true, SchemaType: map[string]string{"postgres": "char(26)"}}, + {Name: "namespace", Type: field.TypeString}, + {Name: "price_type", Type: field.TypeEnum, Enums: []string{"flat", "unit", "tiered"}}, + {Name: "feature_key", Type: field.TypeString}, + {Name: "price", Type: field.TypeString, SchemaType: map[string]string{"postgres": "jsonb"}}, + } + // BillingInvoiceManualUsageBasedLineConfigsTable holds the schema information for the "billing_invoice_manual_usage_based_line_configs" table. + BillingInvoiceManualUsageBasedLineConfigsTable = &schema.Table{ + Name: "billing_invoice_manual_usage_based_line_configs", + Columns: BillingInvoiceManualUsageBasedLineConfigsColumns, + PrimaryKey: []*schema.Column{BillingInvoiceManualUsageBasedLineConfigsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "billinginvoicemanualusagebasedlineconfig_namespace", + Unique: false, + Columns: []*schema.Column{BillingInvoiceManualUsageBasedLineConfigsColumns[1]}, + }, + { + Name: "billinginvoicemanualusagebasedlineconfig_id", + Unique: true, + Columns: []*schema.Column{BillingInvoiceManualUsageBasedLineConfigsColumns[0]}, + }, + }, + } // BillingInvoiceValidationIssuesColumns holds the columns for the "billing_invoice_validation_issues" table. BillingInvoiceValidationIssuesColumns = []*schema.Column{ {Name: "id", Type: field.TypeString, Unique: true, SchemaType: map[string]string{"postgres": "char(26)"}}, @@ -518,7 +564,7 @@ var ( Symbol: "billing_invoice_validation_issues_billing_invoices_billing_invoice_validation_issues", Columns: []*schema.Column{BillingInvoiceValidationIssuesColumns[11]}, RefColumns: []*schema.Column{BillingInvoicesColumns[0]}, - OnDelete: schema.NoAction, + OnDelete: schema.Cascade, }, }, Indexes: []*schema.Index{ @@ -1396,6 +1442,7 @@ var ( BillingInvoicesTable, BillingInvoiceLinesTable, BillingInvoiceManualLineConfigsTable, + BillingInvoiceManualUsageBasedLineConfigsTable, BillingInvoiceValidationIssuesTable, BillingProfilesTable, BillingWorkflowConfigsTable, @@ -1434,6 +1481,8 @@ func init() { BillingInvoicesTable.ForeignKeys[5].RefTable = CustomersTable BillingInvoiceLinesTable.ForeignKeys[0].RefTable = BillingInvoicesTable BillingInvoiceLinesTable.ForeignKeys[1].RefTable = BillingInvoiceManualLineConfigsTable + BillingInvoiceLinesTable.ForeignKeys[2].RefTable = BillingInvoiceManualUsageBasedLineConfigsTable + BillingInvoiceLinesTable.ForeignKeys[3].RefTable = BillingInvoiceLinesTable BillingInvoiceValidationIssuesTable.ForeignKeys[0].RefTable = BillingInvoicesTable BillingProfilesTable.ForeignKeys[0].RefTable = AppsTable BillingProfilesTable.ForeignKeys[1].RefTable = AppsTable diff --git a/openmeter/ent/db/mutation.go b/openmeter/ent/db/mutation.go index ed312ff3e..5ba24af89 100644 --- a/openmeter/ent/db/mutation.go +++ b/openmeter/ent/db/mutation.go @@ -25,6 +25,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicevalidationissue" "github.com/openmeterio/openmeter/openmeter/ent/db/billingprofile" "github.com/openmeterio/openmeter/openmeter/ent/db/billingworkflowconfig" @@ -60,31 +61,32 @@ const ( OpUpdateOne = ent.OpUpdateOne // Node types. - TypeApp = "App" - TypeAppCustomer = "AppCustomer" - TypeAppStripe = "AppStripe" - TypeAppStripeCustomer = "AppStripeCustomer" - TypeBalanceSnapshot = "BalanceSnapshot" - TypeBillingCustomerOverride = "BillingCustomerOverride" - TypeBillingInvoice = "BillingInvoice" - TypeBillingInvoiceLine = "BillingInvoiceLine" - TypeBillingInvoiceManualLineConfig = "BillingInvoiceManualLineConfig" - TypeBillingInvoiceValidationIssue = "BillingInvoiceValidationIssue" - TypeBillingProfile = "BillingProfile" - TypeBillingWorkflowConfig = "BillingWorkflowConfig" - TypeCustomer = "Customer" - TypeCustomerSubjects = "CustomerSubjects" - TypeEntitlement = "Entitlement" - TypeFeature = "Feature" - TypeGrant = "Grant" - TypeNotificationChannel = "NotificationChannel" - TypeNotificationEvent = "NotificationEvent" - TypeNotificationEventDeliveryStatus = "NotificationEventDeliveryStatus" - TypeNotificationRule = "NotificationRule" - TypePlan = "Plan" - TypePlanPhase = "PlanPhase" - TypePlanRateCard = "PlanRateCard" - TypeUsageReset = "UsageReset" + TypeApp = "App" + TypeAppCustomer = "AppCustomer" + TypeAppStripe = "AppStripe" + TypeAppStripeCustomer = "AppStripeCustomer" + TypeBalanceSnapshot = "BalanceSnapshot" + TypeBillingCustomerOverride = "BillingCustomerOverride" + TypeBillingInvoice = "BillingInvoice" + TypeBillingInvoiceLine = "BillingInvoiceLine" + TypeBillingInvoiceManualLineConfig = "BillingInvoiceManualLineConfig" + TypeBillingInvoiceManualUsageBasedLineConfig = "BillingInvoiceManualUsageBasedLineConfig" + TypeBillingInvoiceValidationIssue = "BillingInvoiceValidationIssue" + TypeBillingProfile = "BillingProfile" + TypeBillingWorkflowConfig = "BillingWorkflowConfig" + TypeCustomer = "Customer" + TypeCustomerSubjects = "CustomerSubjects" + TypeEntitlement = "Entitlement" + TypeFeature = "Feature" + TypeGrant = "Grant" + TypeNotificationChannel = "NotificationChannel" + TypeNotificationEvent = "NotificationEvent" + TypeNotificationEventDeliveryStatus = "NotificationEventDeliveryStatus" + TypeNotificationRule = "NotificationRule" + TypePlan = "Plan" + TypePlanPhase = "PlanPhase" + TypePlanRateCard = "PlanRateCard" + TypeUsageReset = "UsageReset" ) // AppMutation represents an operation that mutates the App nodes in the graph. @@ -6196,6 +6198,8 @@ type BillingInvoiceMutation struct { supplier_tax_code *string customer_name *string customer_timezone *timezone.Timezone + customer_subject_keys *[]string + appendcustomer_subject_keys []string number *string _type *billingentity.InvoiceType description *string @@ -7397,6 +7401,71 @@ func (m *BillingInvoiceMutation) ResetCustomerTimezone() { delete(m.clearedFields, billinginvoice.FieldCustomerTimezone) } +// SetCustomerSubjectKeys sets the "customer_subject_keys" field. +func (m *BillingInvoiceMutation) SetCustomerSubjectKeys(s []string) { + m.customer_subject_keys = &s + m.appendcustomer_subject_keys = nil +} + +// CustomerSubjectKeys returns the value of the "customer_subject_keys" field in the mutation. +func (m *BillingInvoiceMutation) CustomerSubjectKeys() (r []string, exists bool) { + v := m.customer_subject_keys + if v == nil { + return + } + return *v, true +} + +// OldCustomerSubjectKeys returns the old "customer_subject_keys" field's value of the BillingInvoice entity. +// If the BillingInvoice object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BillingInvoiceMutation) OldCustomerSubjectKeys(ctx context.Context) (v []string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCustomerSubjectKeys is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCustomerSubjectKeys requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCustomerSubjectKeys: %w", err) + } + return oldValue.CustomerSubjectKeys, nil +} + +// AppendCustomerSubjectKeys adds s to the "customer_subject_keys" field. +func (m *BillingInvoiceMutation) AppendCustomerSubjectKeys(s []string) { + m.appendcustomer_subject_keys = append(m.appendcustomer_subject_keys, s...) +} + +// AppendedCustomerSubjectKeys returns the list of values that were appended to the "customer_subject_keys" field in this mutation. +func (m *BillingInvoiceMutation) AppendedCustomerSubjectKeys() ([]string, bool) { + if len(m.appendcustomer_subject_keys) == 0 { + return nil, false + } + return m.appendcustomer_subject_keys, true +} + +// ClearCustomerSubjectKeys clears the value of the "customer_subject_keys" field. +func (m *BillingInvoiceMutation) ClearCustomerSubjectKeys() { + m.customer_subject_keys = nil + m.appendcustomer_subject_keys = nil + m.clearedFields[billinginvoice.FieldCustomerSubjectKeys] = struct{}{} +} + +// CustomerSubjectKeysCleared returns if the "customer_subject_keys" field was cleared in this mutation. +func (m *BillingInvoiceMutation) CustomerSubjectKeysCleared() bool { + _, ok := m.clearedFields[billinginvoice.FieldCustomerSubjectKeys] + return ok +} + +// ResetCustomerSubjectKeys resets all changes to the "customer_subject_keys" field. +func (m *BillingInvoiceMutation) ResetCustomerSubjectKeys() { + m.customer_subject_keys = nil + m.appendcustomer_subject_keys = nil + delete(m.clearedFields, billinginvoice.FieldCustomerSubjectKeys) +} + // SetNumber sets the "number" field. func (m *BillingInvoiceMutation) SetNumber(s string) { m.number = &s @@ -8443,7 +8512,7 @@ func (m *BillingInvoiceMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *BillingInvoiceMutation) Fields() []string { - fields := make([]string, 0, 40) + fields := make([]string, 0, 41) if m.namespace != nil { fields = append(fields, billinginvoice.FieldNamespace) } @@ -8513,6 +8582,9 @@ func (m *BillingInvoiceMutation) Fields() []string { if m.customer_timezone != nil { fields = append(fields, billinginvoice.FieldCustomerTimezone) } + if m.customer_subject_keys != nil { + fields = append(fields, billinginvoice.FieldCustomerSubjectKeys) + } if m.number != nil { fields = append(fields, billinginvoice.FieldNumber) } @@ -8618,6 +8690,8 @@ func (m *BillingInvoiceMutation) Field(name string) (ent.Value, bool) { return m.CustomerName() case billinginvoice.FieldCustomerTimezone: return m.CustomerTimezone() + case billinginvoice.FieldCustomerSubjectKeys: + return m.CustomerSubjectKeys() case billinginvoice.FieldNumber: return m.Number() case billinginvoice.FieldType: @@ -8707,6 +8781,8 @@ func (m *BillingInvoiceMutation) OldField(ctx context.Context, name string) (ent return m.OldCustomerName(ctx) case billinginvoice.FieldCustomerTimezone: return m.OldCustomerTimezone(ctx) + case billinginvoice.FieldCustomerSubjectKeys: + return m.OldCustomerSubjectKeys(ctx) case billinginvoice.FieldNumber: return m.OldNumber(ctx) case billinginvoice.FieldType: @@ -8911,6 +8987,13 @@ func (m *BillingInvoiceMutation) SetField(name string, value ent.Value) error { } m.SetCustomerTimezone(v) return nil + case billinginvoice.FieldCustomerSubjectKeys: + v, ok := value.([]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCustomerSubjectKeys(v) + return nil case billinginvoice.FieldNumber: v, ok := value.(string) if !ok { @@ -9114,6 +9197,9 @@ func (m *BillingInvoiceMutation) ClearedFields() []string { if m.FieldCleared(billinginvoice.FieldCustomerTimezone) { fields = append(fields, billinginvoice.FieldCustomerTimezone) } + if m.FieldCleared(billinginvoice.FieldCustomerSubjectKeys) { + fields = append(fields, billinginvoice.FieldCustomerSubjectKeys) + } if m.FieldCleared(billinginvoice.FieldNumber) { fields = append(fields, billinginvoice.FieldNumber) } @@ -9206,6 +9292,9 @@ func (m *BillingInvoiceMutation) ClearField(name string) error { case billinginvoice.FieldCustomerTimezone: m.ClearCustomerTimezone() return nil + case billinginvoice.FieldCustomerSubjectKeys: + m.ClearCustomerSubjectKeys() + return nil case billinginvoice.FieldNumber: m.ClearNumber() return nil @@ -9307,6 +9396,9 @@ func (m *BillingInvoiceMutation) ResetField(name string) error { case billinginvoice.FieldCustomerTimezone: m.ResetCustomerTimezone() return nil + case billinginvoice.FieldCustomerSubjectKeys: + m.ResetCustomerSubjectKeys() + return nil case billinginvoice.FieldNumber: m.ResetNumber() return nil @@ -9583,32 +9675,39 @@ func (m *BillingInvoiceMutation) ResetEdge(name string) error { // BillingInvoiceLineMutation represents an operation that mutates the BillingInvoiceLine nodes in the graph. type BillingInvoiceLineMutation struct { config - op Op - typ string - id *string - namespace *string - metadata *map[string]string - created_at *time.Time - updated_at *time.Time - deleted_at *time.Time - name *string - description *string - period_start *time.Time - period_end *time.Time - invoice_at *time.Time - _type *billingentity.InvoiceLineType - status *billingentity.InvoiceLineStatus - currency *currencyx.Code - quantity *alpacadecimal.Decimal - tax_overrides **billingentity.TaxOverrides - clearedFields map[string]struct{} - billing_invoice *string - clearedbilling_invoice bool - billing_invoice_manual_lines *string - clearedbilling_invoice_manual_lines bool - done bool - oldValue func(context.Context) (*BillingInvoiceLine, error) - predicates []predicate.BillingInvoiceLine + op Op + typ string + id *string + namespace *string + metadata *map[string]string + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + name *string + description *string + period_start *time.Time + period_end *time.Time + invoice_at *time.Time + _type *billingentity.InvoiceLineType + status *billingentity.InvoiceLineStatus + currency *currencyx.Code + quantity *alpacadecimal.Decimal + tax_overrides **billingentity.TaxOverrides + clearedFields map[string]struct{} + billing_invoice *string + clearedbilling_invoice bool + manual_fee_line *string + clearedmanual_fee_line bool + manual_usage_based_line *string + clearedmanual_usage_based_line bool + parent_line *string + clearedparent_line bool + child_lines map[string]struct{} + removedchild_lines map[string]struct{} + clearedchild_lines bool + done bool + oldValue func(context.Context) (*BillingInvoiceLine, error) + predicates []predicate.BillingInvoiceLine } var _ ent.Mutation = (*BillingInvoiceLineMutation)(nil) @@ -10042,6 +10141,55 @@ func (m *BillingInvoiceLineMutation) ResetInvoiceID() { m.billing_invoice = nil } +// SetParentLineID sets the "parent_line_id" field. +func (m *BillingInvoiceLineMutation) SetParentLineID(s string) { + m.parent_line = &s +} + +// ParentLineID returns the value of the "parent_line_id" field in the mutation. +func (m *BillingInvoiceLineMutation) ParentLineID() (r string, exists bool) { + v := m.parent_line + if v == nil { + return + } + return *v, true +} + +// OldParentLineID returns the old "parent_line_id" field's value of the BillingInvoiceLine entity. +// If the BillingInvoiceLine object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BillingInvoiceLineMutation) OldParentLineID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldParentLineID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldParentLineID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldParentLineID: %w", err) + } + return oldValue.ParentLineID, nil +} + +// ClearParentLineID clears the value of the "parent_line_id" field. +func (m *BillingInvoiceLineMutation) ClearParentLineID() { + m.parent_line = nil + m.clearedFields[billinginvoiceline.FieldParentLineID] = struct{}{} +} + +// ParentLineIDCleared returns if the "parent_line_id" field was cleared in this mutation. +func (m *BillingInvoiceLineMutation) ParentLineIDCleared() bool { + _, ok := m.clearedFields[billinginvoiceline.FieldParentLineID] + return ok +} + +// ResetParentLineID resets all changes to the "parent_line_id" field. +func (m *BillingInvoiceLineMutation) ResetParentLineID() { + m.parent_line = nil + delete(m.clearedFields, billinginvoiceline.FieldParentLineID) +} + // SetPeriodStart sets the "period_start" field. func (m *BillingInvoiceLineMutation) SetPeriodStart(t time.Time) { m.period_start = &t @@ -10396,43 +10544,163 @@ func (m *BillingInvoiceLineMutation) ResetBillingInvoice() { m.clearedbilling_invoice = false } -// SetBillingInvoiceManualLinesID sets the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity by id. -func (m *BillingInvoiceLineMutation) SetBillingInvoiceManualLinesID(id string) { - m.billing_invoice_manual_lines = &id +// SetManualFeeLineID sets the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity by id. +func (m *BillingInvoiceLineMutation) SetManualFeeLineID(id string) { + m.manual_fee_line = &id +} + +// ClearManualFeeLine clears the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity. +func (m *BillingInvoiceLineMutation) ClearManualFeeLine() { + m.clearedmanual_fee_line = true +} + +// ManualFeeLineCleared reports if the "manual_fee_line" edge to the BillingInvoiceManualLineConfig entity was cleared. +func (m *BillingInvoiceLineMutation) ManualFeeLineCleared() bool { + return m.clearedmanual_fee_line +} + +// ManualFeeLineID returns the "manual_fee_line" edge ID in the mutation. +func (m *BillingInvoiceLineMutation) ManualFeeLineID() (id string, exists bool) { + if m.manual_fee_line != nil { + return *m.manual_fee_line, true + } + return +} + +// ManualFeeLineIDs returns the "manual_fee_line" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ManualFeeLineID instead. It exists only for internal usage by the builders. +func (m *BillingInvoiceLineMutation) ManualFeeLineIDs() (ids []string) { + if id := m.manual_fee_line; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetManualFeeLine resets all changes to the "manual_fee_line" edge. +func (m *BillingInvoiceLineMutation) ResetManualFeeLine() { + m.manual_fee_line = nil + m.clearedmanual_fee_line = false +} + +// SetManualUsageBasedLineID sets the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity by id. +func (m *BillingInvoiceLineMutation) SetManualUsageBasedLineID(id string) { + m.manual_usage_based_line = &id } -// ClearBillingInvoiceManualLines clears the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity. -func (m *BillingInvoiceLineMutation) ClearBillingInvoiceManualLines() { - m.clearedbilling_invoice_manual_lines = true +// ClearManualUsageBasedLine clears the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity. +func (m *BillingInvoiceLineMutation) ClearManualUsageBasedLine() { + m.clearedmanual_usage_based_line = true } -// BillingInvoiceManualLinesCleared reports if the "billing_invoice_manual_lines" edge to the BillingInvoiceManualLineConfig entity was cleared. -func (m *BillingInvoiceLineMutation) BillingInvoiceManualLinesCleared() bool { - return m.clearedbilling_invoice_manual_lines +// ManualUsageBasedLineCleared reports if the "manual_usage_based_line" edge to the BillingInvoiceManualUsageBasedLineConfig entity was cleared. +func (m *BillingInvoiceLineMutation) ManualUsageBasedLineCleared() bool { + return m.clearedmanual_usage_based_line +} + +// ManualUsageBasedLineID returns the "manual_usage_based_line" edge ID in the mutation. +func (m *BillingInvoiceLineMutation) ManualUsageBasedLineID() (id string, exists bool) { + if m.manual_usage_based_line != nil { + return *m.manual_usage_based_line, true + } + return } -// BillingInvoiceManualLinesID returns the "billing_invoice_manual_lines" edge ID in the mutation. -func (m *BillingInvoiceLineMutation) BillingInvoiceManualLinesID() (id string, exists bool) { - if m.billing_invoice_manual_lines != nil { - return *m.billing_invoice_manual_lines, true +// ManualUsageBasedLineIDs returns the "manual_usage_based_line" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ManualUsageBasedLineID instead. It exists only for internal usage by the builders. +func (m *BillingInvoiceLineMutation) ManualUsageBasedLineIDs() (ids []string) { + if id := m.manual_usage_based_line; id != nil { + ids = append(ids, *id) } return } -// BillingInvoiceManualLinesIDs returns the "billing_invoice_manual_lines" edge IDs in the mutation. +// ResetManualUsageBasedLine resets all changes to the "manual_usage_based_line" edge. +func (m *BillingInvoiceLineMutation) ResetManualUsageBasedLine() { + m.manual_usage_based_line = nil + m.clearedmanual_usage_based_line = false +} + +// ClearParentLine clears the "parent_line" edge to the BillingInvoiceLine entity. +func (m *BillingInvoiceLineMutation) ClearParentLine() { + m.clearedparent_line = true + m.clearedFields[billinginvoiceline.FieldParentLineID] = struct{}{} +} + +// ParentLineCleared reports if the "parent_line" edge to the BillingInvoiceLine entity was cleared. +func (m *BillingInvoiceLineMutation) ParentLineCleared() bool { + return m.ParentLineIDCleared() || m.clearedparent_line +} + +// ParentLineIDs returns the "parent_line" edge IDs in the mutation. // Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// BillingInvoiceManualLinesID instead. It exists only for internal usage by the builders. -func (m *BillingInvoiceLineMutation) BillingInvoiceManualLinesIDs() (ids []string) { - if id := m.billing_invoice_manual_lines; id != nil { +// ParentLineID instead. It exists only for internal usage by the builders. +func (m *BillingInvoiceLineMutation) ParentLineIDs() (ids []string) { + if id := m.parent_line; id != nil { ids = append(ids, *id) } return } -// ResetBillingInvoiceManualLines resets all changes to the "billing_invoice_manual_lines" edge. -func (m *BillingInvoiceLineMutation) ResetBillingInvoiceManualLines() { - m.billing_invoice_manual_lines = nil - m.clearedbilling_invoice_manual_lines = false +// ResetParentLine resets all changes to the "parent_line" edge. +func (m *BillingInvoiceLineMutation) ResetParentLine() { + m.parent_line = nil + m.clearedparent_line = false +} + +// AddChildLineIDs adds the "child_lines" edge to the BillingInvoiceLine entity by ids. +func (m *BillingInvoiceLineMutation) AddChildLineIDs(ids ...string) { + if m.child_lines == nil { + m.child_lines = make(map[string]struct{}) + } + for i := range ids { + m.child_lines[ids[i]] = struct{}{} + } +} + +// ClearChildLines clears the "child_lines" edge to the BillingInvoiceLine entity. +func (m *BillingInvoiceLineMutation) ClearChildLines() { + m.clearedchild_lines = true +} + +// ChildLinesCleared reports if the "child_lines" edge to the BillingInvoiceLine entity was cleared. +func (m *BillingInvoiceLineMutation) ChildLinesCleared() bool { + return m.clearedchild_lines +} + +// RemoveChildLineIDs removes the "child_lines" edge to the BillingInvoiceLine entity by IDs. +func (m *BillingInvoiceLineMutation) RemoveChildLineIDs(ids ...string) { + if m.removedchild_lines == nil { + m.removedchild_lines = make(map[string]struct{}) + } + for i := range ids { + delete(m.child_lines, ids[i]) + m.removedchild_lines[ids[i]] = struct{}{} + } +} + +// RemovedChildLines returns the removed IDs of the "child_lines" edge to the BillingInvoiceLine entity. +func (m *BillingInvoiceLineMutation) RemovedChildLinesIDs() (ids []string) { + for id := range m.removedchild_lines { + ids = append(ids, id) + } + return +} + +// ChildLinesIDs returns the "child_lines" edge IDs in the mutation. +func (m *BillingInvoiceLineMutation) ChildLinesIDs() (ids []string) { + for id := range m.child_lines { + ids = append(ids, id) + } + return +} + +// ResetChildLines resets all changes to the "child_lines" edge. +func (m *BillingInvoiceLineMutation) ResetChildLines() { + m.child_lines = nil + m.clearedchild_lines = false + m.removedchild_lines = nil } // Where appends a list predicates to the BillingInvoiceLineMutation builder. @@ -10469,7 +10737,7 @@ func (m *BillingInvoiceLineMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *BillingInvoiceLineMutation) Fields() []string { - fields := make([]string, 0, 16) + fields := make([]string, 0, 17) if m.namespace != nil { fields = append(fields, billinginvoiceline.FieldNamespace) } @@ -10494,6 +10762,9 @@ func (m *BillingInvoiceLineMutation) Fields() []string { if m.billing_invoice != nil { fields = append(fields, billinginvoiceline.FieldInvoiceID) } + if m.parent_line != nil { + fields = append(fields, billinginvoiceline.FieldParentLineID) + } if m.period_start != nil { fields = append(fields, billinginvoiceline.FieldPeriodStart) } @@ -10542,6 +10813,8 @@ func (m *BillingInvoiceLineMutation) Field(name string) (ent.Value, bool) { return m.Description() case billinginvoiceline.FieldInvoiceID: return m.InvoiceID() + case billinginvoiceline.FieldParentLineID: + return m.ParentLineID() case billinginvoiceline.FieldPeriodStart: return m.PeriodStart() case billinginvoiceline.FieldPeriodEnd: @@ -10583,6 +10856,8 @@ func (m *BillingInvoiceLineMutation) OldField(ctx context.Context, name string) return m.OldDescription(ctx) case billinginvoiceline.FieldInvoiceID: return m.OldInvoiceID(ctx) + case billinginvoiceline.FieldParentLineID: + return m.OldParentLineID(ctx) case billinginvoiceline.FieldPeriodStart: return m.OldPeriodStart(ctx) case billinginvoiceline.FieldPeriodEnd: @@ -10664,6 +10939,13 @@ func (m *BillingInvoiceLineMutation) SetField(name string, value ent.Value) erro } m.SetInvoiceID(v) return nil + case billinginvoiceline.FieldParentLineID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetParentLineID(v) + return nil case billinginvoiceline.FieldPeriodStart: v, ok := value.(time.Time) if !ok { @@ -10759,6 +11041,9 @@ func (m *BillingInvoiceLineMutation) ClearedFields() []string { if m.FieldCleared(billinginvoiceline.FieldDescription) { fields = append(fields, billinginvoiceline.FieldDescription) } + if m.FieldCleared(billinginvoiceline.FieldParentLineID) { + fields = append(fields, billinginvoiceline.FieldParentLineID) + } if m.FieldCleared(billinginvoiceline.FieldQuantity) { fields = append(fields, billinginvoiceline.FieldQuantity) } @@ -10788,6 +11073,9 @@ func (m *BillingInvoiceLineMutation) ClearField(name string) error { case billinginvoiceline.FieldDescription: m.ClearDescription() return nil + case billinginvoiceline.FieldParentLineID: + m.ClearParentLineID() + return nil case billinginvoiceline.FieldQuantity: m.ClearQuantity() return nil @@ -10826,6 +11114,9 @@ func (m *BillingInvoiceLineMutation) ResetField(name string) error { case billinginvoiceline.FieldInvoiceID: m.ResetInvoiceID() return nil + case billinginvoiceline.FieldParentLineID: + m.ResetParentLineID() + return nil case billinginvoiceline.FieldPeriodStart: m.ResetPeriodStart() return nil @@ -10856,12 +11147,21 @@ func (m *BillingInvoiceLineMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *BillingInvoiceLineMutation) AddedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 5) if m.billing_invoice != nil { edges = append(edges, billinginvoiceline.EdgeBillingInvoice) } - if m.billing_invoice_manual_lines != nil { - edges = append(edges, billinginvoiceline.EdgeBillingInvoiceManualLines) + if m.manual_fee_line != nil { + edges = append(edges, billinginvoiceline.EdgeManualFeeLine) + } + if m.manual_usage_based_line != nil { + edges = append(edges, billinginvoiceline.EdgeManualUsageBasedLine) + } + if m.parent_line != nil { + edges = append(edges, billinginvoiceline.EdgeParentLine) + } + if m.child_lines != nil { + edges = append(edges, billinginvoiceline.EdgeChildLines) } return edges } @@ -10874,34 +11174,68 @@ func (m *BillingInvoiceLineMutation) AddedIDs(name string) []ent.Value { if id := m.billing_invoice; id != nil { return []ent.Value{*id} } - case billinginvoiceline.EdgeBillingInvoiceManualLines: - if id := m.billing_invoice_manual_lines; id != nil { + case billinginvoiceline.EdgeManualFeeLine: + if id := m.manual_fee_line; id != nil { + return []ent.Value{*id} + } + case billinginvoiceline.EdgeManualUsageBasedLine: + if id := m.manual_usage_based_line; id != nil { + return []ent.Value{*id} + } + case billinginvoiceline.EdgeParentLine: + if id := m.parent_line; id != nil { return []ent.Value{*id} } + case billinginvoiceline.EdgeChildLines: + ids := make([]ent.Value, 0, len(m.child_lines)) + for id := range m.child_lines { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *BillingInvoiceLineMutation) RemovedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 5) + if m.removedchild_lines != nil { + edges = append(edges, billinginvoiceline.EdgeChildLines) + } return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. func (m *BillingInvoiceLineMutation) RemovedIDs(name string) []ent.Value { + switch name { + case billinginvoiceline.EdgeChildLines: + ids := make([]ent.Value, 0, len(m.removedchild_lines)) + for id := range m.removedchild_lines { + ids = append(ids, id) + } + return ids + } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *BillingInvoiceLineMutation) ClearedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 5) if m.clearedbilling_invoice { edges = append(edges, billinginvoiceline.EdgeBillingInvoice) } - if m.clearedbilling_invoice_manual_lines { - edges = append(edges, billinginvoiceline.EdgeBillingInvoiceManualLines) + if m.clearedmanual_fee_line { + edges = append(edges, billinginvoiceline.EdgeManualFeeLine) + } + if m.clearedmanual_usage_based_line { + edges = append(edges, billinginvoiceline.EdgeManualUsageBasedLine) + } + if m.clearedparent_line { + edges = append(edges, billinginvoiceline.EdgeParentLine) + } + if m.clearedchild_lines { + edges = append(edges, billinginvoiceline.EdgeChildLines) } return edges } @@ -10912,8 +11246,14 @@ func (m *BillingInvoiceLineMutation) EdgeCleared(name string) bool { switch name { case billinginvoiceline.EdgeBillingInvoice: return m.clearedbilling_invoice - case billinginvoiceline.EdgeBillingInvoiceManualLines: - return m.clearedbilling_invoice_manual_lines + case billinginvoiceline.EdgeManualFeeLine: + return m.clearedmanual_fee_line + case billinginvoiceline.EdgeManualUsageBasedLine: + return m.clearedmanual_usage_based_line + case billinginvoiceline.EdgeParentLine: + return m.clearedparent_line + case billinginvoiceline.EdgeChildLines: + return m.clearedchild_lines } return false } @@ -10925,8 +11265,14 @@ func (m *BillingInvoiceLineMutation) ClearEdge(name string) error { case billinginvoiceline.EdgeBillingInvoice: m.ClearBillingInvoice() return nil - case billinginvoiceline.EdgeBillingInvoiceManualLines: - m.ClearBillingInvoiceManualLines() + case billinginvoiceline.EdgeManualFeeLine: + m.ClearManualFeeLine() + return nil + case billinginvoiceline.EdgeManualUsageBasedLine: + m.ClearManualUsageBasedLine() + return nil + case billinginvoiceline.EdgeParentLine: + m.ClearParentLine() return nil } return fmt.Errorf("unknown BillingInvoiceLine unique edge %s", name) @@ -10939,8 +11285,17 @@ func (m *BillingInvoiceLineMutation) ResetEdge(name string) error { case billinginvoiceline.EdgeBillingInvoice: m.ResetBillingInvoice() return nil - case billinginvoiceline.EdgeBillingInvoiceManualLines: - m.ResetBillingInvoiceManualLines() + case billinginvoiceline.EdgeManualFeeLine: + m.ResetManualFeeLine() + return nil + case billinginvoiceline.EdgeManualUsageBasedLine: + m.ResetManualUsageBasedLine() + return nil + case billinginvoiceline.EdgeParentLine: + m.ResetParentLine() + return nil + case billinginvoiceline.EdgeChildLines: + m.ResetChildLines() return nil } return fmt.Errorf("unknown BillingInvoiceLine edge %s", name) @@ -11332,6 +11687,500 @@ func (m *BillingInvoiceManualLineConfigMutation) ResetEdge(name string) error { return fmt.Errorf("unknown BillingInvoiceManualLineConfig edge %s", name) } +// BillingInvoiceManualUsageBasedLineConfigMutation represents an operation that mutates the BillingInvoiceManualUsageBasedLineConfig nodes in the graph. +type BillingInvoiceManualUsageBasedLineConfigMutation struct { + config + op Op + typ string + id *string + namespace *string + price_type *plan.PriceType + feature_key *string + price **plan.Price + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) + predicates []predicate.BillingInvoiceManualUsageBasedLineConfig +} + +var _ ent.Mutation = (*BillingInvoiceManualUsageBasedLineConfigMutation)(nil) + +// billinginvoicemanualusagebasedlineconfigOption allows management of the mutation configuration using functional options. +type billinginvoicemanualusagebasedlineconfigOption func(*BillingInvoiceManualUsageBasedLineConfigMutation) + +// newBillingInvoiceManualUsageBasedLineConfigMutation creates new mutation for the BillingInvoiceManualUsageBasedLineConfig entity. +func newBillingInvoiceManualUsageBasedLineConfigMutation(c config, op Op, opts ...billinginvoicemanualusagebasedlineconfigOption) *BillingInvoiceManualUsageBasedLineConfigMutation { + m := &BillingInvoiceManualUsageBasedLineConfigMutation{ + config: c, + op: op, + typ: TypeBillingInvoiceManualUsageBasedLineConfig, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withBillingInvoiceManualUsageBasedLineConfigID sets the ID field of the mutation. +func withBillingInvoiceManualUsageBasedLineConfigID(id string) billinginvoicemanualusagebasedlineconfigOption { + return func(m *BillingInvoiceManualUsageBasedLineConfigMutation) { + var ( + err error + once sync.Once + value *BillingInvoiceManualUsageBasedLineConfig + ) + m.oldValue = func(ctx context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().BillingInvoiceManualUsageBasedLineConfig.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withBillingInvoiceManualUsageBasedLineConfig sets the old BillingInvoiceManualUsageBasedLineConfig of the mutation. +func withBillingInvoiceManualUsageBasedLineConfig(node *BillingInvoiceManualUsageBasedLineConfig) billinginvoicemanualusagebasedlineconfigOption { + return func(m *BillingInvoiceManualUsageBasedLineConfigMutation) { + m.oldValue = func(context.Context) (*BillingInvoiceManualUsageBasedLineConfig, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m BillingInvoiceManualUsageBasedLineConfigMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m BillingInvoiceManualUsageBasedLineConfigMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("db: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of BillingInvoiceManualUsageBasedLineConfig entities. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetID(id string) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ID() (id string, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().BillingInvoiceManualUsageBasedLineConfig.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetNamespace sets the "namespace" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetNamespace(s string) { + m.namespace = &s +} + +// Namespace returns the value of the "namespace" field in the mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Namespace() (r string, exists bool) { + v := m.namespace + if v == nil { + return + } + return *v, true +} + +// OldNamespace returns the old "namespace" field's value of the BillingInvoiceManualUsageBasedLineConfig entity. +// If the BillingInvoiceManualUsageBasedLineConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) OldNamespace(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNamespace is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNamespace requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNamespace: %w", err) + } + return oldValue.Namespace, nil +} + +// ResetNamespace resets all changes to the "namespace" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ResetNamespace() { + m.namespace = nil +} + +// SetPriceType sets the "price_type" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetPriceType(pt plan.PriceType) { + m.price_type = &pt +} + +// PriceType returns the value of the "price_type" field in the mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) PriceType() (r plan.PriceType, exists bool) { + v := m.price_type + if v == nil { + return + } + return *v, true +} + +// OldPriceType returns the old "price_type" field's value of the BillingInvoiceManualUsageBasedLineConfig entity. +// If the BillingInvoiceManualUsageBasedLineConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) OldPriceType(ctx context.Context) (v plan.PriceType, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPriceType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPriceType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPriceType: %w", err) + } + return oldValue.PriceType, nil +} + +// ResetPriceType resets all changes to the "price_type" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ResetPriceType() { + m.price_type = nil +} + +// SetFeatureKey sets the "feature_key" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetFeatureKey(s string) { + m.feature_key = &s +} + +// FeatureKey returns the value of the "feature_key" field in the mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) FeatureKey() (r string, exists bool) { + v := m.feature_key + if v == nil { + return + } + return *v, true +} + +// OldFeatureKey returns the old "feature_key" field's value of the BillingInvoiceManualUsageBasedLineConfig entity. +// If the BillingInvoiceManualUsageBasedLineConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) OldFeatureKey(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFeatureKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFeatureKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFeatureKey: %w", err) + } + return oldValue.FeatureKey, nil +} + +// ResetFeatureKey resets all changes to the "feature_key" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ResetFeatureKey() { + m.feature_key = nil +} + +// SetPrice sets the "price" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetPrice(pl *plan.Price) { + m.price = &pl +} + +// Price returns the value of the "price" field in the mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Price() (r *plan.Price, exists bool) { + v := m.price + if v == nil { + return + } + return *v, true +} + +// OldPrice returns the old "price" field's value of the BillingInvoiceManualUsageBasedLineConfig entity. +// If the BillingInvoiceManualUsageBasedLineConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) OldPrice(ctx context.Context) (v *plan.Price, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrice is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrice requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrice: %w", err) + } + return oldValue.Price, nil +} + +// ResetPrice resets all changes to the "price" field. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ResetPrice() { + m.price = nil +} + +// Where appends a list predicates to the BillingInvoiceManualUsageBasedLineConfigMutation builder. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Where(ps ...predicate.BillingInvoiceManualUsageBasedLineConfig) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the BillingInvoiceManualUsageBasedLineConfigMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.BillingInvoiceManualUsageBasedLineConfig, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (BillingInvoiceManualUsageBasedLineConfig). +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.namespace != nil { + fields = append(fields, billinginvoicemanualusagebasedlineconfig.FieldNamespace) + } + if m.price_type != nil { + fields = append(fields, billinginvoicemanualusagebasedlineconfig.FieldPriceType) + } + if m.feature_key != nil { + fields = append(fields, billinginvoicemanualusagebasedlineconfig.FieldFeatureKey) + } + if m.price != nil { + fields = append(fields, billinginvoicemanualusagebasedlineconfig.FieldPrice) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) Field(name string) (ent.Value, bool) { + switch name { + case billinginvoicemanualusagebasedlineconfig.FieldNamespace: + return m.Namespace() + case billinginvoicemanualusagebasedlineconfig.FieldPriceType: + return m.PriceType() + case billinginvoicemanualusagebasedlineconfig.FieldFeatureKey: + return m.FeatureKey() + case billinginvoicemanualusagebasedlineconfig.FieldPrice: + return m.Price() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case billinginvoicemanualusagebasedlineconfig.FieldNamespace: + return m.OldNamespace(ctx) + case billinginvoicemanualusagebasedlineconfig.FieldPriceType: + return m.OldPriceType(ctx) + case billinginvoicemanualusagebasedlineconfig.FieldFeatureKey: + return m.OldFeatureKey(ctx) + case billinginvoicemanualusagebasedlineconfig.FieldPrice: + return m.OldPrice(ctx) + } + return nil, fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) SetField(name string, value ent.Value) error { + switch name { + case billinginvoicemanualusagebasedlineconfig.FieldNamespace: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNamespace(v) + return nil + case billinginvoicemanualusagebasedlineconfig.FieldPriceType: + v, ok := value.(plan.PriceType) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPriceType(v) + return nil + case billinginvoicemanualusagebasedlineconfig.FieldFeatureKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFeatureKey(v) + return nil + case billinginvoicemanualusagebasedlineconfig.FieldPrice: + v, ok := value.(*plan.Price) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrice(v) + return nil + } + return fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ClearField(name string) error { + return fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ResetField(name string) error { + switch name { + case billinginvoicemanualusagebasedlineconfig.FieldNamespace: + m.ResetNamespace() + return nil + case billinginvoicemanualusagebasedlineconfig.FieldPriceType: + m.ResetPriceType() + return nil + case billinginvoicemanualusagebasedlineconfig.FieldFeatureKey: + m.ResetFeatureKey() + return nil + case billinginvoicemanualusagebasedlineconfig.FieldPrice: + m.ResetPrice() + return nil + } + return fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *BillingInvoiceManualUsageBasedLineConfigMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown BillingInvoiceManualUsageBasedLineConfig edge %s", name) +} + // BillingInvoiceValidationIssueMutation represents an operation that mutates the BillingInvoiceValidationIssue nodes in the graph. type BillingInvoiceValidationIssueMutation struct { config diff --git a/openmeter/ent/db/paginate.go b/openmeter/ent/db/paginate.go index cba1e85ec..45e18b51c 100644 --- a/openmeter/ent/db/paginate.go +++ b/openmeter/ent/db/paginate.go @@ -441,6 +441,54 @@ func (bimlc *BillingInvoiceManualLineConfigQuery) Paginate(ctx context.Context, // type check var _ pagination.Paginator[*BillingInvoiceManualLineConfig] = (*BillingInvoiceManualLineConfigQuery)(nil) +// Paginate runs the query and returns a paginated response. +// If page is its 0 value then it will return all the items and populate the response page accordingly. +func (bimublc *BillingInvoiceManualUsageBasedLineConfigQuery) Paginate(ctx context.Context, page pagination.Page) (pagination.PagedResponse[*BillingInvoiceManualUsageBasedLineConfig], error) { + // Get the limit and offset + limit, offset := page.Limit(), page.Offset() + + // Unset previous pagination settings + zero := 0 + bimublc.ctx.Offset = &zero + bimublc.ctx.Limit = &zero + + // Create duplicate of the query to run for + countQuery := bimublc.Clone() + pagedQuery := bimublc + + // Unset ordering for count query + countQuery.order = nil + + pagedResponse := pagination.PagedResponse[*BillingInvoiceManualUsageBasedLineConfig]{ + Page: page, + } + + // Get the total count + count, err := countQuery.Count(ctx) + if err != nil { + return pagedResponse, fmt.Errorf("failed to get count: %w", err) + } + pagedResponse.TotalCount = count + + // If page is its 0 value then return all the items + if page.IsZero() { + offset = 0 + limit = count + } + + // Set the limit and offset + pagedQuery.ctx.Limit = &limit + pagedQuery.ctx.Offset = &offset + + // Get the paged items + items, err := pagedQuery.All(ctx) + pagedResponse.Items = items + return pagedResponse, err +} + +// type check +var _ pagination.Paginator[*BillingInvoiceManualUsageBasedLineConfig] = (*BillingInvoiceManualUsageBasedLineConfigQuery)(nil) + // Paginate runs the query and returns a paginated response. // If page is its 0 value then it will return all the items and populate the response page accordingly. func (bivi *BillingInvoiceValidationIssueQuery) Paginate(ctx context.Context, page pagination.Page) (pagination.PagedResponse[*BillingInvoiceValidationIssue], error) { diff --git a/openmeter/ent/db/predicate/predicate.go b/openmeter/ent/db/predicate/predicate.go index af83790d5..92d0a5d76 100644 --- a/openmeter/ent/db/predicate/predicate.go +++ b/openmeter/ent/db/predicate/predicate.go @@ -33,6 +33,20 @@ type BillingInvoiceLine func(*sql.Selector) // BillingInvoiceManualLineConfig is the predicate function for billinginvoicemanuallineconfig builders. type BillingInvoiceManualLineConfig func(*sql.Selector) +// BillingInvoiceManualUsageBasedLineConfig is the predicate function for billinginvoicemanualusagebasedlineconfig builders. +type BillingInvoiceManualUsageBasedLineConfig func(*sql.Selector) + +// BillingInvoiceManualUsageBasedLineConfigOrErr calls the predicate only if the error is not nit. +func BillingInvoiceManualUsageBasedLineConfigOrErr(p BillingInvoiceManualUsageBasedLineConfig, err error) BillingInvoiceManualUsageBasedLineConfig { + return func(s *sql.Selector) { + if err != nil { + s.AddError(err) + return + } + p(s) + } +} + // BillingInvoiceValidationIssue is the predicate function for billinginvoicevalidationissue builders. type BillingInvoiceValidationIssue func(*sql.Selector) diff --git a/openmeter/ent/db/runtime.go b/openmeter/ent/db/runtime.go index eca51f9a7..5bb2038d5 100644 --- a/openmeter/ent/db/runtime.go +++ b/openmeter/ent/db/runtime.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoice" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoiceline" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanuallineconfig" + "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicemanualusagebasedlineconfig" "github.com/openmeterio/openmeter/openmeter/ent/db/billinginvoicevalidationissue" "github.com/openmeterio/openmeter/openmeter/ent/db/billingprofile" "github.com/openmeterio/openmeter/openmeter/ent/db/billingworkflowconfig" @@ -290,15 +291,15 @@ func init() { // billinginvoice.CustomerNameValidator is a validator for the "customer_name" field. It is called by the builders before save. billinginvoice.CustomerNameValidator = billinginvoiceDescCustomerName.Validators[0].(func(string) error) // billinginvoiceDescCustomerID is the schema descriptor for customer_id field. - billinginvoiceDescCustomerID := billinginvoiceFields[7].Descriptor() + billinginvoiceDescCustomerID := billinginvoiceFields[8].Descriptor() // billinginvoice.CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. billinginvoice.CustomerIDValidator = billinginvoiceDescCustomerID.Validators[0].(func(string) error) // billinginvoiceDescSourceBillingProfileID is the schema descriptor for source_billing_profile_id field. - billinginvoiceDescSourceBillingProfileID := billinginvoiceFields[8].Descriptor() + billinginvoiceDescSourceBillingProfileID := billinginvoiceFields[9].Descriptor() // billinginvoice.SourceBillingProfileIDValidator is a validator for the "source_billing_profile_id" field. It is called by the builders before save. billinginvoice.SourceBillingProfileIDValidator = billinginvoiceDescSourceBillingProfileID.Validators[0].(func(string) error) // billinginvoiceDescCurrency is the schema descriptor for currency field. - billinginvoiceDescCurrency := billinginvoiceFields[12].Descriptor() + billinginvoiceDescCurrency := billinginvoiceFields[13].Descriptor() // billinginvoice.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. billinginvoice.CurrencyValidator = billinginvoiceDescCurrency.Validators[0].(func(string) error) // billinginvoiceDescID is the schema descriptor for id field. @@ -325,7 +326,7 @@ func init() { // billinginvoiceline.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. billinginvoiceline.UpdateDefaultUpdatedAt = billinginvoicelineDescUpdatedAt.UpdateDefault.(func() time.Time) // billinginvoicelineDescCurrency is the schema descriptor for currency field. - billinginvoicelineDescCurrency := billinginvoicelineFields[6].Descriptor() + billinginvoicelineDescCurrency := billinginvoicelineFields[7].Descriptor() // billinginvoiceline.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. billinginvoiceline.CurrencyValidator = billinginvoicelineDescCurrency.Validators[0].(func(string) error) // billinginvoicelineDescID is the schema descriptor for id field. @@ -347,6 +348,28 @@ func init() { billinginvoicemanuallineconfigDescID := billinginvoicemanuallineconfigMixinFields1[0].Descriptor() // billinginvoicemanuallineconfig.DefaultID holds the default value on creation for the id field. billinginvoicemanuallineconfig.DefaultID = billinginvoicemanuallineconfigDescID.Default.(func() string) + billinginvoicemanualusagebasedlineconfigMixin := schema.BillingInvoiceManualUsageBasedLineConfig{}.Mixin() + billinginvoicemanualusagebasedlineconfigMixinFields0 := billinginvoicemanualusagebasedlineconfigMixin[0].Fields() + _ = billinginvoicemanualusagebasedlineconfigMixinFields0 + billinginvoicemanualusagebasedlineconfigMixinFields1 := billinginvoicemanualusagebasedlineconfigMixin[1].Fields() + _ = billinginvoicemanualusagebasedlineconfigMixinFields1 + billinginvoicemanualusagebasedlineconfigFields := schema.BillingInvoiceManualUsageBasedLineConfig{}.Fields() + _ = billinginvoicemanualusagebasedlineconfigFields + // billinginvoicemanualusagebasedlineconfigDescNamespace is the schema descriptor for namespace field. + billinginvoicemanualusagebasedlineconfigDescNamespace := billinginvoicemanualusagebasedlineconfigMixinFields0[0].Descriptor() + // billinginvoicemanualusagebasedlineconfig.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. + billinginvoicemanualusagebasedlineconfig.NamespaceValidator = billinginvoicemanualusagebasedlineconfigDescNamespace.Validators[0].(func(string) error) + // billinginvoicemanualusagebasedlineconfigDescFeatureKey is the schema descriptor for feature_key field. + billinginvoicemanualusagebasedlineconfigDescFeatureKey := billinginvoicemanualusagebasedlineconfigFields[1].Descriptor() + // billinginvoicemanualusagebasedlineconfig.FeatureKeyValidator is a validator for the "feature_key" field. It is called by the builders before save. + billinginvoicemanualusagebasedlineconfig.FeatureKeyValidator = billinginvoicemanualusagebasedlineconfigDescFeatureKey.Validators[0].(func(string) error) + // billinginvoicemanualusagebasedlineconfigDescPrice is the schema descriptor for price field. + billinginvoicemanualusagebasedlineconfigDescPrice := billinginvoicemanualusagebasedlineconfigFields[2].Descriptor() + billinginvoicemanualusagebasedlineconfig.ValueScanner.Price = billinginvoicemanualusagebasedlineconfigDescPrice.ValueScanner.(field.TypeValueScanner[*plan.Price]) + // billinginvoicemanualusagebasedlineconfigDescID is the schema descriptor for id field. + billinginvoicemanualusagebasedlineconfigDescID := billinginvoicemanualusagebasedlineconfigMixinFields1[0].Descriptor() + // billinginvoicemanualusagebasedlineconfig.DefaultID holds the default value on creation for the id field. + billinginvoicemanualusagebasedlineconfig.DefaultID = billinginvoicemanualusagebasedlineconfigDescID.Default.(func() string) billinginvoicevalidationissueMixin := schema.BillingInvoiceValidationIssue{}.Mixin() billinginvoicevalidationissueMixinFields0 := billinginvoicevalidationissueMixin[0].Fields() _ = billinginvoicevalidationissueMixinFields0 diff --git a/openmeter/ent/db/setorclear.go b/openmeter/ent/db/setorclear.go index 93f6cfa7a..9cb8d0c7e 100644 --- a/openmeter/ent/db/setorclear.go +++ b/openmeter/ent/db/setorclear.go @@ -490,6 +490,20 @@ func (u *BillingInvoiceUpdateOne) SetOrClearCustomerTimezone(value *timezone.Tim return u.SetCustomerTimezone(*value) } +func (u *BillingInvoiceUpdate) SetOrClearCustomerSubjectKeys(value *[]string) *BillingInvoiceUpdate { + if value == nil { + return u.ClearCustomerSubjectKeys() + } + return u.SetCustomerSubjectKeys(*value) +} + +func (u *BillingInvoiceUpdateOne) SetOrClearCustomerSubjectKeys(value *[]string) *BillingInvoiceUpdateOne { + if value == nil { + return u.ClearCustomerSubjectKeys() + } + return u.SetCustomerSubjectKeys(*value) +} + func (u *BillingInvoiceUpdate) SetOrClearNumber(value *string) *BillingInvoiceUpdate { if value == nil { return u.ClearNumber() @@ -644,6 +658,20 @@ func (u *BillingInvoiceLineUpdateOne) SetOrClearDescription(value *string) *Bill return u.SetDescription(*value) } +func (u *BillingInvoiceLineUpdate) SetOrClearParentLineID(value *string) *BillingInvoiceLineUpdate { + if value == nil { + return u.ClearParentLineID() + } + return u.SetParentLineID(*value) +} + +func (u *BillingInvoiceLineUpdateOne) SetOrClearParentLineID(value *string) *BillingInvoiceLineUpdateOne { + if value == nil { + return u.ClearParentLineID() + } + return u.SetParentLineID(*value) +} + func (u *BillingInvoiceLineUpdate) SetOrClearQuantity(value *alpacadecimal.Decimal) *BillingInvoiceLineUpdate { if value == nil { return u.ClearQuantity() diff --git a/openmeter/ent/db/tx.go b/openmeter/ent/db/tx.go index 4bf3a6175..7bb1f1242 100644 --- a/openmeter/ent/db/tx.go +++ b/openmeter/ent/db/tx.go @@ -32,6 +32,8 @@ type Tx struct { BillingInvoiceLine *BillingInvoiceLineClient // BillingInvoiceManualLineConfig is the client for interacting with the BillingInvoiceManualLineConfig builders. BillingInvoiceManualLineConfig *BillingInvoiceManualLineConfigClient + // BillingInvoiceManualUsageBasedLineConfig is the client for interacting with the BillingInvoiceManualUsageBasedLineConfig builders. + BillingInvoiceManualUsageBasedLineConfig *BillingInvoiceManualUsageBasedLineConfigClient // BillingInvoiceValidationIssue is the client for interacting with the BillingInvoiceValidationIssue builders. BillingInvoiceValidationIssue *BillingInvoiceValidationIssueClient // BillingProfile is the client for interacting with the BillingProfile builders. @@ -204,6 +206,7 @@ func (tx *Tx) init() { tx.BillingInvoice = NewBillingInvoiceClient(tx.config) tx.BillingInvoiceLine = NewBillingInvoiceLineClient(tx.config) tx.BillingInvoiceManualLineConfig = NewBillingInvoiceManualLineConfigClient(tx.config) + tx.BillingInvoiceManualUsageBasedLineConfig = NewBillingInvoiceManualUsageBasedLineConfigClient(tx.config) tx.BillingInvoiceValidationIssue = NewBillingInvoiceValidationIssueClient(tx.config) tx.BillingProfile = NewBillingProfileClient(tx.config) tx.BillingWorkflowConfig = NewBillingWorkflowConfigClient(tx.config) diff --git a/openmeter/ent/schema/billing.go b/openmeter/ent/schema/billing.go index 3b1116acc..985a2a6b2 100644 --- a/openmeter/ent/schema/billing.go +++ b/openmeter/ent/schema/billing.go @@ -3,12 +3,14 @@ package schema import ( "entgo.io/ent" "entgo.io/ent/dialect" + "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "entgo.io/ent/schema/index" "github.com/alpacahq/alpacadecimal" billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datex" "github.com/openmeterio/openmeter/pkg/framework/entutils" @@ -243,6 +245,11 @@ func (BillingInvoiceLine) Fields() []ent.Field { "postgres": "char(26)", }), + field.String("parent_line_id"). + SchemaType(map[string]string{ + "postgres": "char(26)", + }).Optional().Nillable(), + field.Time("period_start"), field.Time("period_end"), field.Time("invoice_at"), @@ -250,7 +257,8 @@ func (BillingInvoiceLine) Fields() []ent.Field { // TODO[dependency]: overrides (as soon as plan override entities are ready) field.Enum("type"). - GoType(billingentity.InvoiceLineType("")), + GoType(billingentity.InvoiceLineType("")). + Immutable(), field.Enum("status"). GoType(billingentity.InvoiceLineStatus("")), @@ -283,6 +291,7 @@ func (BillingInvoiceLine) Fields() []ent.Field { func (BillingInvoiceLine) Indexes() []ent.Index { return []ent.Index{ index.Fields("namespace", "invoice_id"), + index.Fields("namespace", "parent_line_id"), } } @@ -293,9 +302,19 @@ func (BillingInvoiceLine) Edges() []ent.Edge { Field("invoice_id"). Unique(). Required(), - edge.To("billing_invoice_manual_lines", BillingInvoiceManualLineConfig.Type). + edge.To("manual_fee_line", BillingInvoiceManualLineConfig.Type). StorageKey(edge.Column("manual_line_config_id")). - Unique(), + Unique(). + Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("manual_usage_based_line", BillingInvoiceManualUsageBasedLineConfig.Type). + StorageKey(edge.Column("manual_usage_based_line_config_id")). + Unique(). + Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("child_lines", BillingInvoiceLine.Type). + From("parent_line"). + Field("parent_line_id"). + Unique(). + Annotations(entsql.OnDelete(entsql.Cascade)), } } @@ -312,7 +331,6 @@ func (BillingInvoiceManualLineConfig) Mixin() []ent.Mixin { func (BillingInvoiceManualLineConfig) Fields() []ent.Field { return []ent.Field{ - // Either coming from the field.Other("unit_price", alpacadecimal.Decimal{}). SchemaType(map[string]string{ "postgres": "numeric", @@ -320,13 +338,31 @@ func (BillingInvoiceManualLineConfig) Fields() []ent.Field { } } -func (BillingInvoiceManualLineConfig) Edgexs() []ent.Edge { - return []ent.Edge{ - edge.From("billing_invoice_line", BillingInvoiceLine.Type). - Ref("billing_invoice_manual_lines"). - Field("id"). - Unique(). - Required(), +type BillingInvoiceManualUsageBasedLineConfig struct { + ent.Schema +} + +func (BillingInvoiceManualUsageBasedLineConfig) Mixin() []ent.Mixin { + return []ent.Mixin{ + entutils.NamespaceMixin{}, + entutils.IDMixin{}, + } +} + +func (BillingInvoiceManualUsageBasedLineConfig) Fields() []ent.Field { + return []ent.Field{ + // TODO: do we need this? + field.Enum("price_type"). + GoType(plan.PriceType("")), + field.String("feature_key"). + Immutable(). + NotEmpty(), + field.String("price"). // TODO: Immutable? + GoType(&plan.Price{}). + ValueScanner(PriceValueScanner). + SchemaType(map[string]string{ + dialect.Postgres: "jsonb", + }), } } @@ -368,6 +404,9 @@ func (BillingInvoice) Fields() []ent.Field { Optional(). Nillable(), + field.Strings("customer_subject_keys"). + Optional(), + // Invoice number field.String("number"). Optional(). @@ -475,8 +514,10 @@ func (BillingInvoice) Edges() []ent.Edge { Field("workflow_config_id"). Unique(). Required(), - edge.To("billing_invoice_lines", BillingInvoiceLine.Type), - edge.To("billing_invoice_validation_issues", BillingInvoiceValidationIssue.Type), + edge.To("billing_invoice_lines", BillingInvoiceLine.Type). + Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("billing_invoice_validation_issues", BillingInvoiceValidationIssue.Type). + Annotations(entsql.OnDelete(entsql.Cascade)), edge.From("billing_invoice_customer", Customer.Type). Ref("billing_invoice"). Field("customer_id"). diff --git a/openmeter/meter/inmemory.go b/openmeter/meter/inmemory.go index d6f0ac1ee..93fd8227f 100644 --- a/openmeter/meter/inmemory.go +++ b/openmeter/meter/inmemory.go @@ -61,3 +61,10 @@ func (c *InMemoryRepository) GetMeterByIDOrSlug(_ context.Context, namespace str MeterSlug: idOrSlug, } } + +// ReplaceMeters can be used to replace all meters in the repository. +func (c *InMemoryRepository) ReplaceMeters(_ context.Context, meters []models.Meter) { + c.init() + + c.meters = slices.Clone(meters) +} diff --git a/openmeter/streaming/testutils/streaming.go b/openmeter/streaming/testutils/streaming.go index 071cb28ad..374a571c9 100644 --- a/openmeter/streaming/testutils/streaming.go +++ b/openmeter/streaming/testutils/streaming.go @@ -13,10 +13,9 @@ import ( func NewMockStreamingConnector(t testing.TB) *MockStreamingConnector { t.Helper() - return &MockStreamingConnector{ - rows: map[string][]models.MeterQueryRow{}, - events: map[string][]SimpleEvent{}, - } + out := &MockStreamingConnector{} + out.Reset() + return out } type SimpleEvent struct { @@ -30,6 +29,11 @@ type MockStreamingConnector struct { events map[string][]SimpleEvent } +func (m *MockStreamingConnector) Reset() { + m.rows = map[string][]models.MeterQueryRow{} + m.events = map[string][]SimpleEvent{} +} + func (m *MockStreamingConnector) AddSimpleEvent(meterSlug string, value float64, at time.Time) { m.events[meterSlug] = append(m.events[meterSlug], SimpleEvent{ MeterSlug: meterSlug, diff --git a/pkg/slicesx/map.go b/pkg/slicesx/map.go index 409e79493..f7472bcff 100644 --- a/pkg/slicesx/map.go +++ b/pkg/slicesx/map.go @@ -1,5 +1,7 @@ package slicesx +import "errors" + // Map maps elements of a slice from T to M, returning a new slice. func Map[T any, S any](s []T, f func(T) S) []S { // Nil input, return early. @@ -15,3 +17,31 @@ func Map[T any, S any](s []T, f func(T) S) []S { return n } + +// MapWithErr maps elements of a slice from T to M, returning a new slice and a joined error if there are any. +// If an error is returned from the mapping function, a nil array and the error is returned. +func MapWithErr[T any, S any](s []T, f func(T) (S, error)) ([]S, error) { + // Nil input, return early. + if s == nil { + return nil, nil + } + + var outErr error + n := make([]S, 0, len(s)) + + for _, v := range s { + res, err := f(v) + if err != nil { + outErr = errors.Join(outErr, err) + continue + } + + n = append(n, res) + } + + if outErr != nil { + return nil, outErr + } + + return n, nil +} diff --git a/test/billing/invoice_test.go b/test/billing/invoice_test.go index 8bab36171..2e0e2ddd9 100644 --- a/test/billing/invoice_test.go +++ b/test/billing/invoice_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "slices" "testing" "time" @@ -19,6 +20,8 @@ import ( billingadapter "github.com/openmeterio/openmeter/openmeter/billing/adapter" billingentity "github.com/openmeterio/openmeter/openmeter/billing/entity" customerentity "github.com/openmeterio/openmeter/openmeter/customer/entity" + "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datex" "github.com/openmeterio/openmeter/pkg/models" @@ -64,12 +67,33 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { PhoneNumber: lo.ToPtr("1234567890"), }, Currency: lo.ToPtr(currencyx.Code(currency.USD)), + UsageAttribution: customerentity.CustomerUsageAttribution{ + SubjectKeys: []string{"test"}, + }, }, }) require.NoError(s.T(), err) require.NotNil(s.T(), customerEntity) require.NotEmpty(s.T(), customerEntity.ID) + s.MeterRepo.ReplaceMeters(ctx, []models.Meter{ + { + Namespace: namespace, + Slug: "test", + WindowSize: models.WindowSizeMinute, + Aggregation: models.MeterAggregationSum, + }, + }) + defer s.MeterRepo.ReplaceMeters(ctx, []models.Meter{}) + + _, err = s.FeatureService.CreateFeature(ctx, feature.CreateFeatureInputs{ + Namespace: namespace, + Name: "test", + Key: "test", + MeterSlug: lo.ToPtr("test"), + }) + require.NoError(s.T(), err) + // Given we have a default profile for the namespace var billingProfile billingentity.Profile @@ -111,15 +135,14 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { "key": "value", }, }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(100), Quantity: alpacadecimal.NewFromFloat(1), }, }, { LineBase: billingentity.LineBase{ - Namespace: namespace, - Period: billingentity.Period{Start: periodStart, End: periodEnd}, + Period: billingentity.Period{Start: periodStart, End: periodEnd}, InvoiceAt: issueAt, @@ -128,11 +151,42 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { Name: "Test item - HUF", Currency: currencyx.Code(currency.HUF), }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(200), Quantity: alpacadecimal.NewFromFloat(3), }, }, + { + LineBase: billingentity.LineBase{ + Period: billingentity.Period{Start: periodStart, End: periodEnd}, + + InvoiceAt: issueAt, + + Type: billingentity.InvoiceLineTypeManualUsageBased, + + Name: "Test item - HUF", + Currency: currencyx.Code(currency.HUF), + }, + ManualUsageBased: billingentity.ManualUsageBasedLine{ + Price: plan.NewPriceFrom(plan.TieredPrice{ + Mode: plan.VolumeTieredPrice, + Tiers: []plan.PriceTier{ + { + UpToAmount: lo.ToPtr(alpacadecimal.NewFromFloat(100)), + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(10), + }, + }, + { + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(100), + }, + }, + }, + }), + FeatureKey: "test", + }, + }, }, }) @@ -172,6 +226,8 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { Name: "Test item - USD", Currency: currencyx.Code(currency.USD), + Status: billingentity.InvoiceLineStatusValid, + CreatedAt: usdInvoice.Lines[0].CreatedAt, UpdatedAt: usdInvoice.Lines[0].UpdatedAt, @@ -179,7 +235,7 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { "key": "value", }, }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(100), Quantity: alpacadecimal.NewFromFloat(1), }, @@ -220,6 +276,7 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { Name: customerEntity.Name, BillingAddress: customerEntity.BillingAddress, + Subjects: []string{"test"}, }, Supplier: billingProfile.Supplier, @@ -228,7 +285,7 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { ExpandedFields: billingentity.InvoiceExpandAll, }) - require.Len(s.T(), items, 2) + require.Len(s.T(), items, 3) // Validate that the create returns the expected items items[0].CreatedAt = expectedUSDLine.CreatedAt items[0].UpdatedAt = expectedUSDLine.UpdatedAt @@ -258,8 +315,46 @@ func (s *InvoicingTestSuite) TestPendingLineCreation() { require.NoError(s.T(), err) require.Len(s.T(), hufInvoices.Items, 1) - // Then we have one line item for the invoice - require.Len(s.T(), hufInvoices.Items[0].Lines, 1) + // Then we have two line items for the invoice + require.Len(s.T(), hufInvoices.Items[0].Lines, 2) + + _, found := lo.Find(hufInvoices.Items[0].Lines, func(l billingentity.Line) bool { + return l.Type == billingentity.InvoiceLineTypeManualFee + }) + require.True(s.T(), found, "manual fee item is present") + + // Then we should have the tiered price present + tieredLine, found := lo.Find(hufInvoices.Items[0].Lines, func(l billingentity.Line) bool { + return l.Type == billingentity.InvoiceLineTypeManualUsageBased + }) + + require.True(s.T(), found, "tiered price item is present") + require.Equal(s.T(), tieredLine.ManualUsageBased.Price.Type(), plan.TieredPriceType) + tieredPrice, err := tieredLine.ManualUsageBased.Price.AsTiered() + require.NoError(s.T(), err) + + require.Equal(s.T(), + tieredPrice, + plan.TieredPrice{ + PriceMeta: plan.PriceMeta{ + Type: plan.TieredPriceType, + }, + Mode: plan.VolumeTieredPrice, + Tiers: []plan.PriceTier{ + { + UpToAmount: lo.ToPtr(alpacadecimal.NewFromFloat(100)), + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(10), + }, + }, + { + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(100), + }, + }, + }, + }, + ) }) s.T().Run("Expand scenarios - no expand", func(t *testing.T) { @@ -399,7 +494,7 @@ func (s *InvoicingTestSuite) TestCreateInvoice() { "key": "value", }, }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(100), Quantity: alpacadecimal.NewFromFloat(1), }, @@ -416,7 +511,7 @@ func (s *InvoicingTestSuite) TestCreateInvoice() { Name: "Test item2", Currency: currencyx.Code(currency.USD), }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(200), Quantity: alpacadecimal.NewFromFloat(3), }, @@ -596,7 +691,7 @@ func (s *InvoicingTestSuite) createDraftInvoice(t *testing.T, ctx context.Contex "key": "value", }, }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(100), Quantity: alpacadecimal.NewFromFloat(1), }, @@ -613,7 +708,7 @@ func (s *InvoicingTestSuite) createDraftInvoice(t *testing.T, ctx context.Contex Name: "Test item2", Currency: currencyx.Code(currency.USD), }, - ManualFee: &billingentity.ManualFeeLine{ + ManualFee: billingentity.ManualFeeLine{ Price: alpacadecimal.NewFromFloat(200), Quantity: alpacadecimal.NewFromFloat(3), }, @@ -1140,3 +1235,498 @@ func (s *InvoicingTestSuite) TestInvoicingFlowErrorHandling() { }) } } + +func (s *InvoicingTestSuite) TestUBPInvoicing() { + namespace := "ns-ubp-invoicing" + ctx := context.Background() + + periodStart := lo.Must(time.Parse(time.RFC3339, "2024-09-02T12:13:14Z")) + periodEnd := lo.Must(time.Parse(time.RFC3339, "2024-09-03T12:13:14Z")) + + _ = s.installSandboxApp(s.T(), namespace) + + s.MeterRepo.ReplaceMeters(ctx, []models.Meter{ + { + Namespace: namespace, + Slug: "flat-per-unit", + WindowSize: models.WindowSizeMinute, + Aggregation: models.MeterAggregationSum, + }, + { + Namespace: namespace, + Slug: "flat-per-usage", + WindowSize: models.WindowSizeMinute, + Aggregation: models.MeterAggregationSum, + }, + { + Namespace: namespace, + Slug: "tiered-volume", + WindowSize: models.WindowSizeMinute, + Aggregation: models.MeterAggregationSum, + }, + { + Namespace: namespace, + Slug: "tiered-graduated", + WindowSize: models.WindowSizeMinute, + Aggregation: models.MeterAggregationSum, + }, + }) + defer s.MeterRepo.ReplaceMeters(ctx, []models.Meter{}) + + // Let's initialize the mock streaming connector with data that is out of the period so that we + // can start with empty values + for _, slug := range []string{"flat-per-unit", "flat-per-usage", "tiered-volume", "tiered-graduated"} { + s.MockStreamingConnector.AddSimpleEvent(slug, 0, periodStart.Add(-time.Minute)) + } + + s.MockStreamingConnector.AddSimpleEvent("flat-per-unit", 10, periodStart) + defer s.MockStreamingConnector.Reset() + + // Let's create the features + // TODO[later]: we need to handle archived features, do we want to issue a warning? Can features be archived when used + // by a draft invoice? + features := ubpFeatures{ + flatPerUnit: lo.Must(s.FeatureService.CreateFeature(ctx, feature.CreateFeatureInputs{ + Namespace: namespace, + Name: "flat-per-unit", + Key: "flat-per-unit", + MeterSlug: lo.ToPtr("flat-per-unit"), + })), + flatPerUsage: lo.Must(s.FeatureService.CreateFeature(ctx, feature.CreateFeatureInputs{ + Namespace: namespace, + Name: "flat-per-usage", + Key: "flat-per-usage", + MeterSlug: lo.ToPtr("flat-per-usage"), + })), + tieredVolume: lo.Must(s.FeatureService.CreateFeature(ctx, feature.CreateFeatureInputs{ + Namespace: namespace, + Name: "tiered-volume", + Key: "tiered-volume", + MeterSlug: lo.ToPtr("tiered-volume"), + })), + tieredGraduated: lo.Must(s.FeatureService.CreateFeature(ctx, feature.CreateFeatureInputs{ + Namespace: namespace, + Name: "tiered-graduated", + Key: "tiered-graduated", + MeterSlug: lo.ToPtr("tiered-graduated"), + })), + } + + // Given we have a test customer + + customerEntity, err := s.CustomerService.CreateCustomer(ctx, customerentity.CreateCustomerInput{ + Namespace: namespace, + + Customer: customerentity.Customer{ + ManagedResource: models.NewManagedResource(models.ManagedResourceInput{ + Name: "Test Customer", + }), + PrimaryEmail: lo.ToPtr("test@test.com"), + BillingAddress: &models.Address{ + Country: lo.ToPtr(models.CountryCode("US")), + PostalCode: lo.ToPtr("12345"), + State: lo.ToPtr("NY"), + City: lo.ToPtr("New York"), + Line1: lo.ToPtr("1234 Test St"), + Line2: lo.ToPtr("Apt 1"), + PhoneNumber: lo.ToPtr("1234567890"), + }, + Currency: lo.ToPtr(currencyx.Code(currency.USD)), + UsageAttribution: customerentity.CustomerUsageAttribution{ + SubjectKeys: []string{"test"}, + }, + }, + }) + require.NoError(s.T(), err) + require.NotNil(s.T(), customerEntity) + require.NotEmpty(s.T(), customerEntity.ID) + + // Given we have a default profile for the namespace + minimalCreateProfileInput := minimalCreateProfileInputTemplate + minimalCreateProfileInput.Namespace = namespace + + profile, err := s.BillingService.CreateProfile(ctx, minimalCreateProfileInput) + + require.NoError(s.T(), err) + require.NotNil(s.T(), profile) + + lines := ubpPendingLines{} + s.Run("create pending invoice items", func() { + // When we create pending invoice items + pendingLines, err := s.BillingService.CreateInvoiceLines(ctx, + billing.CreateInvoiceLinesInput{ + Namespace: namespace, + CustomerID: customerEntity.ID, + Lines: []billingentity.Line{ + { + LineBase: billingentity.LineBase{ + Period: billingentity.Period{Start: periodStart, End: periodEnd}, + InvoiceAt: periodEnd, + Currency: currencyx.Code(currency.USD), + Type: billingentity.InvoiceLineTypeManualUsageBased, + Name: "UBP - FLAT per unit", + }, + ManualUsageBased: billingentity.ManualUsageBasedLine{ + FeatureKey: features.flatPerUnit.Key, + Price: plan.NewPriceFrom(plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(100), + }), + }, + }, + { + LineBase: billingentity.LineBase{ + Period: billingentity.Period{Start: periodStart, End: periodEnd}, + InvoiceAt: periodEnd, + Currency: currencyx.Code(currency.USD), + Type: billingentity.InvoiceLineTypeManualUsageBased, + Name: "UBP - FLAT per any usage", + }, + ManualUsageBased: billingentity.ManualUsageBasedLine{ + FeatureKey: features.flatPerUsage.Key, + Price: plan.NewPriceFrom(plan.FlatPrice{ + Amount: alpacadecimal.NewFromFloat(100), + }), + }, + }, + { + LineBase: billingentity.LineBase{ + Period: billingentity.Period{Start: periodStart, End: periodEnd}, + InvoiceAt: periodEnd, + Currency: currencyx.Code(currency.USD), + Type: billingentity.InvoiceLineTypeManualUsageBased, + Name: "UBP - Tiered volume", + }, + ManualUsageBased: billingentity.ManualUsageBasedLine{ + FeatureKey: features.tieredVolume.Key, + Price: plan.NewPriceFrom(plan.TieredPrice{ + Mode: plan.VolumeTieredPrice, + Tiers: []plan.PriceTier{ + { + UpToAmount: lo.ToPtr(alpacadecimal.NewFromFloat(10)), + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(100), + }, + }, + { + UpToAmount: lo.ToPtr(alpacadecimal.NewFromFloat(20)), + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(90), + }, + }, + { + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(80), + }, + }, + }, + }), + }, + }, + { + LineBase: billingentity.LineBase{ + Period: billingentity.Period{Start: periodStart, End: periodEnd}, + InvoiceAt: periodEnd, + Currency: currencyx.Code(currency.USD), + Type: billingentity.InvoiceLineTypeManualUsageBased, + Name: "UBP - Tiered graduated", + }, + ManualUsageBased: billingentity.ManualUsageBasedLine{ + FeatureKey: features.tieredGraduated.Key, + Price: plan.NewPriceFrom(plan.TieredPrice{ + Mode: plan.GraduatedTieredPrice, + Tiers: []plan.PriceTier{ + { + UpToAmount: lo.ToPtr(alpacadecimal.NewFromFloat(10)), + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(100), + }, + }, + { + UpToAmount: lo.ToPtr(alpacadecimal.NewFromFloat(20)), + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(90), + }, + }, + { + UnitPrice: &plan.UnitPrice{ + Amount: alpacadecimal.NewFromFloat(80), + }, + }, + }, + }), + }, + }, + }, + }, + ) + require.NoError(s.T(), err) + require.Len(s.T(), pendingLines.Lines, 4) + + // The pending invoice items should be truncated to 1 min resolution (start => up to next, end down to previous) + for _, line := range pendingLines.Lines { + require.Equal(s.T(), + line.Period, + billingentity.Period{ + Start: lo.Must(time.Parse(time.RFC3339, "2024-09-02T12:13:00Z")), + End: lo.Must(time.Parse(time.RFC3339, "2024-09-03T12:13:00Z")), + }, + "period should be truncated to 1 min resolution", + ) + + require.Equal(s.T(), + line.InvoiceAt, + periodEnd, + "invoice at should be unchanged", + ) + } + + lines = ubpPendingLines{ + flatPerUnit: &pendingLines.Lines[0], + flatPerUsage: &pendingLines.Lines[1], + tieredVolume: &pendingLines.Lines[2], + tieredGraduated: &pendingLines.Lines[3], + } + }) + + s.Run("create invoice with empty truncated periods", func() { + asOf := periodStart.Add(time.Second) + _, err := s.BillingService.CreateInvoice(ctx, billing.CreateInvoiceInput{ + Customer: customerEntity.GetID(), + AsOf: &asOf, + }) + + require.ErrorIs(s.T(), err, billingentity.ErrInvoiceCreateNoLines) + require.ErrorAs(s.T(), err, &billingentity.ValidationError{}) + }) + + s.Run("create mid period invoice", func() { + asOf := periodStart.Add(time.Hour) + out, err := s.BillingService.CreateInvoice(ctx, billing.CreateInvoiceInput{ + Customer: customerEntity.GetID(), + AsOf: &asOf, + }) + + require.NoError(s.T(), err) + require.Len(s.T(), out, 1) + require.Len(s.T(), out[0].Lines, 3) + + // Let's resolve the lines by parent + flatPerUnit := s.lineWithParent(out[0].Lines, lines.flatPerUnit.ID) + flatPerUsage := s.lineWithParent(out[0].Lines, lines.flatPerUsage.ID) + tieredVolume := s.lineWithParent(out[0].Lines, lines.tieredVolume.ID) + + // The invoice should not have: + // - the graduated item as that must be invoiced in arreas + require.NotContains(s.T(), lo.Map(out[0].Lines, func(l billingentity.Line, _ int) string { + return l.ID + }), []string{ + flatPerUnit.ID, + flatPerUsage.ID, + tieredVolume.ID, + }) + + expectedPeriod := billingentity.Period{ + Start: periodStart.Truncate(time.Minute), + End: periodStart.Add(time.Hour).Truncate(time.Minute), + } + for _, line := range out[0].Lines { + require.True(s.T(), expectedPeriod.Equal(line.Period), "period should be changed for the line items") + } + + // Let's validate the output of the split itself + tieredVolumeChildren := s.getLineChildLines(ctx, namespace, lines.tieredVolume.ID) + require.True(s.T(), tieredVolumeChildren.ParentLine.Period.Equal(lines.tieredVolume.Period)) + require.Equal(s.T(), flatPerUnit.ManualUsageBased.Quantity.InexactFloat64(), float64(10), "flat per unit should have 10 units") + require.Equal(s.T(), billingentity.InvoiceLineStatusSplit, tieredVolumeChildren.ParentLine.Status, "parent should be split [id=%s]", tieredVolumeChildren.ParentLine.ID) + require.Len(s.T(), tieredVolumeChildren.ChildLines, 2, "there should be to child lines [id=%s]", tieredVolumeChildren.ParentLine.ID) + require.True(s.T(), tieredVolumeChildren.ChildLines[0].Period.Equal(billingentity.Period{ + Start: periodStart.Truncate(time.Minute), + End: periodStart.Add(time.Hour).Truncate(time.Minute), + }), "first child period should be truncated") + require.True(s.T(), tieredVolumeChildren.ChildLines[0].InvoiceAt.Equal(periodStart.Add(time.Hour).Truncate(time.Minute)), "first child should be issued at the end of parent's period") + require.True(s.T(), tieredVolumeChildren.ChildLines[1].Period.Equal(billingentity.Period{ + Start: periodStart.Add(time.Hour).Truncate(time.Minute), + End: periodEnd.Truncate(time.Minute), + }), "second child period should be until the end of parent's period") + }) + + s.Run("create mid period invoice - pt2", func() { + asOf := periodStart.Add(2 * time.Hour) + out, err := s.BillingService.CreateInvoice(ctx, billing.CreateInvoiceInput{ + Customer: customerEntity.GetID(), + AsOf: &asOf, + }) + + require.NoError(s.T(), err) + require.Len(s.T(), out, 1) + require.Len(s.T(), out[0].Lines, 3) + + // Let's resolve the lines by parent + flatPerUnit := s.lineWithParent(out[0].Lines, lines.flatPerUnit.ID) + flatPerUsage := s.lineWithParent(out[0].Lines, lines.flatPerUsage.ID) + tieredVolume := s.lineWithParent(out[0].Lines, lines.tieredVolume.ID) + + // The invoice should not have: + // - the graduated item as that must be invoiced in arreas + require.NotContains(s.T(), lo.Map(out[0].Lines, func(l billingentity.Line, _ int) string { + return l.ID + }), []string{ + flatPerUnit.ID, + flatPerUsage.ID, + tieredVolume.ID, + }) + + expectedPeriod := billingentity.Period{ + Start: periodStart.Add(time.Hour).Truncate(time.Minute), + End: periodStart.Add(2 * time.Hour).Truncate(time.Minute), + } + for _, line := range out[0].Lines { + require.True(s.T(), expectedPeriod.Equal(line.Period), "period should be changed for the line items") + } + + // Let's validate the output of the split itself + tieredVolumeChildren := s.getLineChildLines(ctx, namespace, lines.tieredVolume.ID) + require.True(s.T(), tieredVolumeChildren.ParentLine.Period.Equal(lines.tieredVolume.Period)) + require.Equal(s.T(), billingentity.InvoiceLineStatusSplit, tieredVolumeChildren.ParentLine.Status, "parent should be split [id=%s]", tieredVolumeChildren.ParentLine.ID) + require.Len(s.T(), tieredVolumeChildren.ChildLines, 3, "there should be to child lines [id=%s]", tieredVolumeChildren.ParentLine.ID) + require.True(s.T(), tieredVolumeChildren.ChildLines[0].Period.Equal(billingentity.Period{ + Start: periodStart.Truncate(time.Minute), + End: periodStart.Add(time.Hour).Truncate(time.Minute), + }), "first child period should be truncated") + require.True(s.T(), tieredVolumeChildren.ChildLines[1].Period.Equal(billingentity.Period{ + Start: periodStart.Add(time.Hour).Truncate(time.Minute), + End: periodStart.Add(2 * time.Hour).Truncate(time.Minute), + }), "second child period should be between the first and the third child's period") + require.True(s.T(), tieredVolumeChildren.ChildLines[1].InvoiceAt.Equal(periodStart.Add(2*time.Hour).Truncate(time.Minute)), "second child should be issued at the end of parent's period") + require.True(s.T(), tieredVolumeChildren.ChildLines[2].Period.Equal(billingentity.Period{ + Start: periodStart.Add(2 * time.Hour).Truncate(time.Minute), + End: periodEnd.Truncate(time.Minute), + }), "third child period should be until the end of parent's period") + }) + + s.Run("create end of period invoice", func() { + asOf := periodEnd + out, err := s.BillingService.CreateInvoice(ctx, billing.CreateInvoiceInput{ + Customer: customerEntity.GetID(), + AsOf: &asOf, + }) + + require.NoError(s.T(), err) + require.Len(s.T(), out, 1) + require.Len(s.T(), out[0].Lines, 4) + + // Let's resolve the lines by parent + flatPerUnit := s.lineWithParent(out[0].Lines, lines.flatPerUnit.ID) + flatPerUsage := s.lineWithParent(out[0].Lines, lines.flatPerUsage.ID) + tieredVolume := s.lineWithParent(out[0].Lines, lines.tieredVolume.ID) + tieredGraduated, tieredGraduatedFound := lo.Find(out[0].Lines, func(l billingentity.Line) bool { + return l.ID == lines.tieredGraduated.ID + }) + require.True(s.T(), tieredGraduatedFound, "tiered graduated line should be present") + require.Equal(s.T(), tieredGraduated.ID, lines.tieredGraduated.ID, "tiered graduated line should be the same (no split occurred)") + + require.NotContains(s.T(), lo.Map(out[0].Lines, func(l billingentity.Line, _ int) string { + return l.ID + }), []string{ + flatPerUnit.ID, + flatPerUsage.ID, + tieredVolume.ID, + lines.tieredGraduated.ID, + }) + + expectedPeriod := billingentity.Period{ + Start: periodStart.Add(2 * time.Hour).Truncate(time.Minute), + End: periodEnd.Truncate(time.Minute), + } + for _, line := range []billingentity.Line{flatPerUnit, flatPerUsage, tieredVolume} { + require.True(s.T(), expectedPeriod.Equal(line.Period), "period should be changed for the line items") + } + require.True(s.T(), tieredGraduated.Period.Equal(lines.tieredGraduated.Period), "period should be unchanged for the tiered graduated line") + + // Let's validate the output of the split itself: no new split should have occurred + tieredVolumeChildren := s.getLineChildLines(ctx, namespace, lines.tieredVolume.ID) + require.True(s.T(), tieredVolumeChildren.ParentLine.Period.Equal(lines.tieredVolume.Period)) + require.Equal(s.T(), billingentity.InvoiceLineStatusSplit, tieredVolumeChildren.ParentLine.Status, "parent should be split [id=%s]", tieredVolumeChildren.ParentLine.ID) + require.Len(s.T(), tieredVolumeChildren.ChildLines, 3, "there should be to child lines [id=%s]", tieredVolumeChildren.ParentLine.ID) + require.True(s.T(), tieredVolumeChildren.ChildLines[0].Period.Equal(billingentity.Period{ + Start: periodStart.Truncate(time.Minute), + End: periodStart.Add(time.Hour).Truncate(time.Minute), + }), "first child period should be truncated") + require.True(s.T(), tieredVolumeChildren.ChildLines[1].Period.Equal(billingentity.Period{ + Start: periodStart.Add(time.Hour).Truncate(time.Minute), + End: periodStart.Add(2 * time.Hour).Truncate(time.Minute), + }), "second child period should be between the first and the third child's period") + require.True(s.T(), tieredVolumeChildren.ChildLines[1].InvoiceAt.Equal(periodStart.Add(2*time.Hour).Truncate(time.Minute)), "second child should be issued at the end of parent's period") + require.True(s.T(), tieredVolumeChildren.ChildLines[2].Period.Equal(billingentity.Period{ + Start: periodStart.Add(2 * time.Hour).Truncate(time.Minute), + End: periodEnd.Truncate(time.Minute), + }), "third child period should be until the end of parent's period") + }) +} + +func (s *InvoicingTestSuite) lineWithParent(lines []billingentity.Line, parentID string) billingentity.Line { + for _, line := range lines { + if line.ParentLineID != nil && *line.ParentLineID == parentID { + return line + } + } + + require.Fail(s.T(), "line with parent not found") + return billingentity.Line{} +} + +type getChlildLinesResponse struct { + ParentLine billingentity.Line + ChildLines []billingentity.Line +} + +func (s *InvoicingTestSuite) getLineChildLines(ctx context.Context, ns string, parentID string) getChlildLinesResponse { + res, err := s.BillingAdapter.ListInvoiceLines(ctx, billing.ListInvoiceLinesAdapterInput{ + Namespace: ns, + ParentLineIDs: []string{parentID}, + ParentLineIDsIncludeParent: true, + }) + require.NoError(s.T(), err) + + if len(res) == 0 { + require.Fail(s.T(), "no child lines found") + } + + response := getChlildLinesResponse{} + + for _, line := range res { + if line.ID == parentID { + response.ParentLine = line + } else { + response.ChildLines = append(response.ChildLines, line) + } + } + + slices.SortFunc(response.ChildLines, func(a, b billingentity.Line) int { + switch { + case a.Period.Start.Equal(b.Period.Start): + return 0 + case a.Period.Start.Before(b.Period.Start): + return -1 + default: + return 1 + } + }) + + require.NotEmpty(s.T(), response.ParentLine.ID) + return response +} + +type ubpPendingLines struct { + flatPerUnit *billingentity.Line + flatPerUsage *billingentity.Line + tieredVolume *billingentity.Line + tieredGraduated *billingentity.Line +} + +type ubpFeatures struct { + flatPerUnit feature.Feature + flatPerUsage feature.Feature + tieredVolume feature.Feature + tieredGraduated feature.Feature +} diff --git a/test/billing/suite_test.go b/test/billing/suite_test.go index 09a92a750..778f816cc 100644 --- a/test/billing/suite_test.go +++ b/test/billing/suite_test.go @@ -22,6 +22,10 @@ import ( customeradapter "github.com/openmeterio/openmeter/openmeter/customer/adapter" customerservice "github.com/openmeterio/openmeter/openmeter/customer/service" "github.com/openmeterio/openmeter/openmeter/ent/db" + "github.com/openmeterio/openmeter/openmeter/meter" + featureadapter "github.com/openmeterio/openmeter/openmeter/productcatalog/adapter" + "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" + streamingtestutils "github.com/openmeterio/openmeter/openmeter/streaming/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/tools/migrate" ) @@ -35,6 +39,10 @@ type BaseSuite struct { BillingAdapter billing.Adapter BillingService billing.Service + FeatureService feature.FeatureConnector + MeterRepo *meter.InMemoryRepository + MockStreamingConnector *streamingtestutils.MockStreamingConnector + CustomerService customer.Service AppService app.Service @@ -59,6 +67,18 @@ func (s *BaseSuite) SetupSuite() { // setup invoicing stack + // Meter repo + + s.MeterRepo = meter.NewInMemoryRepository(nil) + s.MockStreamingConnector = streamingtestutils.NewMockStreamingConnector(t) + + // Feature + featureRepo := featureadapter.NewPostgresFeatureRepo(dbClient, slog.Default()) + + s.FeatureService = feature.NewFeatureConnector(featureRepo, s.MeterRepo) + + // Customer + customerAdapter, err := customeradapter.New(customeradapter.Config{ Client: dbClient, Logger: slog.Default(), @@ -104,11 +124,14 @@ func (s *BaseSuite) SetupSuite() { s.InvoiceCalculator = billingservice.NewMockableCalculator(t) billingService, err := billingservice.New(billingservice.Config{ - Adapter: billingAdapter, - CustomerService: s.CustomerService, - AppService: s.AppService, - Logger: slog.Default(), - InvoiceCalculator: s.InvoiceCalculator, + Adapter: billingAdapter, + CustomerService: s.CustomerService, + AppService: s.AppService, + Logger: slog.Default(), + InvoiceCalculator: s.InvoiceCalculator, + FeatureService: s.FeatureService, + MeterRepo: s.MeterRepo, + StreamingConnector: s.MockStreamingConnector, }) require.NoError(t, err) s.BillingService = billingService