@endora-commerce/mod-sales-channels 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 +67 -0
- package/dist/admin/api/sales-channels-client.d.ts +69 -0
- package/dist/admin/api/sales-channels-client.d.ts.map +1 -0
- package/dist/admin/api/sales-channels-client.js +74 -0
- package/dist/admin/api/sales-channels-client.js.map +1 -0
- package/dist/admin/components/ChannelIdentityForm.d.ts +24 -0
- package/dist/admin/components/ChannelIdentityForm.d.ts.map +1 -0
- package/dist/admin/components/ChannelIdentityForm.js +147 -0
- package/dist/admin/components/ChannelIdentityForm.js.map +1 -0
- package/dist/admin/components/DefaultChannelBadge.d.ts +21 -0
- package/dist/admin/components/DefaultChannelBadge.d.ts.map +1 -0
- package/dist/admin/components/DefaultChannelBadge.js +26 -0
- package/dist/admin/components/DefaultChannelBadge.js.map +1 -0
- package/dist/admin/components/EntityChannelMembership.d.ts +52 -0
- package/dist/admin/components/EntityChannelMembership.d.ts.map +1 -0
- package/dist/admin/components/EntityChannelMembership.js +98 -0
- package/dist/admin/components/EntityChannelMembership.js.map +1 -0
- package/dist/admin/index.d.ts +44 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +148 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/SalesChannelEditPage.d.ts +25 -0
- package/dist/admin/pages/SalesChannelEditPage.d.ts.map +1 -0
- package/dist/admin/pages/SalesChannelEditPage.js +185 -0
- package/dist/admin/pages/SalesChannelEditPage.js.map +1 -0
- package/dist/admin/pages/SalesChannelsListPage.d.ts +25 -0
- package/dist/admin/pages/SalesChannelsListPage.d.ts.map +1 -0
- package/dist/admin/pages/SalesChannelsListPage.js +91 -0
- package/dist/admin/pages/SalesChannelsListPage.js.map +1 -0
- package/dist/admin/zones/OrganizationChannelMembership.d.ts +34 -0
- package/dist/admin/zones/OrganizationChannelMembership.d.ts.map +1 -0
- package/dist/admin/zones/OrganizationChannelMembership.js +11 -0
- package/dist/admin/zones/OrganizationChannelMembership.js.map +1 -0
- package/dist/admin/zones/ProductChannelMembership.d.ts +23 -0
- package/dist/admin/zones/ProductChannelMembership.d.ts.map +1 -0
- package/dist/admin/zones/ProductChannelMembership.js +11 -0
- package/dist/admin/zones/ProductChannelMembership.js.map +1 -0
- package/dist/backend/commands/set-default.command.d.ts +53 -0
- package/dist/backend/commands/set-default.command.d.ts.map +1 -0
- package/dist/backend/commands/set-default.command.js +87 -0
- package/dist/backend/commands/set-default.command.js.map +1 -0
- package/dist/backend/index.d.ts +71 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +81 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +12 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +302 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +36 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +58 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/index.d.ts +2 -0
- package/dist/backend/services/index.d.ts.map +1 -0
- package/dist/backend/services/index.js +2 -0
- package/dist/backend/services/index.js.map +1 -0
- package/dist/backend/services/sales-channel-attribution-registry.d.ts +28 -0
- package/dist/backend/services/sales-channel-attribution-registry.d.ts.map +1 -0
- package/dist/backend/services/sales-channel-attribution-registry.js +50 -0
- package/dist/backend/services/sales-channel-attribution-registry.js.map +1 -0
- package/dist/backend/services/sales-channels.service.d.ts +247 -0
- package/dist/backend/services/sales-channels.service.d.ts.map +1 -0
- package/dist/backend/services/sales-channels.service.js +535 -0
- package/dist/backend/services/sales-channels.service.js.map +1 -0
- package/dist/manifest.d.ts +185 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +204 -0
- package/dist/manifest.js.map +1 -0
- package/docs/sales_channels/admin-usage.md +77 -0
- package/docs/sales_channels/developer-guide.md +107 -0
- package/docs/sales_channels/index.md +95 -0
- package/i18n/en.json +75 -0
- package/i18n/pl.json +75 -0
- package/package.json +88 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { UniqueConstraintViolationException } from '@mikro-orm/core';
|
|
2
|
+
import { DictionaryReferenceError, ERROR_CODES, SALES_CHANNEL_AUDIT_ACTIONS, dispatchValidatorMode, } from '@endora-commerce/contracts';
|
|
3
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
4
|
+
import { SalesChannel } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { makeSetSystemDefaultChannelCommand, } from '../commands/set-default.command.js';
|
|
6
|
+
export class SalesChannelsService {
|
|
7
|
+
emFactory;
|
|
8
|
+
eventBus;
|
|
9
|
+
dictionaryValidator;
|
|
10
|
+
attributionRegistry;
|
|
11
|
+
bridgeRegistry;
|
|
12
|
+
auditLogService;
|
|
13
|
+
cache;
|
|
14
|
+
commandBus;
|
|
15
|
+
constructor(emFactory, eventBus,
|
|
16
|
+
/**
|
|
17
|
+
* Required, and it was not (issue #251's shape, feature 075's occasion).
|
|
18
|
+
* Both roots have supplied it since feature 072 wave 2, so the absent
|
|
19
|
+
* branch was only ever run by tests — and what ran there was a hand-written
|
|
20
|
+
* `select "code" from "languages" / "currencies"`, two other modules' tables
|
|
21
|
+
* named in raw SQL, checking existence and not the active flag, and raising
|
|
22
|
+
* the 422 codes feature 017 superseded with the validator's uniform 409.
|
|
23
|
+
* Deleting that branch is the D-87 drain; making the argument required is
|
|
24
|
+
* what stops it coming back as an omission.
|
|
25
|
+
*/
|
|
26
|
+
dictionaryValidator,
|
|
27
|
+
/**
|
|
28
|
+
* "Who is still attributed to this channel?", asked of the modules that
|
|
29
|
+
* record it (feature 075, D-87). Required for the same reason: the absent
|
|
30
|
+
* form of a delete guard is an open one.
|
|
31
|
+
*/
|
|
32
|
+
attributionRegistry,
|
|
33
|
+
/**
|
|
34
|
+
* Which table holds each channel-scoped entity type's memberships, said by
|
|
35
|
+
* the module that owns that entity type (feature 120, FR-015).
|
|
36
|
+
*
|
|
37
|
+
* This class held its own copy of the platform's nine-entry map until
|
|
38
|
+
* D-226, justified in a comment as *"kept locally to avoid a circular
|
|
39
|
+
* import between the two services"*. That reason had been stale since
|
|
40
|
+
* feature 072's T019 moved `SalesChannelMembershipService` into the kernel:
|
|
41
|
+
* the platform names `packages/modules/sales_channels` in zero import
|
|
42
|
+
* specifiers and D-52/D-53 forbid it ever doing so. The copy was the worse
|
|
43
|
+
* of the two, because {@link findOrphansForChannel} runs a `select` against
|
|
44
|
+
* **all nine** tables on the channel-**delete** path, inside a module the
|
|
45
|
+
* platform refuses to switch off — so on an instance without `cms` deleting
|
|
46
|
+
* a channel failed on a relation that is not there.
|
|
47
|
+
*
|
|
48
|
+
* Resolved **through the container** rather than imported, by the name a
|
|
49
|
+
* composition root contributes off `composeSalesChannelsKernel`. A
|
|
50
|
+
* container resolution is not a module-graph edge, so there is nothing for
|
|
51
|
+
* a cycle to close; and the registry is a **kernel** registration rather
|
|
52
|
+
* than another module's port, so it puts nothing in this module's
|
|
53
|
+
* `dependencies` and makes no activation control a dead switch.
|
|
54
|
+
*/
|
|
55
|
+
bridgeRegistry, auditLogService,
|
|
56
|
+
/**
|
|
57
|
+
* The invalidating half of the channel cache, narrowed to what a writer
|
|
58
|
+
* needs (D-93). A service that can only invalidate cannot seed the cache
|
|
59
|
+
* from the write path, which is the reader's job.
|
|
60
|
+
*/
|
|
61
|
+
cache,
|
|
62
|
+
/**
|
|
63
|
+
* Used by `setDefault` only, which is a Command (Principle XIII) rather than
|
|
64
|
+
* a hand-audited write like the rest of this class. Still optional — and
|
|
65
|
+
* `setDefault` refuses outright when it is missing, so the absence can never
|
|
66
|
+
* become an unaudited flag move.
|
|
67
|
+
*/
|
|
68
|
+
commandBus) {
|
|
69
|
+
this.emFactory = emFactory;
|
|
70
|
+
this.eventBus = eventBus;
|
|
71
|
+
this.dictionaryValidator = dictionaryValidator;
|
|
72
|
+
this.attributionRegistry = attributionRegistry;
|
|
73
|
+
this.bridgeRegistry = bridgeRegistry;
|
|
74
|
+
this.auditLogService = auditLogService;
|
|
75
|
+
this.cache = cache;
|
|
76
|
+
this.commandBus = commandBus;
|
|
77
|
+
}
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
// Read paths
|
|
80
|
+
// -------------------------------------------------------------------------
|
|
81
|
+
async getByCode(code) {
|
|
82
|
+
const em = this.emFactory();
|
|
83
|
+
return em.findOne(SalesChannel, { code });
|
|
84
|
+
}
|
|
85
|
+
async getById(id) {
|
|
86
|
+
const em = this.emFactory();
|
|
87
|
+
return em.findOne(SalesChannel, { id });
|
|
88
|
+
}
|
|
89
|
+
async list(options = {}) {
|
|
90
|
+
const em = this.emFactory();
|
|
91
|
+
const page = Math.max(0, options.page ?? 0);
|
|
92
|
+
const pageSize = Math.min(Math.max(1, options.pageSize ?? 20), 100);
|
|
93
|
+
const where = {};
|
|
94
|
+
if (options.activeOnly)
|
|
95
|
+
where['active'] = true;
|
|
96
|
+
const [items, total] = await em.findAndCount(SalesChannel, where, {
|
|
97
|
+
orderBy: { createdAt: 'asc' },
|
|
98
|
+
offset: page * pageSize,
|
|
99
|
+
limit: pageSize,
|
|
100
|
+
});
|
|
101
|
+
return { items, page, pageSize, total };
|
|
102
|
+
}
|
|
103
|
+
// -------------------------------------------------------------------------
|
|
104
|
+
// Create
|
|
105
|
+
// -------------------------------------------------------------------------
|
|
106
|
+
async create(body, context) {
|
|
107
|
+
const em = this.emFactory();
|
|
108
|
+
await this.assertLanguagesValid(body.languages);
|
|
109
|
+
await this.validateLanguage(body.defaultLanguage, 'create-or-change', 'defaultLanguage');
|
|
110
|
+
await this.assertCurrenciesValid(body.currencies);
|
|
111
|
+
await this.validateCurrency(body.defaultCurrency, 'create-or-change', 'defaultCurrency');
|
|
112
|
+
const channel = em.create(SalesChannel, {
|
|
113
|
+
code: body.code,
|
|
114
|
+
name: body.name,
|
|
115
|
+
logoAssetId: body.logoAssetId ?? null,
|
|
116
|
+
themeCode: body.themeCode ?? null,
|
|
117
|
+
languages: body.languages,
|
|
118
|
+
defaultLanguage: body.defaultLanguage,
|
|
119
|
+
currencies: body.currencies,
|
|
120
|
+
defaultCurrency: body.defaultCurrency,
|
|
121
|
+
active: body.active ?? true,
|
|
122
|
+
systemDefault: false,
|
|
123
|
+
version: 1,
|
|
124
|
+
// Legacy columns preserved for one release (research R-12).
|
|
125
|
+
isPublic: false,
|
|
126
|
+
status: (body.active ?? true) ? 'active' : 'inactive',
|
|
127
|
+
});
|
|
128
|
+
try {
|
|
129
|
+
await em.persistAndFlush(channel);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
if (err instanceof UniqueConstraintViolationException) {
|
|
133
|
+
throw new HttpError(409, ERROR_CODES.DUPLICATE_SALES_CHANNEL_CODE, `Sales channel code "${body.code}" is already in use.`);
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
await this.audit('identity', context, channel.id, {
|
|
138
|
+
action: 'created',
|
|
139
|
+
after: this.snapshot(channel),
|
|
140
|
+
});
|
|
141
|
+
await this.emitIdentityChanged(channel.code);
|
|
142
|
+
return channel;
|
|
143
|
+
}
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// Update (FR-005 optimistic concurrency)
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
async update(code, body, expectedVersion, context) {
|
|
148
|
+
const em = this.emFactory();
|
|
149
|
+
const channel = await this.requireByCode(code);
|
|
150
|
+
if (channel.version !== expectedVersion) {
|
|
151
|
+
throw new HttpError(412, ERROR_CODES.STALE_SALES_CHANNEL_WRITE, `Sales channel "${channel.code}" was modified by another writer; ` +
|
|
152
|
+
`current version is ${channel.version}, your If-Match was ${expectedVersion}.`, [{ path: 'version', issue: String(channel.version) }]);
|
|
153
|
+
}
|
|
154
|
+
// D-50 — a channel's code is written once, at creation, and is immutable
|
|
155
|
+
// afterwards; on every channel, not only the system default. The code is an
|
|
156
|
+
// identity other systems hold: `SALES_CHANNEL_HOST_MAP` names it in
|
|
157
|
+
// deployment configuration, integrations pin it, and the channel cache is
|
|
158
|
+
// keyed by it — `emitIdentityChanged` carries only the new code, so a rename
|
|
159
|
+
// would leave the old key resolving until its TTL expired. The refusal here
|
|
160
|
+
// is what makes that leak unreachable rather than merely unused: the admin
|
|
161
|
+
// form has always locked the field, but this is the only writer of
|
|
162
|
+
// `channel.code` in the tree, so the API is where the guarantee has to hold.
|
|
163
|
+
// A mistyped code is fixed by creating the channel again under the right one
|
|
164
|
+
// and deleting the old — for the default channel, after `setDefault` has
|
|
165
|
+
// moved the flag off it.
|
|
166
|
+
//
|
|
167
|
+
// Checked before the dictionary validation below, so an attempted rename is
|
|
168
|
+
// refused for the reason that matters rather than for whatever the rest of
|
|
169
|
+
// the body happens to trip on.
|
|
170
|
+
if (body.code !== undefined && body.code !== channel.code) {
|
|
171
|
+
throw new HttpError(422, ERROR_CODES.SALES_CHANNEL_CODE_IMMUTABLE, `Sales channel "${channel.code}" cannot be renamed to "${body.code}": a channel code is ` +
|
|
172
|
+
`set when the channel is created and is immutable afterwards. Create a channel with ` +
|
|
173
|
+
`the right code and delete this one instead. The display name stays editable.`, [{ path: 'code', issue: body.code }]);
|
|
174
|
+
}
|
|
175
|
+
if (body.languages !== undefined) {
|
|
176
|
+
await this.assertLanguagesValid(body.languages, channel.languages);
|
|
177
|
+
}
|
|
178
|
+
if (body.defaultLanguage !== undefined) {
|
|
179
|
+
await this.validateLanguage(body.defaultLanguage, dispatchValidatorMode(channel.defaultLanguage, body.defaultLanguage), 'defaultLanguage');
|
|
180
|
+
}
|
|
181
|
+
if (body.currencies !== undefined) {
|
|
182
|
+
await this.assertCurrenciesValid(body.currencies, channel.currencies);
|
|
183
|
+
}
|
|
184
|
+
if (body.defaultCurrency !== undefined) {
|
|
185
|
+
await this.validateCurrency(body.defaultCurrency, dispatchValidatorMode(channel.defaultCurrency, body.defaultCurrency), 'defaultCurrency');
|
|
186
|
+
}
|
|
187
|
+
// Cross-field check: defaultLanguage ∈ effective languages,
|
|
188
|
+
// defaultCurrency ∈ effective currencies. The Zod schema already
|
|
189
|
+
// checks this when both fields are present in the body; this is the
|
|
190
|
+
// fallback when only one of the pair is patched.
|
|
191
|
+
const effectiveLanguages = body.languages ?? channel.languages;
|
|
192
|
+
const effectiveDefaultLang = body.defaultLanguage ?? channel.defaultLanguage;
|
|
193
|
+
if (!effectiveLanguages.includes(effectiveDefaultLang)) {
|
|
194
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'defaultLanguage must be one of languages.', [{ path: 'defaultLanguage', issue: effectiveDefaultLang }]);
|
|
195
|
+
}
|
|
196
|
+
const effectiveCurrencies = body.currencies ?? channel.currencies;
|
|
197
|
+
const effectiveDefaultCurrency = body.defaultCurrency ?? channel.defaultCurrency;
|
|
198
|
+
if (!effectiveCurrencies.includes(effectiveDefaultCurrency)) {
|
|
199
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'defaultCurrency must be one of currencies.', [{ path: 'defaultCurrency', issue: effectiveDefaultCurrency }]);
|
|
200
|
+
}
|
|
201
|
+
const before = this.snapshot(channel);
|
|
202
|
+
if (body.name !== undefined)
|
|
203
|
+
channel.name = body.name;
|
|
204
|
+
if (body.logoAssetId !== undefined)
|
|
205
|
+
channel.logoAssetId = body.logoAssetId;
|
|
206
|
+
if (body.themeCode !== undefined)
|
|
207
|
+
channel.themeCode = body.themeCode;
|
|
208
|
+
if (body.languages !== undefined)
|
|
209
|
+
channel.languages = body.languages;
|
|
210
|
+
if (body.defaultLanguage !== undefined)
|
|
211
|
+
channel.defaultLanguage = body.defaultLanguage;
|
|
212
|
+
if (body.currencies !== undefined)
|
|
213
|
+
channel.currencies = body.currencies;
|
|
214
|
+
if (body.defaultCurrency !== undefined)
|
|
215
|
+
channel.defaultCurrency = body.defaultCurrency;
|
|
216
|
+
if (body.active !== undefined) {
|
|
217
|
+
if (channel.systemDefault && body.active === false) {
|
|
218
|
+
throw new HttpError(422, ERROR_CODES.CANNOT_MODIFY_SYSTEM_DEFAULT, `Sales channel "${channel.code}" is the system default and cannot be deactivated.`);
|
|
219
|
+
}
|
|
220
|
+
channel.active = body.active;
|
|
221
|
+
channel.status = body.active ? 'active' : 'inactive';
|
|
222
|
+
}
|
|
223
|
+
channel.version += 1;
|
|
224
|
+
try {
|
|
225
|
+
await em.persistAndFlush(channel);
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
if (err instanceof UniqueConstraintViolationException) {
|
|
229
|
+
throw new HttpError(409, ERROR_CODES.DUPLICATE_SALES_CHANNEL_CODE, `Sales channel code "${body.code ?? channel.code}" is already in use.`);
|
|
230
|
+
}
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
await this.audit('identity', context, channel.id, {
|
|
234
|
+
action: 'updated',
|
|
235
|
+
before,
|
|
236
|
+
after: this.snapshot(channel),
|
|
237
|
+
});
|
|
238
|
+
await this.emitIdentityChanged(channel.code);
|
|
239
|
+
return channel;
|
|
240
|
+
}
|
|
241
|
+
// -------------------------------------------------------------------------
|
|
242
|
+
// Lifecycle
|
|
243
|
+
// -------------------------------------------------------------------------
|
|
244
|
+
async deactivate(code, context) {
|
|
245
|
+
const em = this.emFactory();
|
|
246
|
+
const channel = await this.requireByCode(code);
|
|
247
|
+
if (channel.systemDefault) {
|
|
248
|
+
throw new HttpError(422, ERROR_CODES.CANNOT_MODIFY_SYSTEM_DEFAULT, `Sales channel "${channel.code}" is the system default and cannot be deactivated.`);
|
|
249
|
+
}
|
|
250
|
+
if (!channel.active)
|
|
251
|
+
return channel; // idempotent
|
|
252
|
+
channel.active = false;
|
|
253
|
+
channel.status = 'inactive';
|
|
254
|
+
channel.version += 1;
|
|
255
|
+
await em.persistAndFlush(channel);
|
|
256
|
+
await this.audit('lifecycle', context, channel.id, {
|
|
257
|
+
action: 'deactivated',
|
|
258
|
+
channelCode: channel.code,
|
|
259
|
+
});
|
|
260
|
+
await this.emitLifecycleChanged(channel.code);
|
|
261
|
+
return channel;
|
|
262
|
+
}
|
|
263
|
+
async activate(code, context) {
|
|
264
|
+
const em = this.emFactory();
|
|
265
|
+
const channel = await this.requireByCode(code);
|
|
266
|
+
if (channel.active)
|
|
267
|
+
return channel; // idempotent
|
|
268
|
+
channel.active = true;
|
|
269
|
+
channel.status = 'active';
|
|
270
|
+
channel.version += 1;
|
|
271
|
+
await em.persistAndFlush(channel);
|
|
272
|
+
await this.audit('lifecycle', context, channel.id, {
|
|
273
|
+
action: 'activated',
|
|
274
|
+
channelCode: channel.code,
|
|
275
|
+
});
|
|
276
|
+
await this.emitLifecycleChanged(channel.code);
|
|
277
|
+
return channel;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Move the `system_default` flag to `code` — feature 072 / D-51.
|
|
281
|
+
*
|
|
282
|
+
* The write itself is a Command, so the audit row, the demote-then-promote
|
|
283
|
+
* ordering and the transaction all live in `commands/set-default.command.ts`.
|
|
284
|
+
* What belongs here is the 404 for a code nobody has, produced before the
|
|
285
|
+
* transaction so an operator's typo is not an aborted transaction, and the
|
|
286
|
+
* cache drop below.
|
|
287
|
+
*
|
|
288
|
+
* Unlike the rest of this class it writes no audit row of its own: the
|
|
289
|
+
* Command Bus writes exactly one, and a second would be the double-audit the
|
|
290
|
+
* coverage check exists to catch.
|
|
291
|
+
*/
|
|
292
|
+
async setDefault(code) {
|
|
293
|
+
const channel = await this.requireByCode(code);
|
|
294
|
+
if (!this.commandBus) {
|
|
295
|
+
throw new HttpError(500, ERROR_CODES.INTERNAL, 'Sales channels are composed without a Command Bus; the system default cannot be moved.');
|
|
296
|
+
}
|
|
297
|
+
const result = await this.commandBus.run(makeSetSystemDefaultChannelCommand(channel.id));
|
|
298
|
+
// D-93 — this is the flag move's drop, not a second one: the Command's
|
|
299
|
+
// `lifecycle_changed{invalidateAll}` event is now an announcement to other
|
|
300
|
+
// modules and invalidates nothing. Two rows changed and the cached value
|
|
301
|
+
// carries `systemDefault` inside it, so the demoted channel's entry is
|
|
302
|
+
// stale too; `invalidateAll` is the cheapest correct answer.
|
|
303
|
+
//
|
|
304
|
+
// It sits **here**, after `CommandBus.run` has returned, rather than inside
|
|
305
|
+
// the Command's `run` where it would precede the buffered event. The
|
|
306
|
+
// ordering the other writers get — drop, then announce — is worth less than
|
|
307
|
+
// the one this position buys: the drop happens after the transaction has
|
|
308
|
+
// committed, so a concurrent read cannot resolve the pre-commit row from
|
|
309
|
+
// Postgres and re-pin it into a cache that has just been emptied. That is
|
|
310
|
+
// the same "after the flush" rule `SettingsAdminService` states at its own
|
|
311
|
+
// write seam, and nothing reads this cache inside the Command.
|
|
312
|
+
if (result.changed)
|
|
313
|
+
await this.cache?.invalidateAllAfterWrite();
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
async delete(code, options = {}, context) {
|
|
317
|
+
const em = this.emFactory();
|
|
318
|
+
const channel = await this.requireByCode(code);
|
|
319
|
+
if (channel.systemDefault) {
|
|
320
|
+
throw new HttpError(422, ERROR_CODES.CANNOT_MODIFY_SYSTEM_DEFAULT, `Sales channel "${channel.code}" is the system default and cannot be deleted.`);
|
|
321
|
+
}
|
|
322
|
+
// FR-006: refuse hard-delete when historical attributions exist.
|
|
323
|
+
//
|
|
324
|
+
// Asked of the modules that record them rather than answered here (feature
|
|
325
|
+
// 075, D-87). This used to be one statement naming `orders` and
|
|
326
|
+
// `quote_requests` — two other modules' tables and two other modules'
|
|
327
|
+
// column names, invisible to the import-level boundary check because raw
|
|
328
|
+
// SQL names no specifier, and wrong the moment either owner changed how it
|
|
329
|
+
// stores the attribution. Each owner counts its own rows now and says what
|
|
330
|
+
// to call them; an owner that is switched off is still counted, which is
|
|
331
|
+
// this registry's stated policy and the only one the `on delete restrict`
|
|
332
|
+
// foreign key underneath agrees with.
|
|
333
|
+
const attributions = await this.attributionRegistry.countForChannel(channel.id);
|
|
334
|
+
if (attributions.length > 0) {
|
|
335
|
+
throw new HttpError(422, ERROR_CODES.SALES_CHANNEL_HAS_ATTRIBUTIONS, `Sales channel "${channel.code}" cannot be deleted while ` +
|
|
336
|
+
`${attributions.map((a) => `${a.count} ${a.consumer}`).join(' and ')} reference it. ` +
|
|
337
|
+
`Deactivate the channel instead.`, attributions.map((a) => ({ path: a.consumer, issue: String(a.count) })));
|
|
338
|
+
}
|
|
339
|
+
// FR-007: orphan check across every bridge. Returns the (entityType,entityId)
|
|
340
|
+
// pairs that would have zero memberships if this channel were removed.
|
|
341
|
+
const orphans = await this.findOrphansForChannel(em, channel.id);
|
|
342
|
+
if (orphans.length > 0 && !options.fallbackToDefault) {
|
|
343
|
+
throw new HttpError(422, ERROR_CODES.ENTITY_WOULD_HAVE_ZERO_CHANNELS, `Deleting sales channel "${channel.code}" would orphan ${orphans.length} channel-scoped ` +
|
|
344
|
+
`entity(ies). Pass fallbackToDefault=true to rebind them to the system default.`, orphans.slice(0, 10).map((o) => ({ path: o.entityType, issue: o.entityId })));
|
|
345
|
+
}
|
|
346
|
+
if (orphans.length > 0) {
|
|
347
|
+
const defaultId = await this.requireSystemDefaultId(em);
|
|
348
|
+
await this.rebindOrphansToDefault(em, defaultId, orphans);
|
|
349
|
+
}
|
|
350
|
+
await em.removeAndFlush(channel);
|
|
351
|
+
await this.audit('lifecycle', context, channel.id, {
|
|
352
|
+
action: 'deleted',
|
|
353
|
+
channelCode: channel.code,
|
|
354
|
+
rebindCount: orphans.length,
|
|
355
|
+
});
|
|
356
|
+
await this.emitLifecycleChanged(channel.code, /*invalidateAll=*/ true);
|
|
357
|
+
}
|
|
358
|
+
// -------------------------------------------------------------------------
|
|
359
|
+
// Helpers
|
|
360
|
+
// -------------------------------------------------------------------------
|
|
361
|
+
async requireByCode(code) {
|
|
362
|
+
const channel = await this.getByCode(code);
|
|
363
|
+
if (channel === null) {
|
|
364
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, `Sales channel "${code}" was not found.`);
|
|
365
|
+
}
|
|
366
|
+
return channel;
|
|
367
|
+
}
|
|
368
|
+
async assertLanguagesValid(codes, currentCodes = []) {
|
|
369
|
+
if (codes.length === 0) {
|
|
370
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'languages must not be empty.', [{ path: 'languages', issue: 'empty' }]);
|
|
371
|
+
}
|
|
372
|
+
const current = new Set(currentCodes);
|
|
373
|
+
for (const code of codes) {
|
|
374
|
+
await this.validateLanguage(code, current.has(code) ? 'unchanged' : 'create-or-change', 'languages');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async assertCurrenciesValid(codes, currentCodes = []) {
|
|
378
|
+
if (codes.length === 0) {
|
|
379
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'currencies must not be empty.', [{ path: 'currencies', issue: 'empty' }]);
|
|
380
|
+
}
|
|
381
|
+
const current = new Set(currentCodes);
|
|
382
|
+
for (const code of codes) {
|
|
383
|
+
await this.validateCurrency(code, current.has(code) ? 'unchanged' : 'create-or-change', 'currencies');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async validateLanguage(code, mode, field) {
|
|
387
|
+
try {
|
|
388
|
+
await this.dictionaryValidator.validateLanguageCode(code, mode);
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
if (err instanceof DictionaryReferenceError) {
|
|
392
|
+
throw dictionaryReferenceHttpError(err, field);
|
|
393
|
+
}
|
|
394
|
+
throw err;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async validateCurrency(code, mode, field) {
|
|
398
|
+
try {
|
|
399
|
+
await this.dictionaryValidator.validateCurrencyCode(code, mode);
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
if (err instanceof DictionaryReferenceError) {
|
|
403
|
+
throw dictionaryReferenceHttpError(err, field);
|
|
404
|
+
}
|
|
405
|
+
throw err;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Read through the kernel's own entity rather than the raw statement this
|
|
410
|
+
* replaces (feature 075, D-87). `sales_channels` is the kernel's table since
|
|
411
|
+
* feature 072 moved the resolution machinery there, and a module relating
|
|
412
|
+
* into the kernel by ORM is the sanctioned access — it was the SQL, not the
|
|
413
|
+
* read, that crossed a boundary nothing could see. It reads through the
|
|
414
|
+
* caller's `em` on purpose: this runs inside the delete path, so the
|
|
415
|
+
* resolver's own fork would not see that path's uncommitted state.
|
|
416
|
+
*/
|
|
417
|
+
async requireSystemDefaultId(em) {
|
|
418
|
+
const channel = await em.findOne(SalesChannel, { systemDefault: true });
|
|
419
|
+
if (channel === null) {
|
|
420
|
+
throw new HttpError(500, ERROR_CODES.INTERNAL, 'No system-default sales channel found; cannot apply fallback.');
|
|
421
|
+
}
|
|
422
|
+
return channel.id;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* For every M:N bridge, find the entities whose ONLY channel membership is
|
|
426
|
+
* `channelId`. Returned shape is the (entityType, entityId) pair so the
|
|
427
|
+
* delete path can either refuse with a sample list or rebind to Default.
|
|
428
|
+
*/
|
|
429
|
+
async findOrphansForChannel(em, channelId) {
|
|
430
|
+
const orphans = [];
|
|
431
|
+
for (const bridge of this.bridgeRegistry.list()) {
|
|
432
|
+
const rows = await em
|
|
433
|
+
.getConnection()
|
|
434
|
+
.execute(`select b."${bridge.entityIdColumn}" as entity_id ` +
|
|
435
|
+
`from "${bridge.table}" b ` +
|
|
436
|
+
`where b."sales_channel_id" = ? ` +
|
|
437
|
+
`and not exists ( ` +
|
|
438
|
+
` select 1 from "${bridge.table}" b2 ` +
|
|
439
|
+
` where b2."${bridge.entityIdColumn}" = b."${bridge.entityIdColumn}" ` +
|
|
440
|
+
` and b2."sales_channel_id" <> b."sales_channel_id" ` +
|
|
441
|
+
`)`, [channelId], 'all', em.getTransactionContext());
|
|
442
|
+
for (const r of rows) {
|
|
443
|
+
orphans.push({
|
|
444
|
+
entityType: bridge.entityType,
|
|
445
|
+
entityId: r.entity_id,
|
|
446
|
+
bridgeTable: bridge.table,
|
|
447
|
+
entityIdColumn: bridge.entityIdColumn,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return orphans;
|
|
452
|
+
}
|
|
453
|
+
async rebindOrphansToDefault(em, defaultId, orphans) {
|
|
454
|
+
for (const o of orphans) {
|
|
455
|
+
await em
|
|
456
|
+
.getConnection()
|
|
457
|
+
.execute(`insert into "${o.bridgeTable}" ("sales_channel_id", "${o.entityIdColumn}") ` +
|
|
458
|
+
`values (?, ?) ` +
|
|
459
|
+
`on conflict ("sales_channel_id", "${o.entityIdColumn}") do nothing`, [defaultId, o.entityId], 'run', em.getTransactionContext());
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
snapshot(c) {
|
|
463
|
+
return {
|
|
464
|
+
id: c.id,
|
|
465
|
+
code: c.code,
|
|
466
|
+
name: c.name,
|
|
467
|
+
logoAssetId: c.logoAssetId ?? null,
|
|
468
|
+
themeCode: c.themeCode ?? null,
|
|
469
|
+
languages: c.languages,
|
|
470
|
+
defaultLanguage: c.defaultLanguage,
|
|
471
|
+
currencies: c.currencies,
|
|
472
|
+
defaultCurrency: c.defaultCurrency,
|
|
473
|
+
active: c.active,
|
|
474
|
+
systemDefault: c.systemDefault,
|
|
475
|
+
version: c.version,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
async audit(kind, context, channelId, payload) {
|
|
479
|
+
if (!this.auditLogService)
|
|
480
|
+
return;
|
|
481
|
+
await this.auditLogService.record({
|
|
482
|
+
actorAdminUserId: context?.actorAdminUserId ?? null,
|
|
483
|
+
action: kind === 'identity'
|
|
484
|
+
? SALES_CHANNEL_AUDIT_ACTIONS.IDENTITY_CHANGED
|
|
485
|
+
: SALES_CHANNEL_AUDIT_ACTIONS.LIFECYCLE_CHANGED,
|
|
486
|
+
objectType: 'sales_channel',
|
|
487
|
+
objectId: channelId,
|
|
488
|
+
stateAfter: payload,
|
|
489
|
+
...(context?.requestId ? { requestId: context.requestId } : {}),
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Drop the cache, then announce (D-93).
|
|
494
|
+
*
|
|
495
|
+
* The drop runs **after the flush**, so a concurrent read cannot re-pin the
|
|
496
|
+
* pre-commit row, and **before the emit**, so every subscriber re-reads
|
|
497
|
+
* post-invalidation state. The count is deliberately unread:
|
|
498
|
+
* `invalidateAfterWrite` answers `null` for an unreachable shared layer, and
|
|
499
|
+
* the row is written and audited by the time we are here, so there is nothing
|
|
500
|
+
* this method could truthfully do with it. Reads stay correct meanwhile — the
|
|
501
|
+
* cache bypasses the marked prefix until a later drop succeeds.
|
|
502
|
+
*/
|
|
503
|
+
async emitIdentityChanged(channelCode) {
|
|
504
|
+
await this.cache?.invalidateAfterWrite(channelCode);
|
|
505
|
+
this.eventBus.emit('sales_channels.identity_changed', {
|
|
506
|
+
eventId: `sales_channels.identity_changed:${channelCode}:${Date.now()}`,
|
|
507
|
+
occurredAt: new Date().toISOString(),
|
|
508
|
+
channelCode,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/** Same seam as {@link emitIdentityChanged}; see its comment for the ordering. */
|
|
512
|
+
async emitLifecycleChanged(channelCode, invalidateAll = false) {
|
|
513
|
+
// `invalidateAll` is the caller saying more than one channel's cached state
|
|
514
|
+
// moved — a delete that rebinds orphans, or a flag move — and the cached
|
|
515
|
+
// value carries `systemDefault` inside it, so the other channel's entry is
|
|
516
|
+
// stale too.
|
|
517
|
+
if (invalidateAll)
|
|
518
|
+
await this.cache?.invalidateAllAfterWrite();
|
|
519
|
+
else
|
|
520
|
+
await this.cache?.invalidateAfterWrite(channelCode);
|
|
521
|
+
this.eventBus.emit('sales_channels.lifecycle_changed', {
|
|
522
|
+
eventId: `sales_channels.lifecycle_changed:${channelCode}:${Date.now()}`,
|
|
523
|
+
occurredAt: new Date().toISOString(),
|
|
524
|
+
channelCode,
|
|
525
|
+
invalidateAll,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function dictionaryReferenceHttpError(err, field) {
|
|
530
|
+
const noun = err.entryType === 'currency' ? 'Currency' : 'Language';
|
|
531
|
+
return new HttpError(409, err.code, err.code === 'DICTIONARY_ENTRY_INACTIVE'
|
|
532
|
+
? `${noun} code ${err.entryCode} is no longer available for sales channels.`
|
|
533
|
+
: `${noun} code ${err.entryCode} is not recognised.`, [{ path: field, issue: err.code }]);
|
|
534
|
+
}
|
|
535
|
+
//# sourceMappingURL=sales-channels.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sales-channels.service.js","sourceRoot":"","sources":["../../../src/backend/services/sales-channels.service.ts"],"names":[],"mappings":"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"}
|