Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 178 additions & 1 deletion billing/v1/billing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,18 @@ service Billing {
};
}

// Update invoice settings on existing billing groups in bulk from a CSV file.
// Unlike BulkCreateBillingGroup, a blank CSV cell preserves the billing group's existing
// value for that setting rather than clearing it, and a row referencing a billing group
// that does not exist fails only that row (no implicit create).
// Returns a long-running operation. Poll status via the Operations service (GET /ops/v1/{name}).
rpc BulkUpdateBillingGroup(BulkUpdateBillingGroupRequest) returns (protos.Operation) {
option (google.api.http) = {
post: "/v1/billinggroups:bulkUpdate"
body: "*"
};
}

// Create Exclude Service Entry
rpc CreateExcludeServiceEntry(CreateExcludeServiceEntryRequest) returns (CreateExcludeServiceEntryResponse) {
option (google.api.http) = {
Expand Down Expand Up @@ -3943,6 +3955,171 @@ message BulkCreateBillingGroupMetadata {

// Job end timestamp. Empty if still running.
string endTime = 8;

// Per-row normalized Invoice Settings results, populated for dryRun requests and for
// completed executions. Reused by BulkUpdateBillingGroupMetadata so Dry-run and execution
// for both Create and Update share one row-result shape.
repeated BulkBillingGroupInvoiceSettingsRow rows = 9;
}

// A single Invoice Settings field's normalized value together with where that value came from.
message NormalizedInvoiceSettingField {
// Canonical string-encoded normalized value. Empty if the setting has no value.
string value = 1;

// Where this value came from. One of: "uploaded", "existing", "default_derived",
// "org_derived", "connection_resolved". "existing" means the value was carried
// forward unchanged from the billing group's current stored state (the common case
// for a blank Update CSV cell — see BulkUpdateBillingGroup's merge semantics).
// Omitted (empty) if the server cannot determine a classification.
// A field the Bulk CSV schema does not accept as an input column (e.g. support_amount_target)
// must never be classified as "uploaded".
string source = 2;
}

// Server-normalized Invoice Settings for a single bulk row, as they will be (Dry-run) or were
// (execution) applied. Shared by BulkCreateBillingGroup and BulkUpdateBillingGroup results.
message NormalizedInvoiceSettings {
NormalizedInvoiceSettingField calcType = 1;
NormalizedInvoiceSettingField displayCost = 2;
NormalizedInvoiceSettingField supportAmountTarget = 3;
NormalizedInvoiceSettingField supportFeeCalcTarget = 4;
NormalizedInvoiceSettingField substitutionFeeCalcTarget = 5;
NormalizedInvoiceSettingField invoiceTemplate = 6;
NormalizedInvoiceSettingField customService = 7;
NormalizedInvoiceSettingField serviceDiscounts = 8;
// Complete per-vendor normalized invoice settings. These fields are additive;
// fields 1-8 above retain their original numbers.
NormalizedInvoiceSettingField currency = 9;
NormalizedInvoiceSettingField taxRate = 10;
NormalizedInvoiceSettingField discountRate = 11;
NormalizedInvoiceSettingField discountTargetUsage = 12;
NormalizedInvoiceSettingField discountCalcLogic = 13;
NormalizedInvoiceSettingField substitutionFee = 14;
NormalizedInvoiceSettingField substitutionRate = 15;
NormalizedInvoiceSettingField substitutionFix = 16;
NormalizedInvoiceSettingField substitutionFeeTargetUsage = 17;
NormalizedInvoiceSettingField substitutionFeeCalcType = 18;
NormalizedInvoiceSettingField supportFee = 19;
NormalizedInvoiceSettingField supportRate = 20;
NormalizedInvoiceSettingField supportFix = 21;
NormalizedInvoiceSettingField supportFeeAdjustment = 22;
}

// Per-row result for a bulk Create or Update Invoice Settings operation.
message BulkBillingGroupInvoiceSettingsRow {
// The canonical logical data-row index in the original CSV (zero-based,
// excluding the header when hasHeader is true). This index is preserved for
// valid rows, parse errors, and worker results; it is never compressed after
// a malformed or missing row.
int32 index = 1;

// The billing group ID. For Update, the resolved existing billing group. For Create,
// populated once known (empty for a row that has not yet been processed on a dryRun).
string billingGroupId = 2;

// Row status: "valid" (Dry-run row ready), "invalid" (parse/validation failure),
// "not_found" (referenced billing group does not exist), "updated" (execution
// succeeded), "failed" (row-level failure during execution). "created" is reserved
// for the Create side of this shared row-result shape.
string status = 3;

// Human-readable error message(s) for this row, if any.
repeated string errors = 4;

// The normalized Invoice Settings this row will produce (dryRun) or produced (execution).
NormalizedInvoiceSettings normalizedSettings = 5;

// Explicit row disposition. Values: "ready" (Dry-run row will update), "invalid"
// (parse/validation failure), "not_found" (referenced billing group does not exist),
// "connection_resolution_failed" (service-discount connection resolution failed),
// "unsupported_field" (a requested setting is not supported, e.g. non-blank
// custom_service), "updated" (execution succeeded), "failed" (any other row-level
// execution failure). It is separate from status so a terminal operation status
// cannot be mistaken for row success.
string disposition = 6;

// One-based physical CSV line number when known, including a header line.
// Zero means the line number was not available.
int32 csvLineNumber = 7;
}

message BulkUpdateBillingGroupRequest {
// Required. The cloud vendor for all billing groups in this CSV.
// Must be one of: aws, azure, gcp.
string vendor = 5;

// Required. The CSV file content as bytes.
bytes csvContent = 1;

// Optional. The delimiter used in the CSV file. Defaults to comma (,).
string delimiter = 2;

// Optional. Whether the first CSV line names the columns. When true, the first
// line is read as a header and columns are matched by name. When false OR omitted
// (proto3 scalar default), the CSV is interpreted using the canonical bulk-update
// column order (the same order as the downloadable template, headerless).
// Callers SHOULD set this field explicitly. Unlike BulkCreateBillingGroup — which
// always interprets the first CSV line as a header — Bulk Update honors this field
// as given, and the value is preserved on the async execution payload.
bool hasHeader = 3;

// Optional. If true, validate the CSV and resolve/merge against existing billing group
// state, returning the normalized result, without persisting any change.
bool dryRun = 4;
}

message BulkUpdateBillingGroupError {
// The canonical logical data-row index of the failed row (zero-based, excluding
// the header line when hasHeader is true). Same definition as
// BulkBillingGroupInvoiceSettingsRow.index: preserved for parse errors and worker
// results, never compressed after a malformed or missing row.
int32 index = 1;

// The billing group ID from the failed row, if present in the CSV.
string billingGroupId = 2;

// Error message.
string message = 3;

// Failure reason code: "not_found" (billing group does not exist),
// "validation_failed" (CSV parse or validation failure),
// "connection_resolution_failed" (service-discount connection resolution failed),
// or "unsupported_field" (a requested setting is not supported, e.g. non-blank
// custom_service).
string reason = 4;
}

// Metadata for the BulkUpdateBillingGroup long-running operation.
// Stored in protos.Operation.metadata.
message BulkUpdateBillingGroupMetadata {
// The current status: "pending", "processing", "completed", "partial_failed", "failed".
string status = 1;

// Total number of rows to process.
int32 totalCount = 2;

// Number of rows processed so far.
int32 processedCount = 3;

// Number of rows successfully updated.
int32 successCount = 4;

// Number of rows that failed (including not-found billing groups).
int32 failureCount = 5;

// Details of failed rows.
repeated BulkUpdateBillingGroupError errors = 6;

// Job start timestamp (RFC 3339).
string startTime = 7;

// Job end timestamp. Empty if still running.
string endTime = 8;

// Per-row normalized Invoice Settings results, populated for dryRun requests and for
// completed executions.
repeated BulkBillingGroupInvoiceSettingsRow rows = 9;
}

message ExcludeServiceEntry {
Expand Down Expand Up @@ -4262,4 +4439,4 @@ message ListInvoiceLayoutConfigBillingGroupsRequest {
message ListInvoiceLayoutConfigBillingGroupsResponse {
// The internal ids of the billing groups attached to this layout config.
repeated string billinggroup_ids = 1;
}
}
59 changes: 59 additions & 0 deletions openapiv2/apidocs.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -6080,6 +6080,39 @@
]
}
},
"/v1/billinggroups:bulkUpdate": {
"post": {
"summary": "Update invoice settings on existing billing groups in bulk from a CSV file.\nUnlike BulkCreateBillingGroup, a blank CSV cell preserves the billing group's existing\nvalue for that setting rather than clearing it, and a row referencing a billing group\nthat does not exist fails only that row (no implicit create).\nReturns a long-running operation. Poll status via the Operations service (GET /ops/v1/{name}).",
"operationId": "Billing_BulkUpdateBillingGroup",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/protosOperation"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1BulkUpdateBillingGroupRequest"
}
}
],
"tags": [
"Billing"
]
}
},
"/v1/billinggroups/billingconductor/{id}": {
"get": {
"summary": "WORK-IN-PROGRESS: Gets all Billing Groups in AWS Billing Conductor(ABC) for specific payer id.",
Expand Down Expand Up @@ -36420,6 +36453,32 @@
}
}
},
"v1BulkUpdateBillingGroupRequest": {
"type": "object",
"properties": {
"vendor": {
"type": "string",
"description": "Required. The cloud vendor for all billing groups in this CSV.\nMust be one of: aws, azure, gcp."
},
"csvContent": {
"type": "string",
"format": "byte",
"description": "Required. The CSV file content as bytes."
},
"delimiter": {
"type": "string",
"description": "Optional. The delimiter used in the CSV file. Defaults to comma (,)."
},
"hasHeader": {
"type": "boolean",
"description": "Optional. Whether the first CSV line names the columns. When true, the first\nline is read as a header and columns are matched by name. When false OR omitted\n(proto3 scalar default), the CSV is interpreted using the canonical bulk-update\ncolumn order (the same order as the downloadable template, headerless).\nCallers SHOULD set this field explicitly. Unlike BulkCreateBillingGroup — which\nalways interprets the first CSV line as a header — Bulk Update honors this field\nas given, and the value is preserved on the async execution payload."
},
"dryRun": {
"type": "boolean",
"description": "Optional. If true, validate the CSV and resolve/merge against existing billing group\nstate, returning the normalized result, without persisting any change."
}
}
},
"v1BulkImportMonthlyMiscFeesRequest": {
"type": "object",
"properties": {
Expand Down
Loading