e-arveldaja-mcp 0.25.6 → 0.25.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.claude/commands/reconcile-bank.md +2 -0
  2. package/CHANGELOG.md +28 -0
  3. package/CLAUDE.md +10 -0
  4. package/README.md +3 -2
  5. package/dist/api/transactions.api.d.ts +41 -0
  6. package/dist/api/transactions.api.d.ts.map +1 -1
  7. package/dist/api/transactions.api.js +110 -18
  8. package/dist/api/transactions.api.js.map +1 -1
  9. package/dist/banking/receipt-ledger-check.d.ts +122 -0
  10. package/dist/banking/receipt-ledger-check.d.ts.map +1 -0
  11. package/dist/banking/receipt-ledger-check.js +254 -0
  12. package/dist/banking/receipt-ledger-check.js.map +1 -0
  13. package/dist/banking/reconciliation/duplicate-policy.d.ts +19 -1
  14. package/dist/banking/reconciliation/duplicate-policy.d.ts.map +1 -1
  15. package/dist/banking/reconciliation/duplicate-policy.js +59 -2
  16. package/dist/banking/reconciliation/duplicate-policy.js.map +1 -1
  17. package/dist/banking/reconciliation/executor.d.ts.map +1 -1
  18. package/dist/banking/reconciliation/executor.js +132 -7
  19. package/dist/banking/reconciliation/executor.js.map +1 -1
  20. package/dist/banking/reconciliation/presenter.d.ts +6 -1
  21. package/dist/banking/reconciliation/presenter.d.ts.map +1 -1
  22. package/dist/banking/reconciliation/presenter.js +61 -4
  23. package/dist/banking/reconciliation/presenter.js.map +1 -1
  24. package/dist/banking/reconciliation/projection.d.ts.map +1 -1
  25. package/dist/banking/reconciliation/projection.js +11 -0
  26. package/dist/banking/reconciliation/projection.js.map +1 -1
  27. package/dist/banking/reconciliation/types.d.ts +50 -0
  28. package/dist/banking/reconciliation/types.d.ts.map +1 -1
  29. package/dist/connection-safety.d.ts +27 -0
  30. package/dist/connection-safety.d.ts.map +1 -1
  31. package/dist/connection-safety.js +50 -0
  32. package/dist/connection-safety.js.map +1 -1
  33. package/dist/guided/run-accounting-report.d.ts.map +1 -1
  34. package/dist/guided/run-accounting-report.js +23 -2
  35. package/dist/guided/run-accounting-report.js.map +1 -1
  36. package/dist/receipts/classification-operations.d.ts.map +1 -1
  37. package/dist/receipts/classification-operations.js +15 -2
  38. package/dist/receipts/classification-operations.js.map +1 -1
  39. package/dist/reporting/operations.d.ts.map +1 -1
  40. package/dist/reporting/operations.js +49 -0
  41. package/dist/reporting/operations.js.map +1 -1
  42. package/dist/reporting/receipt-client-alignment.d.ts +65 -0
  43. package/dist/reporting/receipt-client-alignment.d.ts.map +1 -0
  44. package/dist/reporting/receipt-client-alignment.js +113 -0
  45. package/dist/reporting/receipt-client-alignment.js.map +1 -0
  46. package/dist/reporting/types.d.ts +17 -2
  47. package/dist/reporting/types.d.ts.map +1 -1
  48. package/dist/runtime/connection-manager.d.ts +1 -1
  49. package/dist/runtime/connection-manager.d.ts.map +1 -1
  50. package/dist/runtime/connection-manager.js +2 -2
  51. package/dist/runtime/connection-manager.js.map +1 -1
  52. package/dist/server/create-server.d.ts.map +1 -1
  53. package/dist/server/create-server.js +46 -7
  54. package/dist/server/create-server.js.map +1 -1
  55. package/dist/server/server-instructions.js +1 -1
  56. package/dist/tools/bank-reconciliation.js +3 -3
  57. package/dist/tools/bank-reconciliation.js.map +1 -1
  58. package/dist/tools/crud/purchase-invoices.d.ts.map +1 -1
  59. package/dist/tools/crud/purchase-invoices.js +10 -1
  60. package/dist/tools/crud/purchase-invoices.js.map +1 -1
  61. package/dist/tools/crud/transactions.d.ts.map +1 -1
  62. package/dist/tools/crud/transactions.js +93 -5
  63. package/dist/tools/crud/transactions.js.map +1 -1
  64. package/dist/tools/purchase-vat-defaults.d.ts +7 -0
  65. package/dist/tools/purchase-vat-defaults.d.ts.map +1 -1
  66. package/dist/tools/purchase-vat-defaults.js +13 -4
  67. package/dist/tools/purchase-vat-defaults.js.map +1 -1
  68. package/dist/tools/receipt-inbox-booking.d.ts.map +1 -1
  69. package/dist/tools/receipt-inbox-booking.js +15 -2
  70. package/dist/tools/receipt-inbox-booking.js.map +1 -1
  71. package/dist/types/api.d.ts +7 -0
  72. package/dist/types/api.d.ts.map +1 -1
  73. package/package.json +6 -6
  74. package/server.json +9 -2
  75. package/workflows/reconcile-bank.md +2 -0
@@ -110,6 +110,8 @@ Only do this when a `distribution` key is present.
110
110
  - JSON strings are legacy compatibility only; prefer passing the top-level array directly.
111
111
  - Only confirm one explicitly approved match at a time; do not auto-confirm ambiguous transactions.
112
112
  - When `result.matches` shows two or more candidates tied at the same top confidence for one transaction, skip auto-confirmation and ask the user which candidate is correct, mirroring the inter-account ambiguity handling.
113
+ - **Third-party payer.** When a match carries `manual_review_required`, or the exact-confirm plan lists it under `third_party_payer_reviews`, the transaction's client (the bank payer) differs from the invoice's client. e-arveldaja books the receipt under the transaction's client, so a plain confirm would leave the invoice client's receivable open. Show both clients and ask the user whether this payment settles that invoice; if yes, call `confirm_transaction` with the same `distributions` and `reassign_client_to_invoice: true` (the bank payer name is kept). A confirm without that flag is refused with `linked_invoice_client_mismatch`; never work around it by editing the invoice's client.
114
+ - After any invoice-linked confirm, read the tool's `ledger_check`/`warnings`: a `ledger_client_mismatch` or `registration_journal_not_found` result means the transaction IS confirmed but booked wrongly; follow its `next_action` (invalidate, then confirm again with `reassign_client_to_invoice: true`). `run_accounting_report` with `report: "receipt_client_alignment"` lists all existing mismatches.
113
115
 
