@endora-commerce/mod-currencies 0.100.0
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/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/backend/entities/currency.entity.d.ts +22 -0
- package/dist/backend/entities/currency.entity.d.ts.map +1 -0
- package/dist/backend/entities/currency.entity.js +77 -0
- package/dist/backend/entities/currency.entity.js.map +1 -0
- package/dist/backend/index.d.ts +53 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +108 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.d.ts +36 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +43 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/services/currency-ports.d.ts +23 -0
- package/dist/backend/services/currency-ports.d.ts.map +1 -0
- package/dist/backend/services/currency-ports.js +63 -0
- package/dist/backend/services/currency-ports.js.map +1 -0
- package/dist/backend/services/currency-reference-registry.d.ts +30 -0
- package/dist/backend/services/currency-reference-registry.d.ts.map +1 -0
- package/dist/backend/services/currency-reference-registry.js +54 -0
- package/dist/backend/services/currency-reference-registry.js.map +1 -0
- package/dist/backend/services/currency-seed-service.d.ts +21 -0
- package/dist/backend/services/currency-seed-service.d.ts.map +1 -0
- package/dist/backend/services/currency-seed-service.js +41 -0
- package/dist/backend/services/currency-seed-service.js.map +1 -0
- package/dist/backend/services/currency-service.d.ts +94 -0
- package/dist/backend/services/currency-service.d.ts.map +1 -0
- package/dist/backend/services/currency-service.js +269 -0
- package/dist/backend/services/currency-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +83 -0
- package/dist/manifest.js.map +1 -0
- package/docs/currencies.md +59 -0
- package/package.json +62 -0
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { defineModuleManifest } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Currencies module — manifest backfill (Module Lifecycle, feature 018).
|
|
4
|
+
*
|
|
5
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
6
|
+
* required so the module participates in the registry. No install /
|
|
7
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
8
|
+
* platform-wide migrations.
|
|
9
|
+
*/
|
|
10
|
+
export const manifest = defineModuleManifest({
|
|
11
|
+
id: 'currencies',
|
|
12
|
+
docs: { dir: 'docs' },
|
|
13
|
+
name: 'Currencies',
|
|
14
|
+
description: 'Currency reference data and per-channel currency configuration.',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
// `auth` owns the `requireAdmin` port the admin routes added on 2026-08-29
|
|
17
|
+
// are gated by. It is the only entry, and the list stays otherwise empty for
|
|
18
|
+
// its original reason: `currencies` announces a change on the EventBus and
|
|
19
|
+
// `dictionaries` reacts by dropping its caches — a **notification**, not a
|
|
20
|
+
// query, which `contracts/module-context.md` says needs no dependency edge.
|
|
21
|
+
// Declaring one produced a real cycle (dictionaries → currencies →
|
|
22
|
+
// dictionaries) and the cycle was the design telling us the direction was
|
|
23
|
+
// wrong: a currency has no business knowing a dictionary cache exists.
|
|
24
|
+
dependencies: ['auth'],
|
|
25
|
+
/**
|
|
26
|
+
* The module's own authority (2026-08-29).
|
|
27
|
+
*
|
|
28
|
+
* `/api/v1/admin/currencies`, `/:code`, `/:code/default` and the delete were
|
|
29
|
+
* registered by `languages` and enforced `catalog:write` — all four, the list
|
|
30
|
+
* read included. So whoever could edit a product description could add a
|
|
31
|
+
* currency, deactivate one, delete one and **promote one to the shop's
|
|
32
|
+
* default**, which is the denomination every price, cart, order and invoice
|
|
33
|
+
* is stated in.
|
|
34
|
+
*
|
|
35
|
+
* Nothing could see it. `catalog:write` is real, declared and enforced, so
|
|
36
|
+
* the permission inventory's two directions were clean; D-173's `foreign-gate`
|
|
37
|
+
* sweep passes the site deliberately, because `catalog` is `nonDeactivatable`
|
|
38
|
+
* and the availability coupling that sweep asks about can never bite; and
|
|
39
|
+
* `check:action-route-permissions` never looked at all, neither this module
|
|
40
|
+
* nor `languages` declaring a manifest action. None of them asks whether
|
|
41
|
+
* `catalog:write` is the right authority for changing what a shop charges in,
|
|
42
|
+
* which is a judgement rather than a derivation, so the answer is pinned in a
|
|
43
|
+
* contract test rather than in a new check.
|
|
44
|
+
*
|
|
45
|
+
* A pair and no third code, spelled `<module id>:<read|write>` like
|
|
46
|
+
* `payment_methods`, `delivery_methods`, `taxes`, `returns` and `invoices`. A
|
|
47
|
+
* prefix that is not its owner's id is the mistake `PERMISSION_CATALOGUE`
|
|
48
|
+
* comments on twice (`integrations:manage`, `audit_log:read`), both frozen
|
|
49
|
+
* because they are persisted in role rows; getting it right on a code that
|
|
50
|
+
* does not exist yet is free.
|
|
51
|
+
*
|
|
52
|
+
* The read half is a capability that did not exist rather than a rename of
|
|
53
|
+
* one: every one of the four routes carried the write code, so listing the
|
|
54
|
+
* currencies required the authority to delete one.
|
|
55
|
+
*
|
|
56
|
+
* The **other** door to this table is `dictionaries`'
|
|
57
|
+
* `/api/v1/admin/dictionary/currencies/*`, gated on `dictionary.write`. It is
|
|
58
|
+
* out of this repair's scope and is reported rather than changed: that is the
|
|
59
|
+
* screen an operator actually uses, it is a code `dictionaries` owns rather
|
|
60
|
+
* than a borrowed one, and re-gating it is the same question asked of the
|
|
61
|
+
* whole reference-data registry (countries and languages alike) rather than
|
|
62
|
+
* of currencies.
|
|
63
|
+
*
|
|
64
|
+
* No data migration: see `test/contract/currencies/permission-authority.test.ts`.
|
|
65
|
+
*/
|
|
66
|
+
permissions: [
|
|
67
|
+
{ code: 'currencies:read', label: 'View currencies' },
|
|
68
|
+
{ code: 'currencies:write', label: 'Configure currencies' },
|
|
69
|
+
],
|
|
70
|
+
// Feature 074 (Constitution XVII), test C3 — platform primitive. The
|
|
71
|
+
// declaration used to carry the manifest graph as its second half ("and
|
|
72
|
+
// `dictionaries` declares this module, two edges from the tenancy root");
|
|
73
|
+
// ruling 2 withdraws that, and the business half was always the real one and
|
|
74
|
+
// stands alone. Denomination is not a business decision anyone takes: there
|
|
75
|
+
// is no price, cart, order or invoice without a currency, and no client for
|
|
76
|
+
// whom "no currencies" is a smaller platform rather than a broken one.
|
|
77
|
+
activation: {
|
|
78
|
+
nonDeactivatable: true,
|
|
79
|
+
reason: 'Every amount is denominated; there is no price, cart, order or invoice without a ' +
|
|
80
|
+
'currency.',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,iEAAiE;IACnE,OAAO,EAAE,OAAO;IAChB,2EAA2E;IAC3E,6EAA6E;IAC7E,2EAA2E;IAC3E,2EAA2E;IAC3E,4EAA4E;IAC5E,mEAAmE;IACnE,0EAA0E;IAC1E,uEAAuE;IACvE,YAAY,EAAE,CAAC,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,WAAW,EAAE;QACX,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE;QACrD,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,sBAAsB,EAAE;KAC5D;IACD,qEAAqE;IACrE,wEAAwE;IACxE,0EAA0E;IAC1E,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,uEAAuE;IACvE,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,mFAAmF;YACnF,WAAW;KACd;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: currencies
|
|
3
|
+
description: Pool of accepted ISO 4217 currencies + default
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `currencies`
|
|
7
|
+
|
|
8
|
+
The installation-wide pool of accepted ISO 4217 currency codes. Mirrors the
|
|
9
|
+
`languages` module.
|
|
10
|
+
|
|
11
|
+
## Public surface
|
|
12
|
+
|
|
13
|
+
| Verb + Path | Permission | Purpose |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `GET /api/v1/admin/currencies` | `currencies:read` | Full list |
|
|
16
|
+
| `PUT /api/v1/admin/currencies/:code` | `currencies:write` | Upsert |
|
|
17
|
+
| `POST /api/v1/admin/currencies/:code/default` | `currencies:write` | Promote to default (atomically demotes the prior default) |
|
|
18
|
+
| `DELETE /api/v1/admin/currencies/:code` | `currencies:write` | Remove (rejected for the default) |
|
|
19
|
+
|
|
20
|
+
`GET /api/v1/i18n/config` also answers with the active currencies and the
|
|
21
|
+
default, and is **`languages`'** route rather than this module's: it composes
|
|
22
|
+
both catalogues into one public payload and reads this module's half over
|
|
23
|
+
`currencyReadPort`. See [languages](./languages.md).
|
|
24
|
+
|
|
25
|
+
## Permissions
|
|
26
|
+
|
|
27
|
+
`currencies:read` and `currencies:write`, this module's own since 2026-08-29.
|
|
28
|
+
|
|
29
|
+
The four routes above lived in `languages` until then and enforced
|
|
30
|
+
`catalog:write` — all four, the list read included — so whoever could edit a
|
|
31
|
+
product description could add a currency, deactivate one, delete one and
|
|
32
|
+
promote one to the shop's default. Nothing in this repository calls these
|
|
33
|
+
routes: the admin currency screen is `dictionaries`'
|
|
34
|
+
(`/api/v1/admin/dictionary/currencies/*`, gated on `dictionary.write`), which is
|
|
35
|
+
a second door to this table and a separate question.
|
|
36
|
+
|
|
37
|
+
`test/contract/currencies/permission-authority.test.ts` pins both directions —
|
|
38
|
+
a role holding the catalogue codes is refused, a role holding the pair is
|
|
39
|
+
served — and asserts that the in-process `currencyReadPort` every other module
|
|
40
|
+
reads a currency through is untouched by the move.
|
|
41
|
+
|
|
42
|
+
## Defaults
|
|
43
|
+
|
|
44
|
+
Same partial-unique-index pattern as `languages`. The migration bootstrap
|
|
45
|
+
seeds `PLN` (default) and `EUR`; symbol values are stored via Postgres
|
|
46
|
+
Unicode literals so the migration source stays ASCII.
|
|
47
|
+
|
|
48
|
+
## Entities
|
|
49
|
+
|
|
50
|
+
`Currency` — natural primary key on the ISO 4217 code; `label`,
|
|
51
|
+
`symbol`, `isDefault`, `isActive`, `sortOrder`.
|
|
52
|
+
|
|
53
|
+
## Extension points
|
|
54
|
+
|
|
55
|
+
- **Per-Sales-Channel default** — when a Sales Channel ships its own
|
|
56
|
+
currency, gate `priceFor(cart)` in the orders module on the channel's
|
|
57
|
+
configured currency rather than the global default.
|
|
58
|
+
- **Exchange-rate provider** — out of scope here; integrate via the
|
|
59
|
+
`integrations` module when a real adapter is needed.
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@endora-commerce/mod-currencies",
|
|
3
|
+
"version": "0.100.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "Currency reference data and per-channel currency configuration.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"endora": {
|
|
9
|
+
"type": "module",
|
|
10
|
+
"id": "currencies"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/endora-commerce/endora-commerce.git",
|
|
15
|
+
"directory": "packages/modules/currencies"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/manifest.d.ts",
|
|
23
|
+
"default": "./dist/manifest.js"
|
|
24
|
+
},
|
|
25
|
+
"./backend": {
|
|
26
|
+
"types": "./dist/backend/index.d.ts",
|
|
27
|
+
"default": "./dist/backend/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"docs"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22.18.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@mikro-orm/core": "^6",
|
|
40
|
+
"@mikro-orm/postgresql": "^6",
|
|
41
|
+
"fastify": "^5",
|
|
42
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
43
|
+
"@endora-commerce/platform": "0.100.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@fastify/type-provider-zod": "^1.0.0",
|
|
47
|
+
"@mikro-orm/core": "^6.6.13",
|
|
48
|
+
"@mikro-orm/postgresql": "^6.6.13",
|
|
49
|
+
"@types/node": "^22.9.0",
|
|
50
|
+
"fastify": "^5.12.5",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vitest": "^4.1.11",
|
|
53
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
54
|
+
"@endora-commerce/platform": "0.100.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc -p tsconfig.build.json",
|
|
58
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
59
|
+
"lint": "eslint src",
|
|
60
|
+
"test": "vitest run"
|
|
61
|
+
}
|
|
62
|
+
}
|