@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
|
@@ -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":"AACA,OAAO,EACL,WAAW,GAGZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAG5E;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,eAAe;IAEP;IACA;IACA;IAMA;IATnB,YACmB,SAA8B,EAC9B,yBAA+C,EAC/C,QAAoB;IACrC;;;;OAIG;IACc,iBAAyD;QARzD,cAAS,GAAT,SAAS,CAAqB;QAC9B,8BAAyB,GAAzB,yBAAyB,CAAsB;QAC/C,aAAQ,GAAR,QAAQ,CAAY;QAMpB,sBAAiB,GAAjB,iBAAiB,CAAwC;IACzE,CAAC;IAEJ,MAAM,CACJ,EAAiB,EACjB,MAAc,EACd,QAAgB,EAChB,WAA2C,EAC3C,UAA0C;QAE1C,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,sBAAsB,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE;gBACxC,MAAM;gBACN,UAAU,EAAE,UAAU;gBACtB,QAAQ;gBACR,WAAW;gBACX,UAAU;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,wEAAwE;QACxE,sEAAsE;QACtE,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY;QAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAQZ;QACC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,iBAAiB,EAC7B,YAAY,KAAK,CAAC,IAAI,kBAAkB,CACzC,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,KAAK,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,GAAG,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpF,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzE,CAAC,CAAC;QACH,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;QACzC,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,KAMZ;QACC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACnD,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,wCAAwC,EACpD,6EAA6E,CAC9E,CAAC;YACJ,CAAC;YACD,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBAClD,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE;oBAC3C,QAAQ,EAAE,IAAI;oBACd,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE;iBAC1B,CAAC,CAAC;gBACH,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;oBACtB,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,4BAA4B,EACxC,2CAA2C,CAC5C,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC7B,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC/B,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;gBAAE,QAAQ,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YACrE,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;gBAAE,QAAQ,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YACxE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;YACnF,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;YACzC,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzE,CAAC,CAAC;QACH,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;QACzC,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CACV,IAAY,EACZ,KAOC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,0BAA0B,EACtC,YAAY,IAAI,aAAa,CAC9B,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YACnD,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,wCAAwC,EACpD,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAClD,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE;gBAC3C,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE;aACpB,CAAC,CAAC;YACH,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,4BAA4B,EACxC,2CAA2C,CAC5C,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;gBACvD,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,iBAAiB,EAC7B,wCAAwC,CACzC,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/D,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;YAAE,QAAQ,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;QACvF,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;YAAE,QAAQ,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;QACpF,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;YAAE,QAAQ,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QACrE,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;YAAE,QAAQ,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAExE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChH,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,YAAY,IAAI,aAAa,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,iBAAiB,EAC7B,8CAA8C,CAC/C,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO,MAAM,CAAC;QAEpC,4DAA4D;QAC5D,6CAA6C;QAC7C,MAAM,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3E,MAAM,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;QACzC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC/D,MAAM,UAAU,GAA0B,EAAE,CAAC;QAE7C,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;QACxF,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC;gBACd,aAAa,EAAE,gBAAgB;gBAC/B,QAAQ,EAAE,gBAAgB;gBAC1B,SAAS,EAAE,gBAAgB;gBAC3B,UAAU,EAAE,kBAAkB;gBAC9B,IAAI;gBACJ,KAAK,EAAE,SAAS;gBAChB,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;QACtF,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,UAAU,CAAC,IAAI,CAAC;gBACd,aAAa,EAAE,gBAAgB;gBAC/B,QAAQ,EAAE,gBAAgB;gBAC1B,SAAS,EAAE,gBAAgB;gBAC3B,UAAU,EAAE,cAAc;gBAC1B,IAAI;gBACJ,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC5C,IAAI,QAAQ;YAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAClB,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,wCAAwC,EACpD,qCAAqC,CACtC,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACpD,iFAAiF;QACjF,4EAA4E;QAC5E,oEAAoE;QACpE,yDAAyD;QACzD,MAAM,QAAQ,GAAG,UAAU;aACxB,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC;aACzC,MAAM,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC5D,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,+BAA+B,EAC3C,YAAY,IAAI,8BAA8B,QAAQ,+BAA+B,EACrF,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAC3D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QACzE,MAAM,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;IAC3C,CAAC;CACF"}
|
|
@@ -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"}
|