114
116
  ### Single transaction mode
115
117
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.25.7] - 2026-09-18
6
+
7
+ ### Fixed
8
+
9
+ - **`update_purchase_invoice` no longer 500s when items omit `cl_fringe_benefits_id`** ([#62](https://github.com/iseppo/e-arveldaja-mcp/issues/62)). `create_purchase_invoice` defaulted the field to 1 (no fringe benefit) but `update_purchase_invoice` sent items through verbatim, so the very payload that created an invoice failed on update with a raw `null value in column "cl_fringe_benefits_id" ... violates not-null constraint` from the API. The API GET also omits the field, so even header-only updates (which re-send the existing lines) were exposed. Update now applies the same structural item defaults as create (`cl_fringe_benefits_id` → 1, `amount` → 1, explicit values kept) to caller-supplied and re-sent items alike.
10
+
11
+ ### Changed
12
+
13
+ - **Dependencies updated to current.** `@llamaindex/liteparse` 2.14.3 → 2.14.6, `zod` 4.5.4 → 4.6.5, plus dev tooling (`@babel/parser` 8.0.5, `@types/node` 26.6.1, `vite` 8.3.0). Declared floors raised to the tested versions per the dependency-floor policy. The eight `testdata/tool-surface/*.json` contract pins are byte-identical under zod 4.6, so no tool schema changed. `vitest` stays on 4.1.11: vitest 5 requires Node ≥ 22.12 while CI still runs the unit tests on Node 20. `@modelcontextprotocol/sdk` 1.30.0 is still the latest release.
14
+
15
+ ### Security
16
+
17
+ - **Cleared a moderate advisory in a transitive dependency.** `hono` below 4.13.5 (reached only through `@modelcontextprotocol/sdk`'s HTTP transport, which this stdio-only server never loads) had a `toSSG()` path-escape, a `parseBody()` nesting DoS and a query-parser fragment issue (GHSA-gqvv-2mrq-wpjv, GHSA-g6gw-c38x-mqfc, GHSA-crvj-82cr-hjcx). The locked tree now resolves `hono` 4.13.8; `npm audit` reports zero known vulnerabilities.
18
+
19
+ ### Added
20
+
21
+ - **Third-party payer protection for invoice receipts.** e-arveldaja copies the registration journal's client from the *bank transaction's* client (the payer), not from the linked invoice, and postings carry no client of their own — so when someone else pays an invoice (verified live: a ministry paying a foundation's invoice), the invoice is marked paid but the receivable credit lands in the payer's client sub-ledger and the invoice client keeps showing the debt. Four defences, no new tools:
22
+ - **`TransactionsApi.confirm` refuses a mismatched confirm.** When the transaction already has a client and every invoice in the distribution belongs to one *different* client, the confirm is rejected before the register call with a structured `linked_invoice_client_mismatch` error (transaction and invoice client ids, `next_action`). The existing auto-fix for a *missing* client is unchanged.
23
+ - **`confirm_transaction` gains `reassign_client_to_invoice`** (default false): the explicit, visible approval to replace the payer client on the transaction with the invoice's client before confirming. `bank_account_name` (the real payer) is never touched; the audit entry records `clients_id_before`/`clients_id_after`; the change is rolled back if the register call fails.
24
+ - **Post-confirm ledger check.** After any invoice-linked confirm the registration journal is located (`operation_type = TRANSACTION`, `operations_id = transaction id` — the register call returns no journal id) and checked: exactly one journal, its client equals the invoice client, the bank leg and the receivable/payable leg carry the transaction amount on the accounts taken from the records. A failure is reported as `ledger_client_mismatch` / `ledger_posting_mismatch` / `registration_journal_not_found` with `mutation_occurred: true` and the repair steps (invalidate, then confirm again with `reassign_client_to_invoice`); the confirm itself is never silently reported as a clean success. `reconcile_bank_transactions` exact-confirm runs the same check once for the whole batch and reports `ledger_checks`.
25
+ - **Exact-match reconciliation routes third-party payers to review.** A unique exact match whose transaction client differs from the invoice client (or whose invoice has no client) is no longer auto-confirmed; it becomes a `third_party_payer_reviews` row in the plan (with `transaction_clients_id`, `invoice_clients_id` and the exact repair command), the invoice stays reserved, and the plan fingerprint covers the review set so a changed set is plan drift. `suggest` rows flag the same case with `manual_review_required`.
26
+ - **`run_accounting_report` mode `receipt_client_alignment`** (read-only): scans confirmed invoice-linked transactions, reports every payer/invoice client mismatch with the registration journal id, both clients and the literal repair steps, and separately lists confirmed receipts whose registration journal cannot be found. Nothing is mutated.
27
+ A one-client-per-journal reclassification entry (Dr 1210 payer / Cr 1210 invoice client) is not expressible in e-arveldaja, so the repair path is invalidate → reassign → re-confirm, which stays traceable.
28
+
29
+ - **Wrong-company write protection for multi-connection servers** ([#61](https://github.com/iseppo/e-arveldaja-mcp/issues/61)). The active connection lives only in the server process, so an MCP host that respawns the server (crash, idle timeout, reconnect) silently starts it back on the default connection — and a write intended for company B lands in company A's books. Two defences:
30
+ - **`EARVELDAJA_DEFAULT_CONNECTION`** (index or exact connection name) pins which connection a fresh process starts on; an unknown value fails startup instead of falling back to index 0. The startup log line now also names the active connection.
31
+ - **Optional `connection` argument on every non-readonly tool** when more than one connection is configured (index, numeric string, or name as shown by `list_connections`). A call whose `connection` does not name the active connection is refused with a structured `connection_mismatch` error *before any API request*, so callers can assert the target company on every write instead of trusting session state. Single-connection servers keep their schemas unchanged (the tool-surface contract pins are untouched); `switch_connection` and read-only tools never carry the argument.
32
+
5
33
  ## [0.25.6] - 2026-09-03
6
34
 
7
35
  ### Fixed
package/CLAUDE.md CHANGED
@@ -36,6 +36,11 @@ Set `EARVELDAJA_SERVER=demo` for the demo API (default: `live`).
36
36
  Place multiple `apikey*.txt` files (e.g. `apikey.txt`, `apikey (1).txt`) next to the project.
37
37
  Use `list_connections` to see all available accounts and `switch_connection` to switch between them.
38
38
  Switching clears all cached data to prevent cross-company data leaks.
39
+ The active connection is in-process state: a server respawned by the MCP host starts on
40
+ `EARVELDAJA_DEFAULT_CONNECTION` (index or name; default 0, unknown values fail startup).
41
+ With 2+ connections every non-readonly tool gains an optional `connection` argument
42
+ (index or name); a mismatch with the active connection is refused (`connection_mismatch`)
43
+ before any API request. Pass it on every write in multi-company sessions (GitHub #61).
39
44
 
40
45
  **NEVER commit `.env` or `apikey.txt` to git.** The `.gitignore` is configured to exclude them.
41
46
 
@@ -118,6 +123,10 @@ The guided surface is sized for routing clarity, not token cost (per-tool measur
118
123
  (→ `continue_accounting_workflow`). Default: hidden — the merged tools keep
119
124
  routing to the same handlers internally, so no functionality is lost.
120
125
  `reconcile_inter_account_transfers` is never gated (no merged execute mode).
126
+ - **`EARVELDAJA_DEFAULT_CONNECTION=<index|name>`** — connection a freshly started
127
+ server activates (default: index 0). An unknown index/name fails startup rather
128
+ than silently using index 0. Pair with the per-call `connection` guard argument
129
+ that multi-connection servers add to non-readonly tools.
121
130
  - **`EARVELDAJA_EXPOSE_SETUP_TOOLS=1`** — also register the credential-management
122
131
  tools (`import_apikey_credentials`, `list_stored_credentials`,
123
132
  `remove_stored_credentials`) when the server already has configured
@@ -508,6 +517,7 @@ API data remains raw.
508
517
  - **ÄS § 157 legality checks are split by clause**: the **lg 1 retained-earnings ceiling is NET-based** — the ENTIRE retained-earnings balance is distributable as net dividend, since the CIT is not part of the payout; the **lg 2 net-assets floor is GROSS-based** (net assets fall by dividend payable + tax liability) at share capital **+ non-distributable reserves** (reservkapital, name-detected in the chart, standard 2940; override with `restricted_reserve_accounts`). The tool hard-blocks violations of either clause unless `force=true` (use only alongside a legitimate action such as a capital reduction), and always reports `maximum_distributable.max_net_dividend` = min(retained earnings, (net assets − floor) × den/(den+num)).
509
518
  - **Dividend statutory prerequisites** are echoed as `compliance_notes` on every path: ÄS § 157 lg 1 requires an APPROVED annual report + profit-distribution decision (attach the decision via `attach_document`), and the CIT is declared on TSD annex 7 by the 10th of the month after payout (TuMS § 54).
510
519
  - **Capital gains**: Securities taxed at 22% income tax
520
+ - **Third-party payer / receipt client alignment**: e-arveldaja copies a registration journal's `clients_id` from the bank transaction (payer), not from the linked invoice, and postings carry no client — so a payer that differs from the invoice client books the receivable credit into the wrong client sub-ledger (verified live 2026-09). `TransactionsApi.confirm` refuses such a confirm (`linked_invoice_client_mismatch`) unless `reassignClientToInvoice` (tool param `reassign_client_to_invoice`, an explicit approval) is set; the registration journal is located by `operation_type = "TRANSACTION"` + `operations_id = tx id` (the register call returns no journal id) and checked after every invoice-linked confirm (`src/banking/receipt-ledger-check.ts`); exact-match reconciliation routes these to `third_party_payer_reviews`; `run_accounting_report` `receipt_client_alignment` audits existing books. Repair is invalidate → reassign → re-confirm; a one-journal reclassification between two clients is not expressible.
511
521
  - **Input-VAT deduction restrictions** (`src/estonian-tax-rules.ts`): single date-gated source for the standard VAT-rate timeline (20→22%→24%), the dividend-CIT timeline (20/80→22/78), the 40 000 EUR VAT registration threshold, current reduced rates, and the deduction detectors. `suggest_booking` returns these as `tax_notes` — `KMS § 30` (külaliste vastuvõtt / esinduskulu: input VAT non-deductible; also `TuMS § 49 lg 4` representation limit 50 €/month + 2% of payroll) and `KMS § 30 lg 4` (M1 passenger car: 50% cap). Notes are advisory; `workflows/book-invoice.md` requires surfacing each on the approval card. `validate_invoice_data` also uses the rate timeline to warn when a line's standard rate (20/22/24%) does not match the invoice date. The full dataset is browsable read-only at `earveldaja://tax_rules` — including the `TuMS § 49` representation/donation limits, `TuMS § 48` fringe-benefit pointer, the profit-distribution rules (`ÄS § 157 lg 1–2`, `TuMS § 50`), and the RPS process rules (`§ 10` corrections, `§ 12` 7-year retention, `§ 15` inventory) — plus a `verified_at` stamp (`TAX_RULES_VERIFIED_AT`). `check_tax_free_limits` computes the cumulative `TuMS § 49` representation/donation limits and the 22/78 tax on any excess from caller-supplied year-to-date figures (it does not read the ledger). Update the figures in this module when the law changes — the Estonian-tax tool descriptions render their rates/thresholds from these constants, so they follow automatically (pinned by tests).
512
522
 
513
523
  **Non-goals — do NOT reimplement in MCP (e-arveldaja does these natively; reimplementing risks double-booking):**
package/README.md CHANGED
@@ -93,7 +93,7 @@ You can also import manually at any time by asking your AI assistant:
93
93
 
94
94
  > "Import my API key from apikey.txt"
95
95
 
96
- For multiple companies, place multiple files (`apikey.txt`, `apikey-company2.txt`, etc.) and use `list_connections` / `switch_connection` to switch between them.
96
+ For multiple companies, place multiple files (`apikey.txt`, `apikey-company2.txt`, etc.) and use `list_connections` / `switch_connection` to switch between them. Set `EARVELDAJA_DEFAULT_CONNECTION=<index or name>` to choose which company a freshly started server uses (default: index 0), and pass the optional `connection` argument on writes to have the server refuse any call aimed at a company that is not active.
97
97
 
98
98
  ### 3. Optional: define company-specific accounting rules
99
99
 
@@ -372,7 +372,8 @@ curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.ise
372
372
  - **Large datasets need date filters.** The server loads up to 200 pages of data per query. Companies with thousands of invoices or transactions should narrow reporting and reconciliation tools with date ranges — otherwise the tool will ask you to.
373
373
  - **Caching.** API responses are cached for 2–5 minutes and reference data for up to 10 minutes. The server automatically invalidates caches when you create, update, or delete records through MCP tools. Changes made directly in the e-arveldaja web UI are not visible until the cache expires; call `clear_cache` or pass `fresh: true` to balance/reporting tools when you need the next read to fetch current upstream data.
374
374
  - **EUR by default.** All amounts are EUR unless a different currency is specified.
375
- - **Multi-company.** Place multiple `apikey*.txt` files and use `list_connections` / `switch_connection`. Switching clears the previous and target connections' cached data, so one company's records are never served to another.
375
+ - **Third-party payers.** e-arveldaja books a bank receipt under the *transaction's* client (the payer), so when someone else pays an invoice the receivable credit would land in the wrong client sub-ledger. `confirm_transaction` refuses such a confirm (`linked_invoice_client_mismatch`) unless you pass `reassign_client_to_invoice: true`, checks the resulting registration journal after every invoice-linked confirm, and `reconcile_bank_transactions` lists these cases as `third_party_payer_reviews` instead of auto-confirming them. `run_accounting_report` with `report: "receipt_client_alignment"` audits existing books.
376
+ - **Multi-company.** Place multiple `apikey*.txt` files and use `list_connections` / `switch_connection`. Switching clears the previous and target connections' cached data, so one company's records are never served to another. The active connection is per server process: if your MCP host restarts the server, it comes back on `EARVELDAJA_DEFAULT_CONNECTION` (index or name; default index 0, unknown values fail startup). With several connections configured, every non-readonly tool accepts an optional `connection` argument (index or name); a call whose value is not the active connection is refused with `connection_mismatch` before any API request, so you can pin the target company on each write instead of relying on an earlier `switch_connection`.
376
377
  - **Node.js 18+** required.
377
378
  - **File access scope.** By default, file-reading tools can access supported files under the working directory and `/tmp`. Set `EARVELDAJA_ALLOWED_PATHS` (colon-separated) to allow additional directories, or `EARVELDAJA_ALLOW_HOME=true` to allow the entire home directory.
378
379
  - **Human-editable local accounting rules.** `accounting-rules.md` lets you store company-specific booking defaults and annual-report overrides in Markdown instead of code or JSON.
@@ -4,11 +4,45 @@ import type { Transaction, TransactionDistribution, ApiResponse } from "../types
4
4
  import type { CreateBankTransactionPayload, UpdateBankTransactionRequest } from "../types/mutations.js";
5
5
  import { BaseResource } from "./base-resource.js";
6
6
  export declare function getNormalizedNetworkCause(error: unknown): HttpError | undefined;
7
+ /**
8
+ * The payer on the bank transaction is not the client on the linked invoice.
9
+ *
10
+ * `journal.clients_id` is copied from `transaction.clients_id`, and postings
11
+ * carry no client of their own, so registering this distribution would book the
12
+ * receivable/payable leg into the payer's sub-ledger while the invoice stays
13
+ * open in the invoice client's. Thrown before the register call — nothing is
14
+ * mutated.
15
+ */
16
+ export declare class LinkedInvoiceClientMismatchError extends Error {
17
+ readonly category = "linked_invoice_client_mismatch";
18
+ readonly transaction_id: number;
19
+ readonly transaction_clients_id: number;
20
+ readonly invoice_table: string;
21
+ readonly invoice_id: number;
22
+ readonly invoice_clients_id: number;
23
+ readonly next_action: string;
24
+ constructor(details: {
25
+ transactionId: number;
26
+ transactionClientsId: number;
27
+ invoiceTable: string;
28
+ invoiceId: number;
29
+ invoiceClientsId: number;
30
+ });
31
+ }
7
32
  export declare class TransactionsApi extends BaseResource<Transaction> {
8
33
  constructor(client: HttpClient);
9
34
  invalidateTransactionsAfterAmbiguousCleanup(): void;
10
35
  create(data: CreateBankTransactionPayload): Promise<ApiResponse>;
11
36
  update(id: number, data: UpdateBankTransactionRequest): Promise<ApiResponse>;
37
+ /**
38
+ * Resolve the single client shared by every invoice in a distribution.
39
+ *
40
+ * Returns `undefined` when there is no invoice row, when an invoice carries no
41
+ * client (it cannot pin the journal's sub-ledger), or when the invoices
42
+ * disagree — one journal has one client, so a split-client distribution has no
43
+ * satisfiable expectation and must not be blocked.
44
+ */
45
+ private resolveLinkedInvoiceClient;
12
46
  /**
13
47
  * Confirm a transaction with distribution rows.
14
48
  * If the transaction has no clients_id (common for card payments), automatically
@@ -20,9 +54,16 @@ export declare class TransactionsApi extends BaseResource<Transaction> {
20
54
  * client fix. The plan-bound reconciliation executor uses this so the client
21
55
  * update is booked as its own reviewed, enumerated command instead of a hidden
22
56
  * side effect of confirmation.
57
+ *
58
+ * When the transaction ALREADY has a client and it differs from the linked
59
+ * invoice's, this throws `LinkedInvoiceClientMismatchError` before registering
60
+ * (see that class for why). Pass `{ reassignClientToInvoice: true }` — the
61
+ * caller's explicit approval — to move the transaction to the invoice's client
62
+ * first instead; that update is rolled back if the register call then fails.
23
63
  */
24
64
  confirm(id: number, distributions?: TransactionDistribution[], options?: {
25
65
  autoFixClientsId?: boolean;
66
+ reassignClientToInvoice?: boolean;
26
67
  }): Promise<ApiResponse>;
27
68
  invalidate(id: number): Promise<ApiResponse>;
28
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transactions.api.d.ts","sourceRoot":"","sources":["../../src/api/transactions.api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAgC,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACvH,OAAO,KAAK,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAExG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOlD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAmB/E;AAED,qBAAa,eAAgB,SAAQ,YAAY,CAAC,WAAW,CAAC;IAC5D,YAAY,MAAM,EAAE,UAAU,EAE7B;IAEM,2CAA2C,IAAI,IAAI,CAEzD;IAOc,MAAM,CAAC,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC,CAE9E;IAEc,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC,CAE1F;IAED;;;;;;;;;;;OAWG;IACG,OAAO,CACX,EAAE,EAAE,MAAM,EACV,aAAa,CAAC,EAAE,uBAAuB,EAAE,EACzC,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAE,GACvC,OAAO,CAAC,WAAW,CAAC,CA4HtB;IAEK,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAOjD;CAEF"}
1
+ {"version":3,"file":"transactions.api.d.ts","sourceRoot":"","sources":["../../src/api/transactions.api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAgC,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACvH,OAAO,KAAK,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAExG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOlD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAmB/E;AAOD;;;;;;;;GAQG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,QAAQ,oCAAoC;IACrD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,WAAW,SAA8C;IAElE,YAAY,OAAO,EAAE;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,EAYA;CACF;AAQD,qBAAa,eAAgB,SAAQ,YAAY,CAAC,WAAW,CAAC;IAC5D,YAAY,MAAM,EAAE,UAAU,EAE7B;IAEM,2CAA2C,IAAI,IAAI,CAEzD;IAOc,MAAM,CAAC,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC,CAE9E;IAEc,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC,CAE1F;IAED;;;;;;;OAOG;YACW,0BAA0B;IAoBxC;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CACX,EAAE,EAAE,MAAM,EACV,aAAa,CAAC,EAAE,uBAAuB,EAAE,EACzC,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAAC,uBAAuB,CAAC,EAAE,OAAO,CAAA;KAAE,GAC1E,OAAO,CAAC,WAAW,CAAC,CAmJtB;IAEK,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAOjD;CAEF"}
@@ -26,6 +26,38 @@ export function getNormalizedNetworkCause(error) {
26
26
  return undefined;
27
27
  }
28
28
  }
29
+ const LINKED_INVOICE_CLIENT_MISMATCH_NEXT_ACTION = "Re-run confirm_transaction with reassign_client_to_invoice: true to book the receipt under the invoice's " +
30
+ "client, or fix the linked invoice; the journal client comes from the transaction's client and would land " +
31
+ "the 1210/2310 entry in the wrong sub-ledger.";
32
+ /**
33
+ * The payer on the bank transaction is not the client on the linked invoice.
34
+ *
35
+ * `journal.clients_id` is copied from `transaction.clients_id`, and postings
36
+ * carry no client of their own, so registering this distribution would book the
37
+ * receivable/payable leg into the payer's sub-ledger while the invoice stays
38
+ * open in the invoice client's. Thrown before the register call — nothing is
39
+ * mutated.
40
+ */
41
+ export class LinkedInvoiceClientMismatchError extends Error {
42
+ category = "linked_invoice_client_mismatch";
43
+ transaction_id;
44
+ transaction_clients_id;
45
+ invoice_table;
46
+ invoice_id;
47
+ invoice_clients_id;
48
+ next_action = LINKED_INVOICE_CLIENT_MISMATCH_NEXT_ACTION;
49
+ constructor(details) {
50
+ super(`Transaction ${details.transactionId} is booked to client ${details.transactionClientsId}, but the linked ` +
51
+ `${details.invoiceTable} ${details.invoiceId} belongs to client ${details.invoiceClientsId}. Confirming ` +
52
+ `would post the receipt into the payer's client sub-ledger instead of the invoice client's.`);
53
+ this.name = "LinkedInvoiceClientMismatchError";
54
+ this.transaction_id = details.transactionId;
55
+ this.transaction_clients_id = details.transactionClientsId;
56
+ this.invoice_table = details.invoiceTable;
57
+ this.invoice_id = details.invoiceId;
58
+ this.invoice_clients_id = details.invoiceClientsId;
59
+ }
60
+ }
29
61
  export class TransactionsApi extends BaseResource {
30
62
  constructor(client) {
31
63
  super(client, "/transactions");
@@ -44,6 +76,36 @@ export class TransactionsApi extends BaseResource {
44
76
  async update(id, data) {
45
77
  return super.update(id, data);
46
78
  }
79
+ /**
80
+ * Resolve the single client shared by every invoice in a distribution.
81
+ *
82
+ * Returns `undefined` when there is no invoice row, when an invoice carries no
83
+ * client (it cannot pin the journal's sub-ledger), or when the invoices
84
+ * disagree — one journal has one client, so a split-client distribution has no
85
+ * satisfiable expectation and must not be blocked.
86
+ */
87
+ async resolveLinkedInvoiceClient(body) {
88
+ const resolved = [];
89
+ for (const dist of body) {
90
+ if (!dist.related_id)
91
+ continue;
92
+ if (dist.related_table !== "purchase_invoices" && dist.related_table !== "sale_invoices")
93
+ continue;
94
+ const invoice = dist.related_table === "purchase_invoices"
95
+ ? await this.client.get(`/purchase_invoices/${dist.related_id}`)
96
+ : await this.client.get(`/sale_invoices/${dist.related_id}`);
97
+ const clientsId = invoice?.clients_id;
98
+ if (typeof clientsId !== "number")
99
+ return undefined;
100
+ resolved.push({ table: dist.related_table, id: dist.related_id, clientsId });
101
+ }
102
+ const first = resolved[0];
103
+ if (!first)
104
+ return undefined;
105
+ if (resolved.some(r => r.clientsId !== first.clientsId))
106
+ return undefined;
107
+ return first;
108
+ }
47
109
  /**
48
110
  * Confirm a transaction with distribution rows.
49
111
  * If the transaction has no clients_id (common for card payments), automatically
@@ -55,31 +117,61 @@ export class TransactionsApi extends BaseResource {
55
117
  * client fix. The plan-bound reconciliation executor uses this so the client
56
118
  * update is booked as its own reviewed, enumerated command instead of a hidden
57
119
  * side effect of confirmation.
120
+ *
121
+ * When the transaction ALREADY has a client and it differs from the linked
122
+ * invoice's, this throws `LinkedInvoiceClientMismatchError` before registering
123
+ * (see that class for why). Pass `{ reassignClientToInvoice: true }` — the
124
+ * caller's explicit approval — to move the transaction to the invoice's client
125
+ * first instead; that update is rolled back if the register call then fails.
58
126
  */
59
127
  async confirm(id, distributions, options) {
60
128
  const body = distributions ?? [];
61
129
  const autoFixClientsId = options?.autoFixClientsId !== false;
62
- // Auto-fix missing clients_id from linked invoice
63
- let clientsIdWasSet = false;
64
- if (autoFixClientsId && body.length > 0) {
130
+ const hasInvoiceDistribution = body.some(dist => (dist.related_table === "purchase_invoices" || dist.related_table === "sale_invoices")
131
+ && !!dist.related_id);
132
+ // Value to restore if the register call fails after we touched clients_id
133
+ // (`undefined` = we did not touch it, so there is nothing to roll back).
134
+ let clientsIdRollbackValue;
135
+ if (body.length > 0 && (autoFixClientsId || hasInvoiceDistribution)) {
65
136
  const tx = await this.get(id);
66
137
  if (!tx.clients_id) {
67
- let clientsId;
68
- for (const dist of body) {
69
- if (dist.related_table === "purchase_invoices" && dist.related_id) {
70
- const inv = await this.client.get(`/purchase_invoices/${dist.related_id}`);
71
- clientsId = inv?.clients_id;
138
+ // Auto-fix missing clients_id from linked invoice
139
+ if (autoFixClientsId) {
140
+ let clientsId;
141
+ for (const dist of body) {
142
+ if (dist.related_table === "purchase_invoices" && dist.related_id) {
143
+ const inv = await this.client.get(`/purchase_invoices/${dist.related_id}`);
144
+ clientsId = inv?.clients_id;
145
+ }
146
+ else if (dist.related_table === "sale_invoices" && dist.related_id) {
147
+ const inv = await this.client.get(`/sale_invoices/${dist.related_id}`);
148
+ clientsId = inv?.clients_id;
149
+ }
150
+ if (clientsId !== undefined)
151
+ break;
72
152
  }
73
- else if (dist.related_table === "sale_invoices" && dist.related_id) {
74
- const inv = await this.client.get(`/sale_invoices/${dist.related_id}`);
75
- clientsId = inv?.clients_id;
153
+ if (clientsId !== undefined) {
154
+ await this.update(id, { clients_id: clientsId });
155
+ clientsIdRollbackValue = null;
76
156
  }
77
- if (clientsId !== undefined)
78
- break;
79
157
  }
80
- if (clientsId !== undefined) {
81
- await this.update(id, { clients_id: clientsId });
82
- clientsIdWasSet = true;
158
+ }
159
+ else if (hasInvoiceDistribution) {
160
+ const invoice = await this.resolveLinkedInvoiceClient(body);
161
+ if (invoice && invoice.clientsId !== tx.clients_id) {
162
+ if (options?.reassignClientToInvoice === true) {
163
+ await this.update(id, { clients_id: invoice.clientsId });
164
+ clientsIdRollbackValue = tx.clients_id;
165
+ }
166
+ else {
167
+ throw new LinkedInvoiceClientMismatchError({
168
+ transactionId: id,
169
+ transactionClientsId: tx.clients_id,
170
+ invoiceTable: invoice.table,
171
+ invoiceId: invoice.id,
172
+ invoiceClientsId: invoice.clientsId,
173
+ });
174
+ }
83
175
  }
84
176
  }
85
177
  }
@@ -133,9 +225,9 @@ export class TransactionsApi extends BaseResource {
133
225
  });
134
226
  }
135
227
  }
136
- if (clientsIdWasSet) {
228
+ if (clientsIdRollbackValue !== undefined) {
137
229
  try {
138
- await this.update(id, { clients_id: null });
230
+ await this.update(id, { clients_id: clientsIdRollbackValue });
139
231
  }
140
232
  catch (rollbackErr) {
141
233
  const normalizedNetworkCause = getNormalizedNetworkCause(rollbackErr);
@@ -1 +1 @@
1
- {"version":3,"file":"transactions.api.js","sourceRoot":"","sources":["../../src/api/transactions.api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transactions.api.js","sourceRoot":"","sources":["../../src/api/transactions.api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAG/D,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,KAAK;QAC3D,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,QAAQ,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACtD,IAAI,CAAC;QACH,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACtD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QAC9E,MAAM,KAAK,GAAG,KAAK,CAAC,KAA2C,CAAC;QAChE,IACE,KAAK,CAAC,IAAI,KAAK,WAAW;YAC1B,KAAK,CAAC,MAAM,KAAK,SAAS;YAC1B,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;YACjC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;YAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YACxB,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAC3B,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,0CAA0C,GAC9C,2GAA2G;IAC3G,2GAA2G;IAC3G,8CAA8C,CAAC;AAEjD;;;;;;;;GAQG;AACH,MAAM,OAAO,gCAAiC,SAAQ,KAAK;IAChD,QAAQ,GAAG,gCAAgC,CAAC;IAC5C,cAAc,CAAS;IACvB,sBAAsB,CAAS;IAC/B,aAAa,CAAS;IACtB,UAAU,CAAS;IACnB,kBAAkB,CAAS;IAC3B,WAAW,GAAG,0CAA0C,CAAC;IAElE,YAAY,OAMX;QACC,KAAK,CACH,eAAe,OAAO,CAAC,aAAa,wBAAwB,OAAO,CAAC,oBAAoB,mBAAmB;YAC3G,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,SAAS,sBAAsB,OAAO,CAAC,gBAAgB,eAAe;YACzG,4FAA4F,CAC7F,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kCAAkC,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;QAC5C,IAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,CAAC;QAC3D,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACrD,CAAC;CACF;AAQD,MAAM,OAAO,eAAgB,SAAQ,YAAyB;IAC5D,YAAY,MAAkB;QAC5B,KAAK,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACjC,CAAC;IAEM,2CAA2C;QAChD,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,sEAAsE;IACtE,4EAA4E;IAC5E,0DAA0D;IACjD,KAAK,CAAC,MAAM,CAAC,IAAkC;QACtD,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAEQ,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,IAAkC;QAClE,OAAO,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,0BAA0B,CACtC,IAA+B;QAE/B,MAAM,QAAQ,GAA0B,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,UAAU;gBAAE,SAAS;YAC/B,IAAI,IAAI,CAAC,aAAa,KAAK,mBAAmB,IAAI,IAAI,CAAC,aAAa,KAAK,eAAe;gBAAE,SAAS;YACnG,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,KAAK,mBAAmB;gBACxD,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAkB,sBAAsB,IAAI,CAAC,UAAU,EAAE,CAAC;gBACjF,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAc,kBAAkB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAC5E,MAAM,SAAS,GAAG,OAAO,EAAE,UAAU,CAAC;YACtC,IAAI,OAAO,SAAS,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAC;YACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC1E,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,OAAO,CACX,EAAU,EACV,aAAyC,EACzC,OAA2E;QAE3E,MAAM,IAAI,GAAG,aAAa,IAAI,EAAE,CAAC;QACjC,MAAM,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,KAAK,KAAK,CAAC;QAC7D,MAAM,sBAAsB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9C,CAAC,IAAI,CAAC,aAAa,KAAK,mBAAmB,IAAI,IAAI,CAAC,aAAa,KAAK,eAAe,CAAC;eACnF,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAExB,0EAA0E;QAC1E,yEAAyE;QACzE,IAAI,sBAAiD,CAAC;QACtD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,sBAAsB,CAAC,EAAE,CAAC;YACpE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;gBACnB,kDAAkD;gBAClD,IAAI,gBAAgB,EAAE,CAAC;oBACrB,IAAI,SAA6B,CAAC;oBAElC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;wBACxB,IAAI,IAAI,CAAC,aAAa,KAAK,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;4BAClE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAkB,sBAAsB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;4BAC5F,SAAS,GAAG,GAAG,EAAE,UAAU,CAAC;wBAC9B,CAAC;6BAAM,IAAI,IAAI,CAAC,aAAa,KAAK,eAAe,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;4BACrE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAc,kBAAkB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;4BACpF,SAAS,GAAG,GAAG,EAAE,UAAU,CAAC;wBAC9B,CAAC;wBACD,IAAI,SAAS,KAAK,SAAS;4BAAE,MAAM;oBACrC,CAAC;oBAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;wBAC5B,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;wBACjD,sBAAsB,GAAG,IAAI,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,IAAI,sBAAsB,EAAE,CAAC;gBAClC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC;gBAC5D,IAAI,OAAO,IAAI,OAAO,CAAC,SAAS,KAAK,EAAE,CAAC,UAAU,EAAE,CAAC;oBACnD,IAAI,OAAO,EAAE,uBAAuB,KAAK,IAAI,EAAE,CAAC;wBAC9C,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;wBACzD,sBAAsB,GAAG,EAAE,CAAC,UAAU,CAAC;oBACzC,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,gCAAgC,CAAC;4BACzC,aAAa,EAAE,EAAE;4BACjB,oBAAoB,EAAE,EAAE,CAAC,UAAU;4BACnC,YAAY,EAAE,OAAO,CAAC,KAAK;4BAC3B,SAAS,EAAE,OAAO,CAAC,EAAE;4BACrB,gBAAgB,EAAE,OAAO,CAAC,SAAS;yBACpC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAc,iBAAiB,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;YAC1F,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,qEAAqE;YACrE,qEAAqE;YACrE,iEAAiE;YACjE,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;YAClC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,KAAK,YAAY,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC7D,IAAI,gBAA6B,CAAC;gBAClC,IAAI,CAAC;oBACH,gBAAgB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACxC,CAAC;gBAAC,OAAO,SAAS,EAAE,CAAC;oBACnB,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;oBAClC,MAAM,IAAI,0BAA0B,CAAC;wBACnC,SAAS,EAAE,SAAS;wBACpB,MAAM,EAAE,aAAa;wBACrB,QAAQ,EAAE,EAAE;wBACZ,WAAW,EAAE,cAAc,GAAG,EAAE;wBAChC,cAAc,EAAE,CAAC,eAAe,EAAE,WAAW,CAAC;wBAC9C,KAAK,EAAE,SAAS;wBAChB,UAAU,EAAE,2BAA2B,GAAG,EAAE;4BAC1C,gEAAgE;qBACnE,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,gBAAgB,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC5C,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;oBAClC,oEAAoE;oBACpE,oEAAoE;oBACpE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,4CAA4C,CAAC,EAAE,CAAC;gBACjF,CAAC;gBAED,IAAI,gBAAgB,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC1C,IAAI,CAAC,eAAe,EAAE,CAAC;oBACvB,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;oBAClC,MAAM,IAAI,0BAA0B,CAAC;wBACnC,SAAS,EAAE,SAAS;wBACpB,MAAM,EAAE,aAAa;wBACrB,QAAQ,EAAE,EAAE;wBACZ,WAAW,EAAE,cAAc,GAAG,EAAE;wBAChC,cAAc,EAAE,CAAC,eAAe,EAAE,WAAW,CAAC;wBAC9C,KAAK,EAAE,KAAK;wBACZ,UAAU,EAAE,2BAA2B,GAAG,EAAE;4BAC1C,gEAAgE;qBACnE,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;gBACzC,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBAChE,CAAC;gBAAC,OAAO,WAAW,EAAE,CAAC;oBACrB,MAAM,sBAAsB,GAAG,yBAAyB,CAAC,WAAW,CAAC,CAAC;oBACtE,IAAI,sBAAsB,EAAE,CAAC;wBAC3B,IAAI,CAAC,2CAA2C,EAAE,CAAC;wBACnD,MAAM,IAAI,0BAA0B,CAAC;4BACnC,SAAS,EAAE,UAAU;4BACrB,MAAM,EAAE,aAAa;4BACrB,QAAQ,EAAE,EAAE;4BACZ,WAAW,EAAE,cAAc,GAAG,EAAE;4BAChC,cAAc,EAAE,CAAC,eAAe,CAAC;4BACjC,KAAK,EAAE,sBAAsB;4BAC7B,UAAU,EAAE,2BAA2B,GAAG,EAAE;gCAC1C,qDAAqD;yBACxD,CAAC,CAAC;oBACL,CAAC;oBACD,IAAI,WAAW,YAAY,SAAS,IAAI,WAAW,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;wBACzE,IAAI,CAAC,2CAA2C,EAAE,CAAC;wBACnD,MAAM,IAAI,0BAA0B,CAAC;4BACnC,SAAS,EAAE,UAAU;4BACrB,MAAM,EAAE,aAAa;4BACrB,QAAQ,EAAE,EAAE;4BACZ,WAAW,EAAE,cAAc,GAAG,EAAE;4BAChC,cAAc,EAAE,CAAC,eAAe,CAAC;4BACjC,KAAK,EAAE,WAAW;4BAClB,UAAU,EAAE,2BAA2B,GAAG,EAAE;gCAC1C,qDAAqD;yBACxD,CAAC,CAAC;oBACL,CAAC;oBACD,MAAM,WAAW,GAAG,WAAW,YAAY,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBAC7F,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0DAA0D,EAAE,KAAK,WAAW,IAAI,CACjF,CAAC;oBACF,MAAM,IAAI,KAAK,CACb,eAAe,EAAE,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI;wBACpG,uCAAuC,WAAW,IAAI;wBACtD,qEAAqE,CACtE,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QACzB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAc,iBAAiB,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;QAC1F,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,mEAAmE;QACnE,sCAAsC;QACtC,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;QAClC,OAAO,MAAM,CAAC;IAChB,CAAC;CAEF"}
@@ -0,0 +1,122 @@
1
+ import type { Journal, PurchaseInvoice, SaleInvoice, Transaction } from "../types/api.js";
2
+ /**
3
+ * Post-confirm receipt-ledger invariant.
4
+ *
5
+ * Registering a bank transaction against an invoice is the one write where the
6
+ * MCP server cannot see what it produced: the register call returns no journal
7
+ * id, and the journal's client is copied from the TRANSACTION (the payer), not
8
+ * from the linked invoice. When a third party pays someone else's invoice, the
9
+ * receivable/payable leg therefore lands in the payer's client sub-ledger while
10
+ * the invoice stays open in the invoice client's — silently.
11
+ *
12
+ * This module re-reads the ledger after such a confirm and asserts three
13
+ * things about the registration journal: it exists and is unique, its client is
14
+ * the invoice's client, and its postings actually move the transaction's own
15
+ * bank account against the invoice's own receivable/payable account for the
16
+ * transaction amount. Every account id comes from the records (`tx.accounts_id`,
17
+ * sale `receivable_accounts_id`, purchase `liability_accounts_id`) — nothing is
18
+ * hardcoded to 1020/1210/2310.
19
+ *
20
+ * Kept MCP-free: no tool-response or sandbox imports, so it stays a pure
21
+ * checker plus one api entry point.
22
+ */
23
+ export declare const RECEIPT_LEDGER_AMOUNT_TOLERANCE = 0.01;
24
+ export type ReceiptLedgerFailureCode = "registration_journal_not_found" | "ledger_client_mismatch" | "ledger_posting_mismatch";
25
+ export type InvoiceTable = "sale_invoices" | "purchase_invoices";
26
+ export interface LinkedReceiptInvoice {
27
+ table: InvoiceTable;
28
+ id: number;
29
+ clients_id: number | null;
30
+ /**
31
+ * The invoice's own ledger account: `receivable_accounts_id` for a sale,
32
+ * `liability_accounts_id` for a purchase. `null` when the record does not
33
+ * carry it — then the contra leg cannot be checked (and is reported as
34
+ * unverified rather than passed off as verified).
35
+ */
36
+ ledger_accounts_id: number | null;
37
+ /**
38
+ * EUR magnitude of the distribution booked against THIS invoice, not the
39
+ * transaction total. A transaction may settle an invoice alongside a GL row,
40
+ * and an FX receipt splits into invoice amount + exchange difference, so the
41
+ * contra leg must be measured against the invoice rows. `null` when the
42
+ * amount is unknown — then the contra leg is reported as unverified.
43
+ */
44
+ amount: number | null;
45
+ }
46
+ export interface ReceiptLedgerFailureDetails {
47
+ transaction_id: number | null;
48
+ transaction_clients_id: number | null;
49
+ invoice_clients_id: number | null;
50
+ invoice_table: InvoiceTable | null;
51
+ invoice_id: number | null;
52
+ journal_id?: number;
53
+ [key: string]: unknown;
54
+ }
55
+ export type ReceiptLedgerCheckResult = {
56
+ ok: true;
57
+ journal_id?: number;
58
+ skipped?: string;
59
+ unverified?: string[];
60
+ } | {
61
+ ok: false;
62
+ code: ReceiptLedgerFailureCode;
63
+ details: ReceiptLedgerFailureDetails;
64
+ };
65
+ /**
66
+ * Locate the registration journals of a confirmed bank transaction.
67
+ *
68
+ * Verified live: `operation_type === "TRANSACTION"` + `operations_id === tx.id`
69
+ * is the only reliable link (the register call returns no journal id). Only
70
+ * registered journals count — invalidating and re-confirming a transaction
71
+ * leaves the superseded journal row behind, and every other ledger consumer
72
+ * filters on `registered` too (see `buildInterAccountJournalIndex`). Returns
73
+ * every live match so the caller can insist on exactly one.
74
+ */
75
+ export declare function findRegistrationJournal(journals: readonly Journal[], txId: number): Journal[];
76
+ export declare function checkReceiptLedger(input: {
77
+ tx: Transaction;
78
+ invoices: readonly LinkedReceiptInvoice[];
79
+ journals: readonly Journal[];
80
+ }): ReceiptLedgerCheckResult;
81
+ export interface ReceiptLedgerApi {
82
+ transactions: {
83
+ get(id: number): Promise<Transaction>;
84
+ };
85
+ saleInvoices: {
86
+ get(id: number): Promise<SaleInvoice>;
87
+ };
88
+ purchaseInvoices: {
89
+ get(id: number): Promise<PurchaseInvoice>;
90
+ };
91
+ journals: {
92
+ listAll(params?: {
93
+ start_date?: string;
94
+ end_date?: string;
95
+ }): Promise<Journal[]>;
96
+ get(id: number): Promise<Journal>;
97
+ };
98
+ }
99
+ export interface TransactionInvoiceLink {
100
+ table: InvoiceTable;
101
+ id: number;
102
+ /** EUR magnitude booked against this invoice, or null when a row's amount is unknown. */
103
+ amount: number | null;
104
+ }
105
+ /**
106
+ * Distinct invoice links carried by a transaction's items
107
+ * (relation_table/relation_id), each with the EUR amount booked against it.
108
+ *
109
+ * Several item rows may point at one invoice, so amounts are summed. A row
110
+ * whose amount is not a finite number makes the whole invoice's amount unknown
111
+ * (`null`) rather than silently under-counting the expectation.
112
+ */
113
+ export declare function invoiceLinksFromTransaction(tx: Transaction): TransactionInvoiceLink[];
114
+ /**
115
+ * Re-read a just-confirmed transaction and assert the receipt invariant.
116
+ *
117
+ * Only invoice-linked confirms are checked — a transaction booked straight to
118
+ * GL accounts has no invoice client to compare against, so it returns
119
+ * `{ ok: true, skipped: "no_invoice_distribution" }`.
120
+ */
121
+ export declare function verifyInvoiceReceiptLedger(api: ReceiptLedgerApi, txId: number): Promise<ReceiptLedgerCheckResult>;
122
+ //# sourceMappingURL=receipt-ledger-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt-ledger-check.d.ts","sourceRoot":"","sources":["../../src/banking/receipt-ledger-check.ts"],"names":[],"mappings":"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"}