Add ledger, address/contact sort, and tighter schema-graph layout.

Replace manifestarticle.transactedamount with an append-only ledger, add sort on entity address/contact, and allow same-rank FKs with crossing-aware exits in the graph.
This commit is contained in:
seb
2026-08-06 07:39:48 +02:00
parent 3b77a1c946
commit ae549fd022
17 changed files with 2019 additions and 1656 deletions

View File

@@ -236,11 +236,11 @@ Addresses for an entity. Auth via company + entity ownership.
| Method | Signature | Returns |
| --- | --- | --- |
| `list` | `(googleAuthId, companyId, entityId)` | `EntityAddress[]` |
| `create` | `(googleAuthId, companyId, entityId, { addressType, firstName?, name?, street?, streetNumber?, zip?, city?, county? })` | `EntityAddress` |
| `create` | `(googleAuthId, companyId, entityId, { addressType, firstName?, name?, street?, streetNumber?, zip?, city?, county?, sort? })` | `EntityAddress` |
| `update` | `(googleAuthId, companyId, entityId, id, patch)` | `EntityAddress` |
| `delete` | `(googleAuthId, companyId, entityId, id)` | `void` |
`addressType` required. Errors: `Company not found`, `Not authorized`, `Entity not found`, `Address type required`, `Address not found`.
`addressType` required. `sort` defaults to next index for the entity (`0…n`). List ordered by `sort`, `id`. Errors: `Company not found`, `Not authorized`, `Entity not found`, `Address type required`, `Address not found`, `Invalid sort`.
---
@@ -250,12 +250,12 @@ Contacts for an entity (`contactType` e.g. `email`, `phone`). Auth via company +
| Method | Signature | Returns |
| --- | --- | --- |
| `list` | `(googleAuthId, companyId, entityId)` | `{ id, entityId, contactType, contact }[]` |
| `create` | `(googleAuthId, companyId, entityId, { contactType, contact })` | row |
| `update` | `(googleAuthId, companyId, entityId, id, { contactType?, contact? })` | row |
| `list` | `(googleAuthId, companyId, entityId)` | `{ id, entityId, contactType, contact, sort }[]` |
| `create` | `(googleAuthId, companyId, entityId, { contactType, contact, sort? })` | row |
| `update` | `(googleAuthId, companyId, entityId, id, { contactType?, contact?, sort? })` | row |
| `delete` | `(googleAuthId, companyId, entityId, id)` | `void` |
Errors: `Company not found`, `Not authorized`, `Entity not found`, `Contact required`, `Contact not found`.
`sort` defaults to next index for the entity. List ordered by `sort`, `id`. Errors: `Company not found`, `Not authorized`, `Entity not found`, `Contact required`, `Contact not found`, `Invalid sort`.
---
@@ -301,7 +301,6 @@ type DeliveryLine = {
sku: string | null,
amount: number,
net: number,
transactedAmount: number,
rebate: number,
tax: number, // tax.rate
comment: string | null,
@@ -310,7 +309,7 @@ type DeliveryLine = {
type Delivery = DeliverySummary & { lines: DeliveryLine[] }
```
`create` inserts header + lines in one transaction. `supplierId` null or a Lieferanten `entity` of the company (else `Supplier not found`). Each line requires `sku` (resolved in the company category tree → `Article not found`), `amount`, `net`, `tax` (rate → `tax.ensure`); `rebate` defaults `0`; `transactedAmount` defaults to `amount`. Empty/invalid `number` or `date` → `Invalid delivery`; bad line payload → `Invalid lines`.
`create` inserts header + lines in one transaction. `supplierId` null or a Lieferanten `entity` of the company (else `Supplier not found`). Each line requires `sku` (resolved in the company category tree → `Article not found`), `amount`, `net`, `tax` (rate → `tax.ensure`); `rebate` defaults `0`. Empty/invalid `number` or `date` → `Invalid delivery`; bad line payload → `Invalid lines`.
`update` patches header fields that are present. If `lines` is present, replace-all lines; if omitted, leave lines unchanged. Returns full `Delivery`.
@@ -318,6 +317,23 @@ type Delivery = DeliverySummary & { lines: DeliveryLine[] }
---
## `ledger`
Append-only SQL Server ledger table (`WITH (LEDGER = ON (APPEND_ONLY = ON))`). Rows cannot be updated or deleted. References `manifestarticle` with `ON DELETE NO ACTION` (a line with ledger entries cannot be removed). No store API yet — INSERT only via future store methods / direct SQL.
| Column | Type | Notes |
| --- | --- | --- |
| `id` | `INT IDENTITY` | PK |
| `manifestarticle_id` | `INT` | FK → `manifestarticle` |
| `transaction_type` | `NVARCHAR(100)` | |
| `amount` | `FLOAT` | |
| `transaction_text` | `NVARCHAR(MAX)` | |
| `prev_hash` | `NVARCHAR(128)` | nullable (genesis row) |
Engine also maintains `ledger_start_transaction_id` / `ledger_start_sequence_number`. `purge`/`reset-db` soft-drops the table (`MSSQL_DroppedLedgerTable_*` remains; a new `ledger` can still be created).
---
## `posterminal`
Company-scoped POS terminals. Auth via `resolveCompanyAccess`.