@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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/admin/api/sales-channels-client.d.ts +69 -0
  4. package/dist/admin/api/sales-channels-client.d.ts.map +1 -0
  5. package/dist/admin/api/sales-channels-client.js +74 -0
  6. package/dist/admin/api/sales-channels-client.js.map +1 -0
  7. package/dist/admin/components/ChannelIdentityForm.d.ts +24 -0
  8. package/dist/admin/components/ChannelIdentityForm.d.ts.map +1 -0
  9. package/dist/admin/components/ChannelIdentityForm.js +147 -0
  10. package/dist/admin/components/ChannelIdentityForm.js.map +1 -0
  11. package/dist/admin/components/DefaultChannelBadge.d.ts +21 -0
  12. package/dist/admin/components/DefaultChannelBadge.d.ts.map +1 -0
  13. package/dist/admin/components/DefaultChannelBadge.js +26 -0
  14. package/dist/admin/components/DefaultChannelBadge.js.map +1 -0
  15. package/dist/admin/components/EntityChannelMembership.d.ts +52 -0
  16. package/dist/admin/components/EntityChannelMembership.d.ts.map +1 -0
  17. package/dist/admin/components/EntityChannelMembership.js +98 -0
  18. package/dist/admin/components/EntityChannelMembership.js.map +1 -0
  19. package/dist/admin/index.d.ts +44 -0
  20. package/dist/admin/index.d.ts.map +1 -0
  21. package/dist/admin/index.js +148 -0
  22. package/dist/admin/index.js.map +1 -0
  23. package/dist/admin/pages/SalesChannelEditPage.d.ts +25 -0
  24. package/dist/admin/pages/SalesChannelEditPage.d.ts.map +1 -0
  25. package/dist/admin/pages/SalesChannelEditPage.js +185 -0
  26. package/dist/admin/pages/SalesChannelEditPage.js.map +1 -0
  27. package/dist/admin/pages/SalesChannelsListPage.d.ts +25 -0
  28. package/dist/admin/pages/SalesChannelsListPage.d.ts.map +1 -0
  29. package/dist/admin/pages/SalesChannelsListPage.js +91 -0
  30. package/dist/admin/pages/SalesChannelsListPage.js.map +1 -0
  31. package/dist/admin/zones/OrganizationChannelMembership.d.ts +34 -0
  32. package/dist/admin/zones/OrganizationChannelMembership.d.ts.map +1 -0
  33. package/dist/admin/zones/OrganizationChannelMembership.js +11 -0
  34. package/dist/admin/zones/OrganizationChannelMembership.js.map +1 -0
  35. package/dist/admin/zones/ProductChannelMembership.d.ts +23 -0
  36. package/dist/admin/zones/ProductChannelMembership.d.ts.map +1 -0
  37. package/dist/admin/zones/ProductChannelMembership.js +11 -0
  38. package/dist/admin/zones/ProductChannelMembership.js.map +1 -0
  39. package/dist/backend/commands/set-default.command.d.ts +53 -0
  40. package/dist/backend/commands/set-default.command.d.ts.map +1 -0
  41. package/dist/backend/commands/set-default.command.js +87 -0
  42. package/dist/backend/commands/set-default.command.js.map +1 -0
  43. package/dist/backend/index.d.ts +71 -0
  44. package/dist/backend/index.d.ts.map +1 -0
  45. package/dist/backend/index.js +81 -0
  46. package/dist/backend/index.js.map +1 -0
  47. package/dist/backend/routes.admin.d.ts +12 -0
  48. package/dist/backend/routes.admin.d.ts.map +1 -0
  49. package/dist/backend/routes.admin.js +302 -0
  50. package/dist/backend/routes.admin.js.map +1 -0
  51. package/dist/backend/routes.storefront.d.ts +36 -0
  52. package/dist/backend/routes.storefront.d.ts.map +1 -0
  53. package/dist/backend/routes.storefront.js +58 -0
  54. package/dist/backend/routes.storefront.js.map +1 -0
  55. package/dist/backend/services/index.d.ts +2 -0
  56. package/dist/backend/services/index.d.ts.map +1 -0
  57. package/dist/backend/services/index.js +2 -0
  58. package/dist/backend/services/index.js.map +1 -0
  59. package/dist/backend/services/sales-channel-attribution-registry.d.ts +28 -0
  60. package/dist/backend/services/sales-channel-attribution-registry.d.ts.map +1 -0
  61. package/dist/backend/services/sales-channel-attribution-registry.js +50 -0
  62. package/dist/backend/services/sales-channel-attribution-registry.js.map +1 -0
  63. package/dist/backend/services/sales-channels.service.d.ts +247 -0
  64. package/dist/backend/services/sales-channels.service.d.ts.map +1 -0
  65. package/dist/backend/services/sales-channels.service.js +535 -0
  66. package/dist/backend/services/sales-channels.service.js.map +1 -0
  67. package/dist/manifest.d.ts +185 -0
  68. package/dist/manifest.d.ts.map +1 -0
  69. package/dist/manifest.js +204 -0
  70. package/dist/manifest.js.map +1 -0
  71. package/docs/sales_channels/admin-usage.md +77 -0
  72. package/docs/sales_channels/developer-guide.md +107 -0
  73. package/docs/sales_channels/index.md +95 -0
  74. package/i18n/en.json +75 -0
  75. package/i18n/pl.json +75 -0
  76. package/package.json +88 -0
  77. 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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"}