kh-vendor-ai-coding → staging · Generated 2026-06-22
This PR introduces the Accounting Columns settings section — a new
org-level catalog of Nitra-managed accounting columns that members can enable/disable, with
changes cascading into each member's customized display columns. It also migrates the
Rutter Custom Field config update endpoint to a batch (array) request shape to match the
new settings UX, and adds an internal auto-match-vendor step to the Rutter sync
flow that creates/links vendors when the AI vendor-coding confidence is low. Finally, it splits
transaction auto-vendor-matching onto its own autoMatchVendorOnTransaction
flag (the existing autoMatchVendor becomes bill-scoped), settable via the accounting
platform update endpoint.
Returns the fixed Nitra-managed accounting column catalog merged with the org's enable config, scoped to the columns available on the org's connected accounting platform.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organizationId | INTEGER | No | req.organization.id | The organization to read columns for. |
enabled | BOOLEAN | No | — | When set, returns only rows whose enabled flag matches (true or false). |
{
"status": 200,
"success": true,
"accountingColumns": [
{
"id": 1, // AccountingColumnConfigs row id, or null if no config row exists yet
"columnKey": "glaccount",
"name": "GL Account",
"enabled": true,
"required": true // platform "Always On" column; cannot be disabled
}
]
}
required: true
column is always enabled: true and must be rendered as locked/non-toggleable. When
id is null, no config row exists yet — send columnKey (not
id) to /update for that row. If the org has no connected platform, the full
catalog is returned (display only) with nothing forced on.
| Status | Code |
|---|---|
| 400 | BAD_REQUEST_INVALID_ARGUMENTS |
| 401 | UNAUTHORIZED |
| 500 | INTERNAL_SERVER_ERROR |
Batch-update the enabled status for accounting columns and cascade the change into each member's customized display column lists. All-or-nothing within a single transaction.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organizationId | INTEGER | No | req.organization.id | Must equal the caller's org or the request is rejected with FORBIDDEN. |
accountingColumns | ARRAY | Yes | — | Min 1 item. Each item is { id | columnKey, enabled }. |
accountingColumns[].id | INTEGER | Conditional | — | Existing AccountingColumnConfigs row id. Mutually exclusive with columnKey (exactly one required per item). |
accountingColumns[].columnKey | STRING | Conditional | — | Catalog column key (enum: ACCOUNTING_COLUMN_KEYS). Use when no config row exists yet. Mutually exclusive with id. |
accountingColumns[].enabled | BOOLEAN | Yes | — | Whether the column is enabled for the org. |
{
"status": 200,
"success": true
}
| Status | Code |
|---|---|
| 400 | BAD_REQUEST_INVALID_ARGUMENTS (incl. duplicate column keys in one batch) |
| 400 | ACCOUNTINGCOLUMNCONFIG_BAD_REQUEST_ACCOUNTING_COLUMN_CONFIG_DOES_NOT_EXIST |
| 400 | ACCOUNTINGCOLUMNCONFIG_BAD_REQUEST_COLUMN_NOT_AVAILABLE_ON_PLATFORM |
| 400 | ACCOUNTINGCOLUMNCONFIG_BAD_REQUEST_CANNOT_DISABLE_REQUIRED_COLUMN |
| 401 | UNAUTHORIZED |
| 403 | FORBIDDEN (org mismatch / row belongs to another org) |
| 500 | INTERNAL_SERVER_ERROR |
Update enabled status for Rutter custom fields for the organization — now accepts a batch array instead of a single field.
id / rutterCustomFieldDefinitionId / enabled fields removed.customFields array (min 1); each item is { id | rutterCustomFieldDefinitionId, enabled } with the same xor rule as before.rutterCustomFieldConfig object — it now returns only { status, success }.FORBIDDEN failures for missing platform/definition with specific 400 error codes (see Errors).{
"id": 12, // OR rutterCustomFieldDefinitionId (xor)
"rutterCustomFieldDefinitionId": 5,
"organizationId": 99,
"enabled": true
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
organizationId | INTEGER | No | req.organization.id | Must equal the caller's org or rejected with FORBIDDEN. |
customFields | ARRAY | Yes | — | Min 1 item. Each item is { id | rutterCustomFieldDefinitionId, enabled }. |
customFields[].id | INTEGER | Conditional | — | Existing RutterCustomFieldConfigs row id. Mutually exclusive with rutterCustomFieldDefinitionId. |
customFields[].rutterCustomFieldDefinitionId | INTEGER | Conditional | — | Definition id, used when no config row exists yet. Mutually exclusive with id. |
customFields[].enabled | BOOLEAN | Yes | — | Whether the field is enabled for the org. |
{
"status": 200,
"success": true
}
| Status | Code |
|---|---|
| 400 | BAD_REQUEST_INVALID_ARGUMENTS |
| 400 | RUTTERCUSTOMFIELDCONFIG_BAD_REQUEST_RUTTER_CUSTOM_FIELD_CONFIG_DOES_NOT_EXIST |
| 400 | RUTTERACCOUNTINGPLATFORM_BAD_REQUEST_RUTTER_ACCOUNTING_PLATFORM_DOES_NOT_EXIST |
| 400 | RUTTERCUSTOMFIELDDEFINITION_BAD_REQUEST_RUTTER_CUSTOM_FIELD_DEFINITION_DOES_NOT_EXIST |
| 403 | FORBIDDEN (org mismatch) |
Update accounting platform settings. Adds a transaction-specific auto-match-vendor toggle,
independent of the existing (now bill-scoped) autoMatchVendor flag.
autoMatchVendorOnTransaction (BOOLEAN, nullable) — gates auto-vendor-matching for transactions.autoMatchVendor is now documented as bill-scoped (no behavior change to the field itself; transactions no longer read it)..or() rule now also accepts autoMatchVendorOnTransaction as a sole field in the request.| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | INTEGER | Yes | — | The Rutter accounting platform id. |
defaultCardGLAccountId | STRING | No | — | Default card GL account id. Nullable (pass null to clear). |
defaultCardPaymentGLAccountId | STRING | No | — | Default card payment GL account id. Nullable. |
defaultCardCashbackGLAccountId | STRING | No | — | Default card cashback GL account id. Nullable. |
defaultCardChargeGLAccountId | STRING | No | — | Default card charge GL account id. Nullable. |
defaultReimbursementGLAccountId | STRING | No | — | Default reimbursement GL account id. Nullable. |
autoMatchVendor | BOOLEAN | No | — | Auto-match vendors when syncing bills. Nullable. |
autoMatchVendorOnTransaction | BOOLEAN | No | — | New. Auto-match vendors when syncing transactions. Nullable. |
autoMatchVendor, or autoMatchVendorOnTransaction) must be present alongside id.
{
"status": 200,
"success": true,
"platform": { "id": 1, "autoMatchVendor": true, "autoMatchVendorOnTransaction": true, ... }
}
| Status | Code |
|---|---|
| 400 | BAD_REQUEST_INVALID_ARGUMENTS |
| 400 | RUTTERACCOUNTINGPLATFORM_BAD_REQUEST_RUTTER_ACCOUNTING_PLATFORM_DOES_NOT_EXIST |
| 400 | RUTTERGENERALLEDGERACCOUNT_BAD_REQUEST_ACCOUNT_DOES_NOT_EXIST |
| 400 | RUTTERACCOUNTINGPLATFORM_BAD_REQUEST_CARD_PAYMENT_ACCOUNT_TYPE_MUST_NOT_BE_BANK |
| 400 | RUTTERACCOUNTINGPLATFORM_BAD_REQUEST_CARD_CASHBACK_ACCOUNT_TYPE_MUST_NOT_BE_BANK |
| 401 | UNAUTHORIZED |
| 403 | FORBIDDEN |
| 500 | INTERNAL_SERVER_ERROR |
New table AccountingColumnConfigs — one row per organization + column key.
organizationId — INTEGER, NOT NULL (FK to organization).columnKey — STRING, NOT NULL.enabled — BOOLEAN, NOT NULL, default true.createdAt / updatedAt / deletedAt (paranoid).
Surfaced to the FE via /accountingcolumnconfig/query as { id, columnKey, name, enabled, required }
(name and required are derived from the catalog/platform, not stored on the row).
New column on the existing RutterAccountingPlatforms table.
autoMatchVendorOnTransaction — BOOLEAN, nullable, default null. No backfill — all existing rows start null.
Surfaced to the FE via /rutteraccountingplatforms/read (the full platform object is serialized).
Settable via /rutteraccountingplatforms/update.
Auto-match-vendor step added to the Rutter sync flow. Request and response contracts are unchanged.
autoMatchVendorOnTransaction enabled and the line item is CARD_TRANSACTION / REFUND / NITRA_MART_TRANSACTION / NITRA_MART_REFUND, items whose AI vendor-coding confidence is LOW/VERY_LOW are auto-matched to a Rutter vendor by merchant name.autoMatchVendorOnTransaction flag instead of the shared autoMatchVendor. Orgs that had autoMatchVendor = true must enable the new flag to resume transaction auto-matching.failedAccountingItems for now); the matched/created vendor is assigned to every split item.failedAccountingItems array (e.g. QuickBooks Desktop vendor creation, or a Rutter create failure).
| File | Issue |
|---|---|
| None — all changed actions have JSDoc headers and Joi schemas that match. | |