@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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/dist/backend/entities/currency.entity.d.ts +22 -0
  4. package/dist/backend/entities/currency.entity.d.ts.map +1 -0
  5. package/dist/backend/entities/currency.entity.js +77 -0
  6. package/dist/backend/entities/currency.entity.js.map +1 -0
  7. package/dist/backend/index.d.ts +53 -0
  8. package/dist/backend/index.d.ts.map +1 -0
  9. package/dist/backend/index.js +108 -0
  10. package/dist/backend/index.js.map +1 -0
  11. package/dist/backend/routes.d.ts +36 -0
  12. package/dist/backend/routes.d.ts.map +1 -0
  13. package/dist/backend/routes.js +43 -0
  14. package/dist/backend/routes.js.map +1 -0
  15. package/dist/backend/services/currency-ports.d.ts +23 -0
  16. package/dist/backend/services/currency-ports.d.ts.map +1 -0
  17. package/dist/backend/services/currency-ports.js +63 -0
  18. package/dist/backend/services/currency-ports.js.map +1 -0
  19. package/dist/backend/services/currency-reference-registry.d.ts +30 -0
  20. package/dist/backend/services/currency-reference-registry.d.ts.map +1 -0
  21. package/dist/backend/services/currency-reference-registry.js +54 -0
  22. package/dist/backend/services/currency-reference-registry.js.map +1 -0
  23. package/dist/backend/services/currency-seed-service.d.ts +21 -0
  24. package/dist/backend/services/currency-seed-service.d.ts.map +1 -0
  25. package/dist/backend/services/currency-seed-service.js +41 -0
  26. package/dist/backend/services/currency-seed-service.js.map +1 -0
  27. package/dist/backend/services/currency-service.d.ts +94 -0
  28. package/dist/backend/services/currency-service.d.ts.map +1 -0
  29. package/dist/backend/services/currency-service.js +269 -0
  30. package/dist/backend/services/currency-service.js.map +1 -0
  31. package/dist/manifest.d.ts +169 -0
  32. package/dist/manifest.d.ts.map +1 -0
  33. package/dist/manifest.js +83 -0
  34. package/dist/manifest.js.map +1 -0
  35. package/docs/currencies.md +59 -0
  36. package/package.json +62 -0
