From 8f9227f9fcbfefbd8b59c94aee20eeeadb082bf8 Mon Sep 17 00:00:00 2001 From: Ephemerish Date: Wed, 9 Sep 2026 18:17:21 +0800 Subject: [PATCH 1/2] blueapi's billing/v1/billing.proto now matches the round-2 spec --- billing/v1/billing.proto | 66 +++++++++++++++++++++++++++--- openapiv2/apidocs.swagger.json | 73 +++++++++++++++++++++++++--------- 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/billing/v1/billing.proto b/billing/v1/billing.proto index 92dbea30..441c2c5f 100644 --- a/billing/v1/billing.proto +++ b/billing/v1/billing.proto @@ -806,7 +806,9 @@ service Billing { }; } - // Validates or imports monthly FreeFormItems from CSV through invoice settings. + // Validates or imports monthly FreeFormItems from CSV through invoice settings. The CSV + // supplies each row's own billing month, billing group, and vendor; there is no + // request-level billing_month or vendor to scope the call. rpc BulkImportMonthlyMiscFees(BulkImportMonthlyMiscFeesRequest) returns (MonthlyMiscFeeImportResult) { option (google.api.http) = { post: "/v1/monthly-misc-fees:bulkImport" @@ -2888,16 +2890,28 @@ message GetMonthlyMiscFeeImportTemplateRequest { message MonthlyMiscFeeImportTemplate { string format = 1; string format_version = 2; + + // CSV header columns in required order. Has no "enabled" column: every item the + // workflow imports is saved as enabled. repeated string headers = 3; + repeated string sample_row = 4; + + // Supported values for BulkImportMonthlyMiscFeesRequest.mode: "merge" and "replace". + // Preview vs. apply is controlled separately by BulkImportMonthlyMiscFeesRequest.dry_run, + // not by this field. repeated string allowed_modes = 5; + int64 max_file_size_bytes = 6; int32 max_rows = 7; } message BulkImportMonthlyMiscFeesRequest { - // Required. Selected billing cycle in YYYY-MM form; YYYYMM is normalized. - string billing_month = 1 [(google.api.field_behavior) = REQUIRED]; + // Removed: a request no longer selects a billing month. Each CSV row supplies its own + // billing_month, billing_group_id, and vendor, so one upload can span any combination of + // billing groups, vendors, and invoice months. + reserved 1; + reserved "billing_month"; // Required. Uploaded CSV content as normalized UTF-8 text. string csv_content = 2 [(google.api.field_behavior) = REQUIRED]; @@ -2905,11 +2919,20 @@ message BulkImportMonthlyMiscFeesRequest { // Required. Original upload file name for audit and result display. string file_name = 3 [(google.api.field_behavior) = REQUIRED]; - // Required. "dry-run" validates only; "merge" upserts FreeFormItems by label. + // Required. "merge" upserts each row's item by label, leaving every other existing item for + // that billing group and vendor untouched. "replace" sets the complete additional-item list + // for each (billing_month, billing_group_id, vendor) combination the CSV names to exactly + // that combination's CSV rows, removing any existing item the CSV omits for it; a + // "replace" CSV must contain rows for exactly one billing_month, and the request is + // rejected before saving anything if it spans more than one. string mode = 4 [(google.api.field_behavior) = REQUIRED]; string format = 5 [(google.api.field_behavior) = REQUIRED]; string format_version = 6 [(google.api.field_behavior) = REQUIRED]; + + // Optional. When true, validates and returns the planned result for either mode without + // saving anything. + bool dry_run = 7; } message ListMonthlyMiscFeesRequest { @@ -2950,7 +2973,11 @@ message MonthlyMiscFee { message MonthlyMiscFeeImportResult { string status = 1; - string billing_month = 2; + + // Removed: a result can now span multiple billing months. See billing_months below. + reserved 2; + reserved "billing_month"; + string file_name = 3; int32 total_rows = 4; int32 valid_rows = 5; @@ -2959,11 +2986,36 @@ message MonthlyMiscFeeImportResult { int32 unchanged_rows = 8; int32 failed_rows = 9; repeated MonthlyMiscFeeImportRowResult row_results = 10; + + // Existing items removed because a "replace"-mode CSV omitted them from a combination it + // touched. Always 0 in "merge" mode. + int32 removed_rows = 11; + + // Every distinct billing month, in "YYYY-MM" form, a valid row in this file touches. + repeated string billing_months = 12; + + // Every distinct vendor a valid row in this file touches. + repeated string vendors = 13; + + // Every distinct billing group a valid row in this file touches. + repeated MonthlyMiscFeeImportGroupSummary billing_groups = 14; +} + +message MonthlyMiscFeeImportGroupSummary { + string billing_group_id = 1; + string billing_group_name = 2; } message MonthlyMiscFeeImportRowResult { + // The 1-based CSV row this result corresponds to. 0 when action is "remove", since a + // removal has no corresponding CSV row. int32 csv_row_number = 1; + + // One of "insert", "update", "unchanged", "invalid", or "remove". "remove" only appears in + // "replace" mode, for an existing item the CSV silently drops from a combination it + // otherwise touches. string action = 2; + string billing_group_id = 3; string billing_group_name = 4; string vendor = 5; @@ -2973,6 +3025,10 @@ message MonthlyMiscFeeImportRowResult { int64 amount = 9; bool enabled = 10; repeated MonthlyMiscFeeValidationError validation_errors = 11; + + // The row's own billing month in "YYYY-MM" form. Always populated, including for "remove" + // results, since a request can span multiple months. + string billing_month = 12; } message MonthlyMiscFeeValidationError { diff --git a/openapiv2/apidocs.swagger.json b/openapiv2/apidocs.swagger.json index 7210a7ec..c86e7780 100644 --- a/openapiv2/apidocs.swagger.json +++ b/openapiv2/apidocs.swagger.json @@ -12471,7 +12471,8 @@ }, "/v1/monthly-misc-fees:bulkImport": { "post": { - "summary": "Validates or imports monthly miscellaneous fees from CSV.", + "summary": "Validates or imports monthly FreeFormItems from CSV through invoice settings.", + "description": "The CSV supplies each row's own billing month, billing group, and vendor; there is no\nrequest-level billing_month or vendor to scope the call.", "operationId": "Billing_BulkImportMonthlyMiscFees", "responses": { "200": { @@ -36446,21 +36447,17 @@ "v1BulkImportMonthlyMiscFeesRequest": { "type": "object", "properties": { - "billingMonth": { - "type": "string", - "description": "Required. Selected billing cycle in YYYY-MM form; YYYYMM is accepted and normalized by the server." - }, "csvContent": { "type": "string", "description": "Required. Uploaded CSV content as normalized UTF-8 text." }, "fileName": { "type": "string", - "description": "Required. Original upload file name for audit metadata." + "description": "Required. Original upload file name for audit and result display." }, "mode": { "type": "string", - "description": "Required. One of \"dry-run\", \"replace-month\", or \"merge\". Empty defaults to \"dry-run\"." + "description": "Required. \"merge\" upserts each row's item by label, leaving every other existing item for\nthat billing group and vendor untouched. \"replace\" sets the complete additional-item list\nfor each (billing_month, billing_group_id, vendor) combination the CSV names to exactly\nthat combination's CSV rows, removing any existing item the CSV omits for it; a\n\"replace\" CSV must contain rows for exactly one billing_month, and the request is\nrejected before saving anything if it spans more than one." }, "format": { "type": "string", @@ -36470,13 +36467,12 @@ "type": "string", "description": "Required. Import format version. Currently \"misc_fee_import_v1\"." }, - "idempotencyKey": { - "type": "string", - "description": "Optional. Idempotency key for write-mode imports." + "dryRun": { + "type": "boolean", + "description": "Optional. When true, validates and returns the planned result for either mode without\nsaving anything." } }, "required": [ - "billingMonth", "csvContent", "fileName", "mode", @@ -44670,13 +44666,21 @@ } } }, - "v1MonthlyMiscFeeImportResult": { + "v1MonthlyMiscFeeImportGroupSummary": { "type": "object", "properties": { - "status": { + "billingGroupId": { "type": "string" }, - "billingMonth": { + "billingGroupName": { + "type": "string" + } + } + }, + "v1MonthlyMiscFeeImportResult": { + "type": "object", + "properties": { + "status": { "type": "string" }, "fileName": { @@ -44712,6 +44716,33 @@ "type": "object", "$ref": "#/definitions/v1MonthlyMiscFeeImportRowResult" } + }, + "removedRows": { + "type": "integer", + "format": "int32", + "description": "Existing items removed because a \"replace\"-mode CSV omitted them from a combination it\ntouched. Always 0 in \"merge\" mode." + }, + "billingMonths": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Every distinct billing month, in \"YYYY-MM\" form, a valid row in this file touches." + }, + "vendors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Every distinct vendor a valid row in this file touches." + }, + "billingGroups": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1MonthlyMiscFeeImportGroupSummary" + }, + "description": "Every distinct billing group a valid row in this file touches." } } }, @@ -44720,10 +44751,12 @@ "properties": { "csvRowNumber": { "type": "integer", - "format": "int32" + "format": "int32", + "description": "The 1-based CSV row this result corresponds to. 0 when action is \"remove\", since a\nremoval has no corresponding CSV row." }, "action": { - "type": "string" + "type": "string", + "description": "One of \"insert\", \"update\", \"unchanged\", \"invalid\", or \"remove\". \"remove\" only appears in\n\"replace\" mode, for an existing item the CSV silently drops from a combination it\notherwise touches." }, "billingGroupId": { "type": "string" @@ -44758,6 +44791,10 @@ "type": "object", "$ref": "#/definitions/v1MonthlyMiscFeeValidationError" } + }, + "billingMonth": { + "type": "string", + "description": "The row's own billing month in \"YYYY-MM\" form. Always populated, including for \"remove\"\nresults, since a request can span multiple months." } } }, @@ -44777,7 +44814,7 @@ "items": { "type": "string" }, - "description": "CSV header columns in required order." + "description": "CSV header columns in required order. Has no \"enabled\" column: every item the\nworkflow imports is saved as enabled." }, "sampleRow": { "type": "array", @@ -44791,7 +44828,7 @@ "items": { "type": "string" }, - "description": "Supported modes: \"dry-run\", \"replace-month\", and \"merge\"." + "description": "Supported values for BulkImportMonthlyMiscFeesRequest.mode: \"merge\" and \"replace\".\nPreview vs. apply is controlled separately by BulkImportMonthlyMiscFeesRequest.dry_run,\nnot by this field." }, "maxFileSizeBytes": { "type": "string", From 4bf95cd0ee2ff7f1fd383aa6eddf130e02ae4786 Mon Sep 17 00:00:00 2001 From: Ephemerish Date: Wed, 9 Sep 2026 18:21:48 +0800 Subject: [PATCH 2/2] completly remove month parameter --- billing/v1/billing.proto | 51 +++++++++++++++------------------- openapiv2/apidocs.swagger.json | 4 ++- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/billing/v1/billing.proto b/billing/v1/billing.proto index 441c2c5f..cee20dcc 100644 --- a/billing/v1/billing.proto +++ b/billing/v1/billing.proto @@ -2906,18 +2906,14 @@ message MonthlyMiscFeeImportTemplate { int32 max_rows = 7; } +// No billing_month or vendor field: each CSV row supplies its own, so one upload can span +// any combination of billing groups, vendors, and invoice months. message BulkImportMonthlyMiscFeesRequest { - // Removed: a request no longer selects a billing month. Each CSV row supplies its own - // billing_month, billing_group_id, and vendor, so one upload can span any combination of - // billing groups, vendors, and invoice months. - reserved 1; - reserved "billing_month"; - // Required. Uploaded CSV content as normalized UTF-8 text. - string csv_content = 2 [(google.api.field_behavior) = REQUIRED]; + string csv_content = 1 [(google.api.field_behavior) = REQUIRED]; // Required. Original upload file name for audit and result display. - string file_name = 3 [(google.api.field_behavior) = REQUIRED]; + string file_name = 2 [(google.api.field_behavior) = REQUIRED]; // Required. "merge" upserts each row's item by label, leaving every other existing item for // that billing group and vendor untouched. "replace" sets the complete additional-item list @@ -2925,14 +2921,14 @@ message BulkImportMonthlyMiscFeesRequest { // that combination's CSV rows, removing any existing item the CSV omits for it; a // "replace" CSV must contain rows for exactly one billing_month, and the request is // rejected before saving anything if it spans more than one. - string mode = 4 [(google.api.field_behavior) = REQUIRED]; + string mode = 3 [(google.api.field_behavior) = REQUIRED]; - string format = 5 [(google.api.field_behavior) = REQUIRED]; - string format_version = 6 [(google.api.field_behavior) = REQUIRED]; + string format = 4 [(google.api.field_behavior) = REQUIRED]; + string format_version = 5 [(google.api.field_behavior) = REQUIRED]; // Optional. When true, validates and returns the planned result for either mode without // saving anything. - bool dry_run = 7; + bool dry_run = 6; } message ListMonthlyMiscFeesRequest { @@ -2971,34 +2967,31 @@ message MonthlyMiscFee { string source = 11; } +// No singular billing_month field: a result can now span multiple billing months. See +// billing_months below. message MonthlyMiscFeeImportResult { string status = 1; - - // Removed: a result can now span multiple billing months. See billing_months below. - reserved 2; - reserved "billing_month"; - - string file_name = 3; - int32 total_rows = 4; - int32 valid_rows = 5; - int32 inserted_rows = 6; - int32 updated_rows = 7; - int32 unchanged_rows = 8; - int32 failed_rows = 9; - repeated MonthlyMiscFeeImportRowResult row_results = 10; + string file_name = 2; + int32 total_rows = 3; + int32 valid_rows = 4; + int32 inserted_rows = 5; + int32 updated_rows = 6; + int32 unchanged_rows = 7; + int32 failed_rows = 8; + repeated MonthlyMiscFeeImportRowResult row_results = 9; // Existing items removed because a "replace"-mode CSV omitted them from a combination it // touched. Always 0 in "merge" mode. - int32 removed_rows = 11; + int32 removed_rows = 10; // Every distinct billing month, in "YYYY-MM" form, a valid row in this file touches. - repeated string billing_months = 12; + repeated string billing_months = 11; // Every distinct vendor a valid row in this file touches. - repeated string vendors = 13; + repeated string vendors = 12; // Every distinct billing group a valid row in this file touches. - repeated MonthlyMiscFeeImportGroupSummary billing_groups = 14; + repeated MonthlyMiscFeeImportGroupSummary billing_groups = 13; } message MonthlyMiscFeeImportGroupSummary { diff --git a/openapiv2/apidocs.swagger.json b/openapiv2/apidocs.swagger.json index c86e7780..09c854c9 100644 --- a/openapiv2/apidocs.swagger.json +++ b/openapiv2/apidocs.swagger.json @@ -36472,6 +36472,7 @@ "description": "Optional. When true, validates and returns the planned result for either mode without\nsaving anything." } }, + "description": "No billing_month or vendor field: each CSV row supplies its own, so one upload can span\nany combination of billing groups, vendors, and invoice months.", "required": [ "csvContent", "fileName", @@ -44744,7 +44745,8 @@ }, "description": "Every distinct billing group a valid row in this file touches." } - } + }, + "description": "No singular billing_month field: a result can now span multiple billing months. See\nbilling_months below." }, "v1MonthlyMiscFeeImportRowResult": { "type": "object",