ekwo-os 0.20.0 → 0.21.2
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.
- package/README.md +14 -2
- package/dist/assets/expected-objects.json +149 -4
- package/dist/assets/migrations/20261023160600_a_draft_changes_its_lines_in_one_statement.sql +126 -0
- package/dist/assets/migrations/20261023174709_an_agent_posts_in_the_mode_its_company_chose.sql +501 -0
- package/dist/assets/migrations/20261025072151_a_schema_for_the_socle.sql +52 -0
- package/dist/assets/migrations/20261026030000_schema_version_0_21_0.sql +39 -0
- package/dist/assets/migrations/20261027073538_an_archive_carries_its_own_clock.sql +27 -0
- package/dist/assets/modules/fixed-assets/supabase/migrations/20261023160500_assets_months_run_while_the_asset_is_in_service.sql +227 -0
- package/dist/assets/seed/00_currencies.sql +25 -0
- package/dist/assets/seed/00_territories.sql +72 -0
- package/dist/assets/seed/222_pack_fj.sql +626 -0
- package/dist/assets/seed/223_pack_ki.sql +629 -0
- package/dist/assets/seed/225_pack_ws.sql +620 -0
- package/dist/assets/seed/227_pack_to.sql +619 -0
- package/dist/assets/seed/228_pack_vu.sql +621 -0
- package/dist/assets/seed/411_pack_mc.sql +905 -0
- package/dist/assets/seed/412_pack_pg.sql +619 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +3 -2
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.d.ts +3 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +35 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/company.d.ts.map +1 -1
- package/dist/commands/company.js +42 -1
- package/dist/commands/company.js.map +1 -1
- package/dist/commands/document.d.ts +2 -2
- package/dist/commands/document.d.ts.map +1 -1
- package/dist/commands/document.js +27 -3
- package/dist/commands/document.js.map +1 -1
- package/dist/commands/key.d.ts +13 -0
- package/dist/commands/key.d.ts.map +1 -0
- package/dist/commands/key.js +80 -0
- package/dist/commands/key.js.map +1 -0
- package/dist/commands/mcp.d.ts +27 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +70 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +11 -0
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/module.d.ts.map +1 -1
- package/dist/commands/module.js +2 -1
- package/dist/commands/module.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +10 -0
- package/dist/context.js.map +1 -1
- package/dist/doctor.d.ts +9 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +92 -19
- package/dist/doctor.js.map +1 -1
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +13 -5
- package/dist/inventory.js.map +1 -1
- package/dist/output.js +1 -1
- package/dist/output.js.map +1 -1
- package/dist/rest.d.ts +11 -4
- package/dist/rest.d.ts.map +1 -1
- package/dist/rest.js +34 -4
- package/dist/rest.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/dist/seeds.d.ts +5 -0
- package/dist/seeds.d.ts.map +1 -1
- package/dist/seeds.js +7 -0
- package/dist/seeds.js.map +1 -1
- package/dist/socle.d.ts +35 -0
- package/dist/socle.d.ts.map +1 -0
- package/dist/socle.js +48 -0
- package/dist/socle.js.map +1 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +5 -3
- package/dist/status.js.map +1 -1
- package/package.json +3 -3
- package/schema/output.1.json +20 -0
package/README.md
CHANGED
|
@@ -171,7 +171,11 @@ project reports what was already there and creates nothing a second time.
|
|
|
171
171
|
| `ekwo contact duplicates` | The pairs of contacts that are probably one party, strongest first, through `suggest_duplicate_contacts()`: which one to keep, a score, and the evidence — the same VAT or registration number, the same bank account, the words of the name weighed by how few contacts carry them. Writes nothing. |
|
|
172
172
|
| `ekwo contact merge <keep> <absorb>` | One party created twice — two spellings, two imports — joined through `merge_contacts()`: everything that names the second, posted lines and issued documents included, names the first; the first takes the fields it left empty; the second is retired, never deleted, and named by nothing again. By name or id. |
|
|
173
173
|
| `ekwo doc new` / `doc line add` | A draft document — any kind, with `--type` — and one more line on it. A draft books nothing. `ekwo invoice` is the old name of `ekwo doc`, kept as an alias. |
|
|
174
|
-
| `ekwo
|
|
174
|
+
| `ekwo doc approve <document>` | Approves a draft, as it stands, for an agent to post where agents post in `review`, through `approve_document()`. Signed in as yourself, with `documents.post`; refused to a machine key. A change to the draft afterwards needs another approval. |
|
|
175
|
+
| `ekwo company agent-posting [review\|autopilot\|review_only]` | The setting `agent_posting`: how an agent — a machine key, the MCP server — books a document of the company in use. `review`, the default, once a person approved it, unless the agent's session was started with `--autopilot`; `autopilot`, directly, within its rights, each posting on the audit trail; `review_only`, review with autopilot refused to every session. Without a value, says which. Signed in as yourself, with `company.write`; an agent may only make it stricter. |
|
|
176
|
+
| `ekwo key list [--all]` / `ekwo key create <name> --capability <code>… [--expires <timestamp>] [--autopilot]` | The machine keys of the company in use, with what each may do and how it posts; and one more, through `create_api_key()`, its secret shown once. A key posts as the company's `agent_posting` says; `--autopilot` issues one that posts without approvals — with `company.write`, never in a `review_only` company — and the mode is fixed for good: a key wanted in another mode is issued again. Needs `members.manage`. |
|
|
177
|
+
| `ekwo mcp [--autopilot\|--review]` | The MCP server, over stdio, for an agent: the same server as `ekwo-mcp` of [`@ekwo-ai/mcp`](../mcp/README.md), with its flags and its environment (`ekwo mcp --help`). The `@ekwo-ai/mcp` installed beside this CLI, or the one of its release through `npx` — this CLI keeps its one outside dependency. In `.mcp.json`: `"command": "ekwo", "args": ["mcp"]`. |
|
|
178
|
+
| `ekwo post <document>` | Books it, through `post_document()`. `--dry-run` shows the entry the database would write and writes nothing. Signed in as yourself, you post in any mode; with a machine key, the company's `agent_posting` applies, or the key's own autopilot. |
|
|
175
179
|
| `ekwo cancel <document>` | Undoes a posted invoice, and says how. Back to draft, through `unpost_document()`, where its country allows it and nothing about it has left; otherwise through `cancel_document()`: the credit note that names it, posted and matched against it, and the invoice cancelled. `--date` books the credit note on another day than the invoice's, which is how a locked period is stepped over; `--credit` asks for the note where a draft was possible. |
|
|
176
180
|
| `ekwo reverse <entry>` | Undoes a posted entry keyed by hand, through `reverse_entry()`: its mirror, posted and matched against it. By id or by number; `--date` as for `cancel`. |
|
|
177
181
|
| `ekwo line contacts suggest [--line <line>]…` / `ekwo line contact set <line> <contact>` | The open receivable and payable lines that name nobody — open items of books imported line by line — with the contacts each could be, best first: a learned motif, the number of a document of the company quoted in the label, the description or the reference of the entry, the words of a contact's name, and how many contacts the same evidence reached. `set` names one, once, through `set_line_contact()`, and the label is learned for the next time. A contact by name or id. |
|
|
@@ -386,6 +390,14 @@ extra tables are reported as information. It also checks row level security on
|
|
|
386
390
|
every table, pending migrations, memberships of deleted users, companies
|
|
387
391
|
without a bank account, and posted entries that do not balance.
|
|
388
392
|
|
|
393
|
+
Two checks say where the socle lives, ahead of its move to the schema `ekwo`
|
|
394
|
+
in 0.22.0 ([decision 0090](../../docs/decisions/0090-the-socle-has-its-own-schema.md)):
|
|
395
|
+
`socle_schema` (`ekwo` or `public`, found by the marker `ekwo_schema_version()`)
|
|
396
|
+
and `ekwo_exposed` (whether the Data API serves `ekwo`, read from
|
|
397
|
+
`pgrst.db_schemas` where the database carries it; otherwise the line to set).
|
|
398
|
+
`ekwo migrate` prints the same line: Project Settings → Data API → Exposed
|
|
399
|
+
schemas, add `ekwo`.
|
|
400
|
+
|
|
389
401
|
Exit code `0` when there is no problem, `1` otherwise — usable as a deployment
|
|
390
402
|
gate.
|
|
391
403
|
|
|
@@ -529,7 +541,7 @@ exactly six fields:
|
|
|
529
541
|
"organization": "My Organisation",
|
|
530
542
|
"country": "<cc>",
|
|
531
543
|
"edition": "community",
|
|
532
|
-
"schema_version": "0.
|
|
544
|
+
"schema_version": "0.21.0",
|
|
533
545
|
"contact_email": "you@example.com"
|
|
534
546
|
}
|
|
535
547
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion": "0.
|
|
3
|
-
"migration": "
|
|
2
|
+
"schemaVersion": "0.21.0",
|
|
3
|
+
"migration": "20261027073538",
|
|
4
4
|
"socle": {
|
|
5
5
|
"schema": "public",
|
|
6
6
|
"tables": [
|
|
@@ -346,6 +346,10 @@
|
|
|
346
346
|
"name": "name",
|
|
347
347
|
"type": "text"
|
|
348
348
|
},
|
|
349
|
+
{
|
|
350
|
+
"name": "posting_mode",
|
|
351
|
+
"type": "text"
|
|
352
|
+
},
|
|
349
353
|
{
|
|
350
354
|
"name": "prefix",
|
|
351
355
|
"type": "text"
|
|
@@ -889,6 +893,10 @@
|
|
|
889
893
|
"name": "address_line2",
|
|
890
894
|
"type": "text"
|
|
891
895
|
},
|
|
896
|
+
{
|
|
897
|
+
"name": "agent_posting",
|
|
898
|
+
"type": "text"
|
|
899
|
+
},
|
|
892
900
|
{
|
|
893
901
|
"name": "archived_at",
|
|
894
902
|
"type": "timestamp with time zone"
|
|
@@ -4751,6 +4759,18 @@
|
|
|
4751
4759
|
"name": "aged_balance",
|
|
4752
4760
|
"arguments": "p_company_id uuid, p_at date, p_group text"
|
|
4753
4761
|
},
|
|
4762
|
+
{
|
|
4763
|
+
"name": "agent_call",
|
|
4764
|
+
"arguments": ""
|
|
4765
|
+
},
|
|
4766
|
+
{
|
|
4767
|
+
"name": "agent_mark",
|
|
4768
|
+
"arguments": "p_name text"
|
|
4769
|
+
},
|
|
4770
|
+
{
|
|
4771
|
+
"name": "agent_posting_mode",
|
|
4772
|
+
"arguments": "p_company_id uuid"
|
|
4773
|
+
},
|
|
4754
4774
|
{
|
|
4755
4775
|
"name": "amount_text_format",
|
|
4756
4776
|
"arguments": "p_rounding money_rounding"
|
|
@@ -4775,6 +4795,10 @@
|
|
|
4775
4795
|
"name": "api_key_on_company",
|
|
4776
4796
|
"arguments": "p_company_id uuid"
|
|
4777
4797
|
},
|
|
4798
|
+
{
|
|
4799
|
+
"name": "approve_document",
|
|
4800
|
+
"arguments": "p_document_id uuid"
|
|
4801
|
+
},
|
|
4778
4802
|
{
|
|
4779
4803
|
"name": "archive_company",
|
|
4780
4804
|
"arguments": "p_company_id uuid"
|
|
@@ -4947,6 +4971,10 @@
|
|
|
4947
4971
|
"name": "commercial_entity",
|
|
4948
4972
|
"arguments": "p_contact_id uuid"
|
|
4949
4973
|
},
|
|
4974
|
+
{
|
|
4975
|
+
"name": "companies_agent_posting_by_a_person",
|
|
4976
|
+
"arguments": ""
|
|
4977
|
+
},
|
|
4950
4978
|
{
|
|
4951
4979
|
"name": "companies_creator_is_fixed",
|
|
4952
4980
|
"arguments": ""
|
|
@@ -5081,12 +5109,16 @@
|
|
|
5081
5109
|
},
|
|
5082
5110
|
{
|
|
5083
5111
|
"name": "create_api_key",
|
|
5084
|
-
"arguments": "p_company_id uuid, p_name text, p_capabilities jsonb, p_expires_at timestamp with time zone"
|
|
5112
|
+
"arguments": "p_company_id uuid, p_name text, p_capabilities jsonb, p_expires_at timestamp with time zone, p_posting_mode text"
|
|
5085
5113
|
},
|
|
5086
5114
|
{
|
|
5087
5115
|
"name": "create_company",
|
|
5088
5116
|
"arguments": "p_name text, p_country character, p_currency_code character, p_language character, p_chart_code text, p_fiscal_year integer, p_fiscal_year_start date, p_owner_user_id uuid"
|
|
5089
5117
|
},
|
|
5118
|
+
{
|
|
5119
|
+
"name": "create_document",
|
|
5120
|
+
"arguments": "p_document jsonb, p_lines jsonb"
|
|
5121
|
+
},
|
|
5090
5122
|
{
|
|
5091
5123
|
"name": "currency_of_bank_account",
|
|
5092
5124
|
"arguments": ""
|
|
@@ -5203,6 +5235,10 @@
|
|
|
5203
5235
|
"name": "document_territory",
|
|
5204
5236
|
"arguments": "p_document_id uuid, p_party text"
|
|
5205
5237
|
},
|
|
5238
|
+
{
|
|
5239
|
+
"name": "document_version",
|
|
5240
|
+
"arguments": "p_document_id uuid"
|
|
5241
|
+
},
|
|
5206
5242
|
{
|
|
5207
5243
|
"name": "documents_allocate_included_tax",
|
|
5208
5244
|
"arguments": "p_document_id uuid"
|
|
@@ -5223,6 +5259,10 @@
|
|
|
5223
5259
|
"name": "documents_match_credit_note_on_posting",
|
|
5224
5260
|
"arguments": ""
|
|
5225
5261
|
},
|
|
5262
|
+
{
|
|
5263
|
+
"name": "documents_posting_mode",
|
|
5264
|
+
"arguments": ""
|
|
5265
|
+
},
|
|
5226
5266
|
{
|
|
5227
5267
|
"name": "documents_refresh_amount_paid",
|
|
5228
5268
|
"arguments": "p_document_id uuid"
|
|
@@ -5819,6 +5859,10 @@
|
|
|
5819
5859
|
"name": "reopen_fiscal_year",
|
|
5820
5860
|
"arguments": "p_fiscal_year_id uuid"
|
|
5821
5861
|
},
|
|
5862
|
+
{
|
|
5863
|
+
"name": "replace_document_lines",
|
|
5864
|
+
"arguments": "p_document_id uuid, p_lines jsonb"
|
|
5865
|
+
},
|
|
5822
5866
|
{
|
|
5823
5867
|
"name": "resolve_counterpart_account",
|
|
5824
5868
|
"arguments": "p_company_id uuid, p_contact_id uuid, p_is_sale boolean"
|
|
@@ -6844,6 +6888,10 @@
|
|
|
6844
6888
|
"table": "bank_transactions",
|
|
6845
6889
|
"name": "bank_transactions_set_updated_at"
|
|
6846
6890
|
},
|
|
6891
|
+
{
|
|
6892
|
+
"table": "companies",
|
|
6893
|
+
"name": "companies_agent_posting_by_a_person"
|
|
6894
|
+
},
|
|
6847
6895
|
{
|
|
6848
6896
|
"table": "companies",
|
|
6849
6897
|
"name": "companies_audit"
|
|
@@ -7108,6 +7156,10 @@
|
|
|
7108
7156
|
"table": "documents",
|
|
7109
7157
|
"name": "documents_match_credit_note_on_posting"
|
|
7110
7158
|
},
|
|
7159
|
+
{
|
|
7160
|
+
"table": "documents",
|
|
7161
|
+
"name": "documents_posting_mode"
|
|
7162
|
+
},
|
|
7111
7163
|
{
|
|
7112
7164
|
"table": "documents",
|
|
7113
7165
|
"name": "documents_refresh_payment_state"
|
|
@@ -9056,6 +9108,39 @@
|
|
|
9056
9108
|
"execute"
|
|
9057
9109
|
]
|
|
9058
9110
|
},
|
|
9111
|
+
{
|
|
9112
|
+
"name": "agent_call",
|
|
9113
|
+
"arguments": "",
|
|
9114
|
+
"anon": [],
|
|
9115
|
+
"authenticated": [
|
|
9116
|
+
"execute"
|
|
9117
|
+
],
|
|
9118
|
+
"service_role": [
|
|
9119
|
+
"execute"
|
|
9120
|
+
]
|
|
9121
|
+
},
|
|
9122
|
+
{
|
|
9123
|
+
"name": "agent_mark",
|
|
9124
|
+
"arguments": "p_name text",
|
|
9125
|
+
"anon": [],
|
|
9126
|
+
"authenticated": [
|
|
9127
|
+
"execute"
|
|
9128
|
+
],
|
|
9129
|
+
"service_role": [
|
|
9130
|
+
"execute"
|
|
9131
|
+
]
|
|
9132
|
+
},
|
|
9133
|
+
{
|
|
9134
|
+
"name": "agent_posting_mode",
|
|
9135
|
+
"arguments": "p_company_id uuid",
|
|
9136
|
+
"anon": [],
|
|
9137
|
+
"authenticated": [
|
|
9138
|
+
"execute"
|
|
9139
|
+
],
|
|
9140
|
+
"service_role": [
|
|
9141
|
+
"execute"
|
|
9142
|
+
]
|
|
9143
|
+
},
|
|
9059
9144
|
{
|
|
9060
9145
|
"name": "amount_text_format",
|
|
9061
9146
|
"arguments": "p_rounding money_rounding",
|
|
@@ -9124,6 +9209,17 @@
|
|
|
9124
9209
|
"execute"
|
|
9125
9210
|
]
|
|
9126
9211
|
},
|
|
9212
|
+
{
|
|
9213
|
+
"name": "approve_document",
|
|
9214
|
+
"arguments": "p_document_id uuid",
|
|
9215
|
+
"anon": [],
|
|
9216
|
+
"authenticated": [
|
|
9217
|
+
"execute"
|
|
9218
|
+
],
|
|
9219
|
+
"service_role": [
|
|
9220
|
+
"execute"
|
|
9221
|
+
]
|
|
9222
|
+
},
|
|
9127
9223
|
{
|
|
9128
9224
|
"name": "archive_company",
|
|
9129
9225
|
"arguments": "p_company_id uuid",
|
|
@@ -9544,6 +9640,14 @@
|
|
|
9544
9640
|
"execute"
|
|
9545
9641
|
]
|
|
9546
9642
|
},
|
|
9643
|
+
{
|
|
9644
|
+
"name": "companies_agent_posting_by_a_person",
|
|
9645
|
+
"arguments": "",
|
|
9646
|
+
"trigger": true,
|
|
9647
|
+
"anon": [],
|
|
9648
|
+
"authenticated": [],
|
|
9649
|
+
"service_role": []
|
|
9650
|
+
},
|
|
9547
9651
|
{
|
|
9548
9652
|
"name": "companies_creator_is_fixed",
|
|
9549
9653
|
"arguments": "",
|
|
@@ -9854,7 +9958,7 @@
|
|
|
9854
9958
|
},
|
|
9855
9959
|
{
|
|
9856
9960
|
"name": "create_api_key",
|
|
9857
|
-
"arguments": "p_company_id uuid, p_name text, p_capabilities jsonb, p_expires_at timestamp with time zone",
|
|
9961
|
+
"arguments": "p_company_id uuid, p_name text, p_capabilities jsonb, p_expires_at timestamp with time zone, p_posting_mode text",
|
|
9858
9962
|
"anon": [],
|
|
9859
9963
|
"authenticated": [
|
|
9860
9964
|
"execute"
|
|
@@ -9874,6 +9978,17 @@
|
|
|
9874
9978
|
"execute"
|
|
9875
9979
|
]
|
|
9876
9980
|
},
|
|
9981
|
+
{
|
|
9982
|
+
"name": "create_document",
|
|
9983
|
+
"arguments": "p_document jsonb, p_lines jsonb",
|
|
9984
|
+
"anon": [],
|
|
9985
|
+
"authenticated": [
|
|
9986
|
+
"execute"
|
|
9987
|
+
],
|
|
9988
|
+
"service_role": [
|
|
9989
|
+
"execute"
|
|
9990
|
+
]
|
|
9991
|
+
},
|
|
9877
9992
|
{
|
|
9878
9993
|
"name": "currency_of_bank_account",
|
|
9879
9994
|
"arguments": "",
|
|
@@ -10151,6 +10266,17 @@
|
|
|
10151
10266
|
"execute"
|
|
10152
10267
|
]
|
|
10153
10268
|
},
|
|
10269
|
+
{
|
|
10270
|
+
"name": "document_version",
|
|
10271
|
+
"arguments": "p_document_id uuid",
|
|
10272
|
+
"anon": [],
|
|
10273
|
+
"authenticated": [
|
|
10274
|
+
"execute"
|
|
10275
|
+
],
|
|
10276
|
+
"service_role": [
|
|
10277
|
+
"execute"
|
|
10278
|
+
]
|
|
10279
|
+
},
|
|
10154
10280
|
{
|
|
10155
10281
|
"name": "documents_allocate_included_tax",
|
|
10156
10282
|
"arguments": "p_document_id uuid",
|
|
@@ -10194,6 +10320,14 @@
|
|
|
10194
10320
|
"authenticated": [],
|
|
10195
10321
|
"service_role": []
|
|
10196
10322
|
},
|
|
10323
|
+
{
|
|
10324
|
+
"name": "documents_posting_mode",
|
|
10325
|
+
"arguments": "",
|
|
10326
|
+
"trigger": true,
|
|
10327
|
+
"anon": [],
|
|
10328
|
+
"authenticated": [],
|
|
10329
|
+
"service_role": []
|
|
10330
|
+
},
|
|
10197
10331
|
{
|
|
10198
10332
|
"name": "documents_refresh_amount_paid",
|
|
10199
10333
|
"arguments": "p_document_id uuid",
|
|
@@ -11761,6 +11895,17 @@
|
|
|
11761
11895
|
"execute"
|
|
11762
11896
|
]
|
|
11763
11897
|
},
|
|
11898
|
+
{
|
|
11899
|
+
"name": "replace_document_lines",
|
|
11900
|
+
"arguments": "p_document_id uuid, p_lines jsonb",
|
|
11901
|
+
"anon": [],
|
|
11902
|
+
"authenticated": [
|
|
11903
|
+
"execute"
|
|
11904
|
+
],
|
|
11905
|
+
"service_role": [
|
|
11906
|
+
"execute"
|
|
11907
|
+
]
|
|
11908
|
+
},
|
|
11764
11909
|
{
|
|
11765
11910
|
"name": "resolve_counterpart_account",
|
|
11766
11911
|
"arguments": "p_company_id uuid, p_contact_id uuid, p_is_sale boolean",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
-- Ekwo OS — a draft changes its lines in one statement.
|
|
2
|
+
--
|
|
3
|
+
-- Replacing the lines of a draft was two calls from the client: delete every
|
|
4
|
+
-- line, then insert the new ones. When the database refused the insert — a
|
|
5
|
+
-- discount of 100 % fails `document_lines_discount_range`, an account of
|
|
6
|
+
-- another company fails its foreign key — the delete had already happened,
|
|
7
|
+
-- and the draft was left with no line at all. Creating a document had the
|
|
8
|
+
-- same shape: the header, then its lines, so a refused line left an empty
|
|
9
|
+
-- draft behind.
|
|
10
|
+
--
|
|
11
|
+
-- Two functions make each of those one statement, and so one transaction:
|
|
12
|
+
-- either every line is written, or nothing changed.
|
|
13
|
+
--
|
|
14
|
+
-- replace_document_lines(document, lines) the lines of a draft, replaced
|
|
15
|
+
-- create_document(document, lines) a draft and its lines, created
|
|
16
|
+
--
|
|
17
|
+
-- Both are `security invoker`: the caller writes as themselves, and row level
|
|
18
|
+
-- security and the guards of the tables decide exactly as they did for the
|
|
19
|
+
-- two calls. A line is given as the client already wrote it — `name`,
|
|
20
|
+
-- `description`, `product_id`, `unit_code`, `unit_price`, `quantity`,
|
|
21
|
+
-- `discount_percent`, `account_id`, `tax_id`, `attributes` — and is numbered
|
|
22
|
+
-- 10, 20, 30… in the order given. What a line leaves out takes the default of
|
|
23
|
+
-- its column, and a null account is resolved by the trigger that always
|
|
24
|
+
-- resolved it.
|
|
25
|
+
|
|
26
|
+
create or replace function replace_document_lines(p_document_id uuid, p_lines jsonb)
|
|
27
|
+
returns integer
|
|
28
|
+
language plpgsql
|
|
29
|
+
security invoker
|
|
30
|
+
set search_path = public, pg_temp
|
|
31
|
+
as $$
|
|
32
|
+
declare
|
|
33
|
+
v_document documents%rowtype;
|
|
34
|
+
v_count integer;
|
|
35
|
+
begin
|
|
36
|
+
if p_lines is null or jsonb_typeof(p_lines) <> 'array' then
|
|
37
|
+
raise exception 'lines_not_an_array: the lines of a document are a JSON array'
|
|
38
|
+
using errcode = '22023';
|
|
39
|
+
end if;
|
|
40
|
+
|
|
41
|
+
select * into v_document from documents where id = p_document_id for update;
|
|
42
|
+
if not found then
|
|
43
|
+
raise exception 'unknown_document: no document % in the companies you can see', p_document_id
|
|
44
|
+
using errcode = 'P0002';
|
|
45
|
+
end if;
|
|
46
|
+
if v_document.state <> 'draft' then
|
|
47
|
+
raise exception 'document_not_draft: document % is %. A posted document is not edited; correct it with a credit note.',
|
|
48
|
+
p_document_id, v_document.state
|
|
49
|
+
using errcode = '55006';
|
|
50
|
+
end if;
|
|
51
|
+
|
|
52
|
+
delete from document_lines where document_id = p_document_id;
|
|
53
|
+
|
|
54
|
+
insert into document_lines (
|
|
55
|
+
document_id, company_id, sequence, line_type, product_id, name, description,
|
|
56
|
+
unit_code, unit_price, quantity, discount_percent, account_id, tax_id, attributes
|
|
57
|
+
)
|
|
58
|
+
select p_document_id, v_document.company_id, (l.ordinality * 10)::integer,
|
|
59
|
+
coalesce((l.line ->> 'line_type')::document_line_type, 'product'),
|
|
60
|
+
(l.line ->> 'product_id')::uuid,
|
|
61
|
+
l.line ->> 'name',
|
|
62
|
+
l.line ->> 'description',
|
|
63
|
+
coalesce(l.line ->> 'unit_code', 'C62'),
|
|
64
|
+
coalesce((l.line ->> 'unit_price')::numeric, 0),
|
|
65
|
+
coalesce((l.line ->> 'quantity')::numeric, 1),
|
|
66
|
+
coalesce((l.line ->> 'discount_percent')::numeric, 0),
|
|
67
|
+
(l.line ->> 'account_id')::uuid,
|
|
68
|
+
(l.line ->> 'tax_id')::uuid,
|
|
69
|
+
coalesce(l.line -> 'attributes', '{}'::jsonb)
|
|
70
|
+
from jsonb_array_elements(p_lines) with ordinality as l(line, ordinality);
|
|
71
|
+
|
|
72
|
+
get diagnostics v_count = row_count;
|
|
73
|
+
return v_count;
|
|
74
|
+
end;
|
|
75
|
+
$$;
|
|
76
|
+
|
|
77
|
+
comment on function replace_document_lines(uuid, jsonb) is
|
|
78
|
+
'Replaces every line of a draft with the lines given, in one statement: when the database refuses one of them, the draft keeps the lines it had. Security invoker, so row level security and the guards of document_lines decide as for a direct write.';
|
|
79
|
+
|
|
80
|
+
revoke execute on function replace_document_lines(uuid, jsonb) from public, anon;
|
|
81
|
+
grant execute on function replace_document_lines(uuid, jsonb) to authenticated, service_role;
|
|
82
|
+
|
|
83
|
+
create or replace function create_document(p_document jsonb, p_lines jsonb)
|
|
84
|
+
returns uuid
|
|
85
|
+
language plpgsql
|
|
86
|
+
security invoker
|
|
87
|
+
set search_path = public, pg_temp
|
|
88
|
+
as $$
|
|
89
|
+
declare
|
|
90
|
+
v_id uuid;
|
|
91
|
+
begin
|
|
92
|
+
if p_document is null or jsonb_typeof(p_document) <> 'object' then
|
|
93
|
+
raise exception 'document_not_an_object: the header of a document is a JSON object'
|
|
94
|
+
using errcode = '22023';
|
|
95
|
+
end if;
|
|
96
|
+
|
|
97
|
+
insert into documents (
|
|
98
|
+
company_id, doc_type, contact_id, document_date, due_date, accounting_date, number,
|
|
99
|
+
supplier_reference, currency_code, journal_id, payment_reference, client_ref
|
|
100
|
+
)
|
|
101
|
+
values (
|
|
102
|
+
(p_document ->> 'company_id')::uuid,
|
|
103
|
+
(p_document ->> 'doc_type')::doc_type,
|
|
104
|
+
(p_document ->> 'contact_id')::uuid,
|
|
105
|
+
(p_document ->> 'document_date')::date,
|
|
106
|
+
(p_document ->> 'due_date')::date,
|
|
107
|
+
(p_document ->> 'accounting_date')::date,
|
|
108
|
+
p_document ->> 'number',
|
|
109
|
+
p_document ->> 'supplier_reference',
|
|
110
|
+
p_document ->> 'currency_code',
|
|
111
|
+
(p_document ->> 'journal_id')::uuid,
|
|
112
|
+
p_document ->> 'payment_reference',
|
|
113
|
+
p_document ->> 'client_ref'
|
|
114
|
+
)
|
|
115
|
+
returning id into v_id;
|
|
116
|
+
|
|
117
|
+
perform replace_document_lines(v_id, p_lines);
|
|
118
|
+
return v_id;
|
|
119
|
+
end;
|
|
120
|
+
$$;
|
|
121
|
+
|
|
122
|
+
comment on function create_document(jsonb, jsonb) is
|
|
123
|
+
'Creates a draft and its lines in one statement: a refused line creates nothing, rather than an empty draft. The header names company_id, doc_type, contact_id, document_date and currency_code, and optionally due_date, accounting_date, number, supplier_reference, journal_id, payment_reference and client_ref. Security invoker.';
|
|
124
|
+
|
|
125
|
+
revoke execute on function create_document(jsonb, jsonb) from public, anon;
|
|
126
|
+
grant execute on function create_document(jsonb, jsonb) to authenticated, service_role;
|