@@ -0,0 +1,21 @@
1
+ import type { CurrencySeedPort, CurrencySeedRow } from '@endora-commerce/contracts';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ /**
4
+ * The seeding face of `currencies` (feature 077, D-87 drain).
5
+ *
6
+ * `dictionaries` owns the catalogue — it exists so that
7
+ * `countries.default_currency_code` is satisfiable on a fresh install — and
8
+ * this module owns the table. Before this port the reconciler wrote the table
9
+ * directly, with an `insert into "currencies"` that named another module's
10
+ * table in a string no import-level check can see.
11
+ *
12
+ * Raw SQL on **this module's own** table, deliberately, and the same shape
13
+ * `blog`'s default-category seed uses: a boot seed that went through the admin
14
+ * write path would record 53 audit rows for something no operator did.
15
+ */
16
+ export declare class CurrencySeedService implements CurrencySeedPort {
17
+ private readonly emFactory;
18
+ constructor(emFactory: () => EntityManager);
19
+ ensureSeeded(rows: readonly CurrencySeedRow[]): Promise<number>;
20
+ }
21
+ //# sourceMappingURL=currency-seed-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-seed-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/currency-seed-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,YAAY,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA0BtE"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The seeding face of `currencies` (feature 077, D-87 drain).
3
+ *
4
+ * `dictionaries` owns the catalogue — it exists so that
5
+ * `countries.default_currency_code` is satisfiable on a fresh install — and
6
+ * this module owns the table. Before this port the reconciler wrote the table
7
+ * directly, with an `insert into "currencies"` that named another module's
8
+ * table in a string no import-level check can see.
9
+ *
10
+ * Raw SQL on **this module's own** table, deliberately, and the same shape
11
+ * `blog`'s default-category seed uses: a boot seed that went through the admin
12
+ * write path would record 53 audit rows for something no operator did.
13
+ */
14
+ export class CurrencySeedService {
15
+ emFactory;
16
+ constructor(emFactory) {
17
+ this.emFactory = emFactory;
18
+ }
19
+ async ensureSeeded(rows) {
20
+ const em = this.emFactory();
21
+ const existing = (await em.execute('select "code" from "currencies"'));
22
+ const present = new Set(existing.map((row) => row.code));
23
+ let inserted = 0;
24
+ for (const row of rows) {
25
+ if (present.has(row.code))
26
+ continue;
27
+ // command-coverage-ignore: idempotent lifecycle reconciler/seed — a system-
28
+ // owned insert of shipped reference data with no actor behind it. It runs
29
+ // at boot, inserts only codes that are absent and never touches a row an
30
+ // operator has edited, so there is nothing to audit and nothing to undo.
31
+ await em.execute(`insert into "currencies"
32
+ ("code","label","symbol","symbol_position","decimal_places",
33
+ "is_default","is_active","sort_order","created_at","updated_at")
34
+ values (?,?,?,?,?,false,true,0,now(),now())`, [row.code, row.label, row.symbol, row.symbolPosition ?? 'suffix', row.decimalPlaces ?? 2]);
35
+ present.add(row.code);
36
+ inserted += 1;
37
+ }
38
+ return inserted;
39
+ }
40
+ }
41
+ //# sourceMappingURL=currency-seed-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-seed-service.js","sourceRoot":"","sources":["../../../src/backend/services/currency-seed-service.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,mBAAmB;IACD;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D,KAAK,CAAC,YAAY,CAAC,IAAgC;QACjD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAEnE,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAEzD,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YACpC,4EAA4E;YAC5E,0EAA0E;YAC1E,yEAAyE;YACzE,yEAAyE;YACzE,MAAM,EAAE,CAAC,OAAO,CACd;;;qDAG6C,EAC7C,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,IAAI,QAAQ,EAAE,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC,CAC1F,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACtB,QAAQ,IAAI,CAAC,CAAC;QAChB,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
@@ -0,0 +1,94 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type DictionaryReference, type DictionaryReferenceRegistryPort } from '@endora-commerce/contracts';
3
+ import { Currency } from '../entities/currency.entity.js';
4
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
5
+ /**
6
+ * CurrencyService — admin CRUD over the currencies pool.
7
+ *
8
+ * Originally introduced by feature 004 (T238 / FR-105) for the minimal
9
+ * currencies table. Extended by feature 017 to cover the new columns
10
+ * (`symbolPosition`, `decimalPlaces`) and the spec invariants:
11
+ *
12
+ * - exactly one default at a time (partial unique index, transactional
13
+ * promote+demote).
14
+ * - at-least-one-active (the only active row cannot be deactivated).
15
+ * - default-cannot-be-deactivated.
16
+ * - FK-protected hard-delete: refused while any consumer references the
17
+ * currency (sales channels' default_currency + currencies JSONB,
18
+ * promotions, price_lists).
19
+ */
20
+ export declare class CurrencyService {
21
+ #private;
22
+ private readonly emFactory;
23
+ private readonly invalidateDictionaryCache?;
24
+ private readonly auditLog?;
25
+ /**
26
+ * Resolved per call rather than captured: the registry is a singleton this
27
+ * module owns, but the accessor keeps the constructor honest for the tests
28
+ * that build the service without one.
29
+ */
30
+ private readonly referenceRegistry?;
31
+ constructor(emFactory: () => EntityManager, invalidateDictionaryCache?: (() => Promise<void>) | undefined, auditLog?: AuditPort | undefined,
32
+ /**
33
+ * Resolved per call rather than captured: the registry is a singleton this
34
+ * module owns, but the accessor keeps the constructor honest for the tests
35
+ * that build the service without one.
36
+ */
37
+ referenceRegistry?: (() => DictionaryReferenceRegistryPort) | undefined);
38
+ list(): Promise<Currency[]>;
39
+ listActive(): Promise<Currency[]>;
40
+ getDefault(): Promise<Currency | null>;
41
+ getByCode(code: string): Promise<Currency | null>;
42
+ create(input: {
43
+ code: string;
44
+ label: string;
45
+ symbol: string;
46
+ symbolPosition?: 'prefix' | 'suffix';
47
+ decimalPlaces?: number;
48
+ isActive?: boolean;
49
+ sortOrder?: number;
50
+ }): Promise<Currency>;
51
+ /**
52
+ * Legacy upsert kept for `/api/v1/admin/currencies/:code` backward compat.
53
+ * The dictionary admin surface uses `update()` for the extended fields.
54
+ */
55
+ upsert(input: {
56
+ code: string;
57
+ label: string;
58
+ symbol: string;
59
+ isActive?: boolean;
60
+ sortOrder?: number;
61
+ }): Promise<Currency>;
62
+ /**
63
+ * Extended update used by the Dictionary admin surface (feature 017).
64
+ * Supports every column on the Currency entity and enforces the spec
65
+ * invariants.
66
+ */
67
+ update(code: string, input: {
68
+ label?: string;
69
+ symbol?: string;
70
+ symbolPosition?: 'prefix' | 'suffix';
71
+ decimalPlaces?: number;
72
+ isActive?: boolean;
73
+ sortOrder?: number;
74
+ }): Promise<Currency>;
75
+ setDefault(code: string): Promise<Currency>;
76
+ /**
77
+ * Every consumer reference to `code`, across the platform (feature 077, D-87).
78
+ *
79
+ * This used to be five hand-written `count(*)` statements naming
80
+ * `sales_channels`, `promotions`, `price_lists` and `countries` — four tables
81
+ * this module does not own, in strings no import-level boundary check can
82
+ * see. Three of them are contributed descriptors now
83
+ * (`currencyReferenceRegistry`); the fourth is the kernel's channel table,
84
+ * which this module may read through the ORM because the kernel is not a
85
+ * module (D-32).
86
+ *
87
+ * Channels are counted in memory rather than in SQL: a deployment has tens of
88
+ * them, both questions are about the same rows, and `currencies` is a JSON
89
+ * array whose containment test was the reason the statement existed at all.
90
+ */
91
+ countDependents(code: string): Promise<DictionaryReference[]>;
92
+ remove(code: string): Promise<void>;
93
+ }
94
+ //# sourceMappingURL=currency-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/currency-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,+BAA+B,EACrC,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAElE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAe;;IAExB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;gBARlB,SAAS,EAAE,MAAM,aAAa,EAC9B,yBAAyB,CAAC,GAAE,MAAM,OAAO,CAAC,IAAI,CAAC,aAAA,EAC/C,QAAQ,CAAC,EAAE,SAAS,YAAA;IACrC;;;;OAIG;IACc,iBAAiB,CAAC,GAAE,MAAM,+BAA+B,aAAA;IAqBtE,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAK3B,UAAU,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAOjC,UAAU,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAKtC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAKjD,MAAM,CAAC,KAAK,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;QACrC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,QAAQ,CAAC;IA0BrB;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+CrB;;;;OAIG;IACG,MAAM,CACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;QACrC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GACA,OAAO,CAAC,QAAQ,CAAC;IAuDd,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA0BjD;;;;;;;;;;;;;;OAcG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAmC7D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA+B1C"}
@@ -0,0 +1,269 @@
1
+ import { ERROR_CODES, } from '@endora-commerce/contracts';
2
+ import { SalesChannel } from '@endora-commerce/platform/kernel';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { Currency } from '../entities/currency.entity.js';
5
+ import { recordAuditFromContext } from '@endora-commerce/platform/commands';
6
+ /**
7
+ * CurrencyService — admin CRUD over the currencies pool.
8
+ *
9
+ * Originally introduced by feature 004 (T238 / FR-105) for the minimal
10
+ * currencies table. Extended by feature 017 to cover the new columns
11
+ * (`symbolPosition`, `decimalPlaces`) and the spec invariants:
12
+ *
13
+ * - exactly one default at a time (partial unique index, transactional
14
+ * promote+demote).
15
+ * - at-least-one-active (the only active row cannot be deactivated).
16
+ * - default-cannot-be-deactivated.
17
+ * - FK-protected hard-delete: refused while any consumer references the
18
+ * currency (sales channels' default_currency + currencies JSONB,
19
+ * promotions, price_lists).
20
+ */
21
+ export class CurrencyService {
22
+ emFactory;
23
+ invalidateDictionaryCache;
24
+ auditLog;
25
+ referenceRegistry;
26
+ constructor(emFactory, invalidateDictionaryCache, auditLog,
27
+ /**
28
+ * Resolved per call rather than captured: the registry is a singleton this
29
+ * module owns, but the accessor keeps the constructor honest for the tests
30
+ * that build the service without one.
31
+ */
32
+ referenceRegistry) {
33
+ this.emFactory = emFactory;
34
+ this.invalidateDictionaryCache = invalidateDictionaryCache;
35
+ this.auditLog = auditLog;
36
+ this.referenceRegistry = referenceRegistry;
37
+ }
38
+ #audit(em, action, objectId, stateBefore, stateAfter) {
39
+ if (this.auditLog) {
40
+ recordAuditFromContext(this.auditLog, em, {
41
+ action,
42
+ objectType: 'currency',
43
+ objectId,
44
+ stateBefore,
45
+ stateAfter,
46
+ });
47
+ }
48
+ }
49
+ async list() {
50
+ const em = this.emFactory();
51
+ return em.find(Currency, {}, { orderBy: { sortOrder: 'asc', code: 'asc' } });
52
+ }
53
+ async listActive() {
54
+ const em = this.emFactory();
55
+ // `code` tiebreak keeps the order deterministic when several currencies
56
+ // share a sortOrder (e.g. the seed default 0) — matches list() above.
57
+ return em.find(Currency, { isActive: true }, { orderBy: { sortOrder: 'asc', code: 'asc' } });
58
+ }
59
+ async getDefault() {
60
+ const em = this.emFactory();
61
+ return em.findOne(Currency, { isDefault: true });
62
+ }
63
+ async getByCode(code) {
64
+ const em = this.emFactory();
65
+ return em.findOne(Currency, { code });
66
+ }
67
+ async create(input) {
68
+ const em = this.emFactory();
69
+ const existing = await em.findOne(Currency, { code: input.code });
70
+ if (existing) {
71
+ throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, `Currency ${input.code} already exists.`);
72
+ }
73
+ const row = em.create(Currency, {
74
+ code: input.code,
75
+ label: input.label,
76
+ symbol: input.symbol,
77
+ ...(input.symbolPosition !== undefined ? { symbolPosition: input.symbolPosition } : {}),
78
+ ...(input.decimalPlaces !== undefined ? { decimalPlaces: input.decimalPlaces } : {}),
79
+ ...(input.isActive !== undefined ? { isActive: input.isActive } : {}),
80
+ ...(input.sortOrder !== undefined ? { sortOrder: input.sortOrder } : {}),
81
+ });
82
+ em.persist(row);
83
+ this.#audit(em, 'currency.create', row.code, null, { label: row.label });
84
+ await em.flush();
85
+ await this.invalidateDictionaryCache?.();
86
+ return row;
87
+ }
88
+ /**
89
+ * Legacy upsert kept for `/api/v1/admin/currencies/:code` backward compat.
90
+ * The dictionary admin surface uses `update()` for the extended fields.
91
+ */
92
+ async upsert(input) {
93
+ const em = this.emFactory();
94
+ const existing = await em.findOne(Currency, { code: input.code });
95
+ if (existing) {
96
+ if (input.isActive === false && existing.isDefault) {
97
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED, 'Cannot deactivate the default currency. Promote a different currency first.');
98
+ }
99
+ if (input.isActive === false && existing.isActive) {
100
+ const otherActive = await em.count(Currency, {
101
+ isActive: true,
102
+ code: { $ne: input.code },
103
+ });
104
+ if (otherActive === 0) {
105
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_LAST_ACTIVE_ENTRY, 'At least one currency must remain active.');
106
+ }
107
+ }
108
+ existing.label = input.label;
109
+ existing.symbol = input.symbol;
110
+ if (input.isActive !== undefined)
111
+ existing.isActive = input.isActive;
112
+ if (input.sortOrder !== undefined)
113
+ existing.sortOrder = input.sortOrder;
114
+ this.#audit(em, 'currency.upsert', existing.code, null, { label: existing.label });
115
+ await em.flush();
116
+ await this.invalidateDictionaryCache?.();
117
+ return existing;
118
+ }
119
+ const row = em.create(Currency, {
120
+ code: input.code,
121
+ label: input.label,
122
+ symbol: input.symbol,
123
+ ...(input.isActive !== undefined ? { isActive: input.isActive } : {}),
124
+ ...(input.sortOrder !== undefined ? { sortOrder: input.sortOrder } : {}),
125
+ });
126
+ em.persist(row);
127
+ this.#audit(em, 'currency.upsert', row.code, null, { label: row.label });
128
+ await em.flush();
129
+ await this.invalidateDictionaryCache?.();
130
+ return row;
131
+ }
132
+ /**
133
+ * Extended update used by the Dictionary admin surface (feature 017).
134
+ * Supports every column on the Currency entity and enforces the spec
135
+ * invariants.
136
+ */
137
+ async update(code, input) {
138
+ const em = this.emFactory();
139
+ const existing = await em.findOne(Currency, { code });
140
+ if (!existing) {
141
+ throw new HttpError(404, ERROR_CODES.DICTIONARY_ENTRY_NOT_FOUND, `Currency ${code} not found.`);
142
+ }
143
+ if (input.isActive === false && existing.isDefault) {
144
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED, 'Cannot deactivate the default currency. Promote a different currency first.');
145
+ }
146
+ if (input.isActive === false && existing.isActive) {
147
+ const otherActive = await em.count(Currency, {
148
+ isActive: true,
149
+ code: { $ne: code },
150
+ });
151
+ if (otherActive === 0) {
152
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_LAST_ACTIVE_ENTRY, 'At least one currency must remain active.');
153
+ }
154
+ }
155
+ if (input.decimalPlaces !== undefined) {
156
+ if (input.decimalPlaces < 0 || input.decimalPlaces > 6) {
157
+ throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'decimalPlaces must be between 0 and 6.');
158
+ }
159
+ }
160
+ if (input.label !== undefined)
161
+ existing.label = input.label;
162
+ if (input.symbol !== undefined)
163
+ existing.symbol = input.symbol;
164
+ if (input.symbolPosition !== undefined)
165
+ existing.symbolPosition = input.symbolPosition;
166
+ if (input.decimalPlaces !== undefined)
167
+ existing.decimalPlaces = input.decimalPlaces;
168
+ if (input.isActive !== undefined)
169
+ existing.isActive = input.isActive;
170
+ if (input.sortOrder !== undefined)
171
+ existing.sortOrder = input.sortOrder;
172
+ this.#audit(em, 'currency.update', existing.code, null, { label: existing.label, isActive: existing.isActive });
173
+ await em.flush();
174
+ await this.invalidateDictionaryCache?.();
175
+ return existing;
176
+ }
177
+ async setDefault(code) {
178
+ const em = this.emFactory();
179
+ const target = await em.findOne(Currency, { code });
180
+ if (!target) {
181
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, `Currency ${code} not found.`);
182
+ }
183
+ if (!target.isActive) {
184
+ throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'Cannot mark an inactive currency as default.');
185
+ }
186
+ if (target.isDefault)
187
+ return target;
188
+ // Demote-then-promote inside the EM's current tx scope (see
189
+ // LanguageService.setDefault for rationale).
190
+ await em.nativeUpdate(Currency, { isDefault: true }, { isDefault: false });
191
+ await em.nativeUpdate(Currency, { code }, { isDefault: true });
192
+ target.isDefault = true;
193
+ this.#audit(em, 'currency.set_default', code, null, { isDefault: true });
194
+ await em.flush();
195
+ await this.invalidateDictionaryCache?.();
196
+ return target;
197
+ }
198
+ /**
199
+ * Every consumer reference to `code`, across the platform (feature 077, D-87).
200
+ *
201
+ * This used to be five hand-written `count(*)` statements naming
202
+ * `sales_channels`, `promotions`, `price_lists` and `countries` — four tables
203
+ * this module does not own, in strings no import-level boundary check can
204
+ * see. Three of them are contributed descriptors now
205
+ * (`currencyReferenceRegistry`); the fourth is the kernel's channel table,
206
+ * which this module may read through the ORM because the kernel is not a
207
+ * module (D-32).
208
+ *
209
+ * Channels are counted in memory rather than in SQL: a deployment has tens of
210
+ * them, both questions are about the same rows, and `currencies` is a JSON
211
+ * array whose containment test was the reason the statement existed at all.
212
+ */
213
+ async countDependents(code) {
214
+ const channels = await this.emFactory().find(SalesChannel, {});
215
+ const references = [];
216
+ const asDefault = channels.filter((channel) => channel.defaultCurrency === code).length;
217
+ if (asDefault > 0) {
218
+ references.push({
219
+ ownerModuleId: 'sales_channels',
220
+ consumer: 'sales_channels',
221
+ tableName: 'sales_channels',
222
+ columnName: 'default_currency',
223
+ code,
224
+ count: asDefault,
225
+ blocking: true,
226
+ });
227
+ }
228
+ const listed = channels.filter((channel) => channel.currencies.includes(code)).length;
229
+ if (listed > 0) {
230
+ references.push({
231
+ ownerModuleId: 'sales_channels',
232
+ consumer: 'sales_channels',
233
+ tableName: 'sales_channels',
234
+ columnName: 'currencies[]',
235
+ code,
236
+ count: listed,
237
+ blocking: true,
238
+ });
239
+ }
240
+ const registry = this.referenceRegistry?.();
241
+ if (registry)
242
+ references.push(...(await registry.countReferences(code)));
243
+ return references;
244
+ }
245
+ async remove(code) {
246
+ const em = this.emFactory();
247
+ const row = await em.findOne(Currency, { code });
248
+ if (!row)
249
+ return;
250
+ if (row.isDefault) {
251
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED, 'Cannot remove the default currency.');
252
+ }
253
+ const dependents = await this.countDependents(code);
254
+ // A non-blocking reference is still reported — `countries.default_currency_code`
255
+ // has `on delete set null`, so it is a column an operator is about to blank
256
+ // rather than a delete to refuse. Which references are which is the
257
+ // contributing module's call, carried on the descriptor.
258
+ const blocking = dependents
259
+ .filter((reference) => reference.blocking)
260
+ .reduce((total, reference) => total + reference.count, 0);
261
+ if (blocking > 0) {
262
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_ENTRY_HAS_DEPENDENTS, `Currency ${code} cannot be deleted because ${blocking} consumer reference(s) exist.`, [{ path: 'consumers', issue: JSON.stringify(dependents) }]);
263
+ }
264
+ this.#audit(em, 'currency.delete', row.code, { label: row.label }, null);
265
+ await em.removeAndFlush(row);
266
+ await this.invalidateDictionaryCache?.();
267
+ }
268
+ }
269
+ //# sourceMappingURL=currency-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-service.js","sourceRoot":"","sources":["../../../src/backend/services/currency-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Currencies module — manifest backfill (Module Lifecycle, feature 018).
3
+ *
4
+ * Predates the lifecycle system; this manifest is the static record
5
+ * required so the module participates in the registry. No install /
6
+ * uninstall hook today — the module's schema is owned by earlier
7
+ * platform-wide migrations.
8
+ */
9
+ export declare const manifest: {
10
+ id: string;
11
+ name: string;
12
+ version: string;
13
+ dependencies: string[];
14
+ description?: string | undefined;
15
+ acknowledgedDependencies?: {
16
+ moduleId: string;
17
+ port: string;
18
+ reason: string;
19
+ }[] | undefined;
20
+ nonBindingDependencies?: {
21
+ moduleId: string;
22
+ name: string;
23
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
24
+ reason: string;
25
+ whenAbsent?: string | undefined;
26
+ }[] | undefined;
27
+ activation?: {
28
+ settingCode: string;
29
+ default: boolean;
30
+ } | {
31
+ nonDeactivatable: true;
32
+ reason: string;
33
+ } | undefined;
34
+ settings?: {
35
+ moduleCode: string;
36
+ groups: {
37
+ code: string;
38
+ name: string;
39
+ salesChannelCodes?: string[] | undefined;
40
+ isSystemProtected?: boolean | undefined;
41
+ }[];
42
+ settings: {
43
+ code: string;
44
+ name: string;
45
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
46
+ defaultValue: unknown;
47
+ description?: string | undefined;
48
+ groupCode?: string | undefined;
49
+ previousDefaultValues?: unknown[] | undefined;
50
+ salesChannelCodes?: string[] | undefined;
51
+ enumOptions?: string[] | undefined;
52
+ configurationType?: string | undefined;
53
+ hidden?: boolean | undefined;
54
+ }[];
55
+ } | undefined;
56
+ i18n?: {
57
+ bundlesDir: string;
58
+ } | undefined;
59
+ docs?: false | {
60
+ dir: string;
61
+ } | undefined;
62
+ demo?: false | {
63
+ summary: string;
64
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
65
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
66
+ after?: readonly string[] | undefined;
67
+ package?: string | undefined;
68
+ } | undefined;
69
+ actions?: {
70
+ id: string;
71
+ labelKey: string;
72
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
73
+ targetRoute: string;
74
+ keywords: string[];
75
+ weight: number;
76
+ descriptionKey?: string | undefined;
77
+ requiredPermission?: string | undefined;
78
+ }[] | undefined;
79
+ permissions?: {
80
+ code: string;
81
+ label: string;
82
+ module?: string | undefined;
83
+ description?: string | undefined;
84
+ requires?: string[] | undefined;
85
+ }[] | undefined;
86
+ transactionalEmails?: {
87
+ code: string;
88
+ name: string;
89
+ variables: {
90
+ key: string;
91
+ label: string;
92
+ sampleValue?: string | undefined;
93
+ description?: string | undefined;
94
+ }[];
95
+ description?: string | undefined;
96
+ group?: string | undefined;
97
+ }[] | undefined;
98
+ capabilities?: string[] | undefined;
99
+ exclusiveCapabilities?: {
100
+ key: string;
101
+ errorCode: string;
102
+ }[] | undefined;
103
+ errorCodes?: {
104
+ code: string;
105
+ tokens?: string[] | undefined;
106
+ }[] | undefined;
107
+ blocks?: {
108
+ name: string;
109
+ labelKey: string;
110
+ category: string;
111
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
112
+ fields: Record<string, {
113
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
114
+ label?: string | undefined;
115
+ required?: boolean | undefined;
116
+ options?: {
117
+ label: string;
118
+ value: string | number;
119
+ }[] | undefined;
120
+ refKind?: string | undefined;
121
+ }>;
122
+ descriptionKey?: string | undefined;
123
+ defaultProps?: Record<string, unknown> | undefined;
124
+ responsiveFields?: string[] | undefined;
125
+ previewIcon?: string | undefined;
126
+ weight?: number | undefined;
127
+ }[] | undefined;
128
+ blockCategories?: {
129
+ key: string;
130
+ titleKey: string;
131
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
132
+ weight?: number | undefined;
133
+ visible?: boolean | undefined;
134
+ }[] | undefined;
135
+ env?: {
136
+ name: string;
137
+ describes: {
138
+ en: string;
139
+ pl: string;
140
+ };
141
+ requirement: {
142
+ kind: "required";
143
+ } | {
144
+ kind: "requiredWhen";
145
+ input: string;
146
+ equals: string;
147
+ } | {
148
+ kind: "optional";
149
+ without: {
150
+ en: string;
151
+ pl: string;
152
+ };
153
+ };
154
+ secret: boolean;
155
+ generable: boolean;
156
+ owner: {
157
+ kind: "platform";
158
+ } | {
159
+ kind: "application";
160
+ application: "admin" | "backend" | "storefront";
161
+ } | {
162
+ kind: "module";
163
+ moduleId: string;
164
+ };
165
+ consumers: ("admin" | "backend" | "storefront")[];
166
+ addressOf: "admin" | "backend" | "storefront" | null;
167
+ }[] | undefined;
168
+ };
169
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0EnB,CAAC"}