@endora-commerce/mod-shipments 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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/backend/email-templates/transactional-defaults.d.ts +6 -0
  4. package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
  5. package/dist/backend/email-templates/transactional-defaults.js +27 -0
  6. package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
  7. package/dist/backend/entities/shipment.entity.d.ts +40 -0
  8. package/dist/backend/entities/shipment.entity.d.ts.map +1 -0
  9. package/dist/backend/entities/shipment.entity.js +98 -0
  10. package/dist/backend/entities/shipment.entity.js.map +1 -0
  11. package/dist/backend/index.d.ts +71 -0
  12. package/dist/backend/index.d.ts.map +1 -0
  13. package/dist/backend/index.js +151 -0
  14. package/dist/backend/index.js.map +1 -0
  15. package/dist/backend/routes.d.ts +29 -0
  16. package/dist/backend/routes.d.ts.map +1 -0
  17. package/dist/backend/routes.js +35 -0
  18. package/dist/backend/routes.js.map +1 -0
  19. package/dist/backend/services/auto-shipment-on-paid.d.ts +28 -0
  20. package/dist/backend/services/auto-shipment-on-paid.d.ts.map +1 -0
  21. package/dist/backend/services/auto-shipment-on-paid.js +58 -0
  22. package/dist/backend/services/auto-shipment-on-paid.js.map +1 -0
  23. package/dist/backend/services/events.d.ts +40 -0
  24. package/dist/backend/services/events.d.ts.map +1 -0
  25. package/dist/backend/services/events.js +2 -0
  26. package/dist/backend/services/events.js.map +1 -0
  27. package/dist/backend/services/receive-shipment-handler.d.ts +93 -0
  28. package/dist/backend/services/receive-shipment-handler.d.ts.map +1 -0
  29. package/dist/backend/services/receive-shipment-handler.js +203 -0
  30. package/dist/backend/services/receive-shipment-handler.js.map +1 -0
  31. package/dist/backend/services/shipment-email-notifier.d.ts +78 -0
  32. package/dist/backend/services/shipment-email-notifier.d.ts.map +1 -0
  33. package/dist/backend/services/shipment-email-notifier.js +76 -0
  34. package/dist/backend/services/shipment-email-notifier.js.map +1 -0
  35. package/dist/backend/services/shipment-read.service.d.ts +19 -0
  36. package/dist/backend/services/shipment-read.service.d.ts.map +1 -0
  37. package/dist/backend/services/shipment-read.service.js +38 -0
  38. package/dist/backend/services/shipment-read.service.js.map +1 -0
  39. package/dist/backend/services/shipment-service.d.ts +79 -0
  40. package/dist/backend/services/shipment-service.d.ts.map +1 -0
  41. package/dist/backend/services/shipment-service.js +203 -0
  42. package/dist/backend/services/shipment-service.js.map +1 -0
  43. package/dist/backend/services/shipment-usage.service.d.ts +23 -0
  44. package/dist/backend/services/shipment-usage.service.d.ts.map +1 -0
  45. package/dist/backend/services/shipment-usage.service.js +26 -0
  46. package/dist/backend/services/shipment-usage.service.js.map +1 -0
  47. package/dist/backend/services/shipping-email-renderer.d.ts +22 -0
  48. package/dist/backend/services/shipping-email-renderer.d.ts.map +1 -0
  49. package/dist/backend/services/shipping-email-renderer.js +14 -0
  50. package/dist/backend/services/shipping-email-renderer.js.map +1 -0
  51. package/dist/manifest.d.ts +200 -0
  52. package/dist/manifest.d.ts.map +1 -0
  53. package/dist/manifest.js +63 -0
  54. package/dist/manifest.js.map +1 -0
  55. package/dist/migrations/20260817T194652_shipments_order_fk.d.ts +32 -0
  56. package/dist/migrations/20260817T194652_shipments_order_fk.d.ts.map +1 -0
  57. package/dist/migrations/20260817T194652_shipments_order_fk.js +66 -0
  58. package/dist/migrations/20260817T194652_shipments_order_fk.js.map +1 -0
  59. package/dist/migrations/20260819T171006_shipments_status_pending_manual.d.ts +19 -0
  60. package/dist/migrations/20260819T171006_shipments_status_pending_manual.d.ts.map +1 -0
  61. package/dist/migrations/20260819T171006_shipments_status_pending_manual.js +32 -0
  62. package/dist/migrations/20260819T171006_shipments_status_pending_manual.js.map +1 -0
  63. package/dist/migrations/index.d.ts +35 -0
  64. package/dist/migrations/index.d.ts.map +1 -0
  65. package/dist/migrations/index.js +38 -0
  66. package/dist/migrations/index.js.map +1 -0
  67. package/docs/shipments.md +96 -0
  68. package/package.json +69 -0
@@ -0,0 +1,38 @@
1
+ import { Shipment } from '../entities/shipment.entity.js';
2
+ /**
3
+ * The reads behind `shipmentReadPort` — see the port's contract for why a
4
+ * carrier module asks them and what happens when this module is off.
5
+ *
6
+ * Deliberately not methods on {@link ShipmentService}: that service opens
7
+ * attempts and calls carriers, and a lookup by id has no business resolving an
8
+ * adapter registry, an order port and a delivery-method port to answer.
9
+ */
10
+ export class ShipmentReadService {
11
+ emFactory;
12
+ constructor(emFactory) {
13
+ this.emFactory = emFactory;
14
+ }
15
+ async findById(id) {
16
+ const row = await this.emFactory().findOne(Shipment, { id });
17
+ return row ? toShipmentRecord(row) : null;
18
+ }
19
+ async findByExternalReference(reference) {
20
+ const row = await this.emFactory().findOne(Shipment, { externalReference: reference }, { orderBy: { createdAt: 'desc', attemptNo: 'desc' } });
21
+ return row ? toShipmentRecord(row) : null;
22
+ }
23
+ }
24
+ export function toShipmentRecord(shipment) {
25
+ return {
26
+ id: shipment.id,
27
+ orderId: shipment.orderId,
28
+ deliveryMethodId: shipment.deliveryMethodId,
29
+ status: shipment.status,
30
+ externalReference: shipment.externalReference ?? null,
31
+ providerDetails: shipment.providerDetails ?? null,
32
+ failureReason: shipment.failureReason ?? null,
33
+ attemptNo: shipment.attemptNo,
34
+ createdAt: shipment.createdAt,
35
+ updatedAt: shipment.updatedAt,
36
+ };
37
+ }
38
+ //# sourceMappingURL=shipment-read.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-read.service.js","sourceRoot":"","sources":["../../../src/backend/services/shipment-read.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,OAAO,mBAAmB;IACD;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7D,OAAO,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,SAAiB;QAC7C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CACxC,QAAQ,EACR,EAAE,iBAAiB,EAAE,SAAS,EAAE,EAChC,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CACtD,CAAC;QACF,OAAO,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,CAAC;CACF;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,OAAO;QACL,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;QAC3C,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,IAAI,IAAI;QACrD,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,IAAI;QACjD,aAAa,EAAE,QAAQ,CAAC,aAAa,IAAI,IAAI;QAC7C,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,SAAS,EAAE,QAAQ,CAAC,SAAS;KAC9B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,79 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { DeliveryMethodReadPort, OrderReadPort, ShippingAdapterRegistryPort } from '@endora-commerce/contracts';
3
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
4
+ import { Shipment } from '../entities/shipment.entity.js';
5
+ import type { ShippingEventBus } from './events.js';
6
+ /**
7
+ * What a `pending_manual` shipment says about itself, in one place so the row,
8
+ * the audit entry and the tests cannot drift apart (issue #250).
9
+ *
10
+ * English and persisted, like the payment side's refund reasons: this is the
11
+ * record of what happened, not a rendered string. The admin translates the
12
+ * *state* and shows this sentence as the detail that names the module — which
13
+ * is the one thing the operator needs and no translation can supply.
14
+ */
15
+ export declare function carrierNotContactedReason(moduleId: string): string;
16
+ /**
17
+ * ShipmentService (feature 035, FR-021/FR-024).
18
+ *
19
+ * `createShipment` raises `shipment_created`: it opens a pending Shipment
20
+ * against the Order, invokes the method adapter's `onShipmentCreated`, and
21
+ * emits `shipment.created.v1`. It is also the retry path (FR-024): called again
22
+ * after a failure it appends attempt n+1 and leaves the prior rows intact,
23
+ * refusing only once an attempt has succeeded. `listForOrder` powers the admin
24
+ * view.
25
+ *
26
+ * Issue #257 — there used to be a second attempt-opening path, `openRetry`, and
27
+ * it asked no adapter in any state, not only while a carrier module was off. It
28
+ * is gone rather than repaired: no admin surface reached it, its own contract
29
+ * described it as "equivalent to calling the generate route again", and the
30
+ * equivalence was false in the one way that mattered — the row appeared, the
31
+ * status read `pending`, and no carrier had heard of the parcel. Repairing it
32
+ * would have left a second copy of `createShipment` to keep honest forever;
33
+ * deleting it leaves one path, which is the one every caller already used.
34
+ *
35
+ * Feature 075 Phase C — the order and the delivery method are read over their
36
+ * owners' ports. Only the `Shipment` rows are this module's to write, and only
37
+ * those stay inside the transaction: the two reads are of rows nothing in this
38
+ * operation modifies, so moving them onto the owner's `EntityManager` costs no
39
+ * consistency. Both fail closed when their owner is off, which is right — a
40
+ * shipment opened against an order the platform will not read is a parcel with
41
+ * no addressee.
42
+ *
43
+ * Issue #250 — the adapter registry is a contribution point, so an adapter
44
+ * whose owner is switched off is filtered out at enumeration rather than
45
+ * throwing (D-39). That gate is right; what used to follow it was not. The
46
+ * hook was skipped and the row was still written `pending`, so a shipment with
47
+ * no label, no tracking number and no carrier that knew about it read exactly
48
+ * like one the carrier had accepted. It now opens `pending_manual` — the state
49
+ * the platform already uses on the payment side for "the money did not move,
50
+ * a person has to finish this" — with the module named in `failureReason` and
51
+ * an audit row recording the attempt.
52
+ */
53
+ export declare class ShipmentService {
54
+ private readonly emFactory;
55
+ private readonly registry;
56
+ private readonly orderRead;
57
+ private readonly deliveryMethodRead;
58
+ private readonly auditLog;
59
+ private readonly events?;
60
+ constructor(emFactory: () => EntityManager, registry: ShippingAdapterRegistryPort, orderRead: OrderReadPort, deliveryMethodRead: DeliveryMethodReadPort, auditLog: AuditPort, events?: ShippingEventBus | undefined);
61
+ createShipment(orderId: string): Promise<Shipment>;
62
+ /**
63
+ * The order's shipment attempts — for the admin panel.
64
+ *
65
+ * The `orderRead` call is the tenant boundary, not a convenience: `Shipment`
66
+ * is `@GlobalEntity`, so `em.find(Shipment, { orderId })` carries no filter
67
+ * and answers for every order on the platform, while `Order` is `@OrgScoped`
68
+ * and this port reads it through a filtered EntityManager. An
69
+ * assignment-scoped administrator therefore gets `null` here and no tracking
70
+ * number from an organization they are not assigned to.
71
+ *
72
+ * `createShipment` above has always made this read for its own reasons and so
73
+ * was never exposed; this one had no reason to make it, which is exactly how
74
+ * the gap arrived. 404 rather than 403 — an out-of-scope order must read the
75
+ * same as one that is not there.
76
+ */
77
+ listForOrder(orderId: string): Promise<Shipment[]>;
78
+ }
79
+ //# sourceMappingURL=shipment-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/shipment-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EACV,sBAAsB,EACtB,aAAa,EACb,2BAA2B,EAC5B,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBALP,SAAS,EAAE,MAAM,aAAa,EAC9B,QAAQ,EAAE,2BAA2B,EACrC,SAAS,EAAE,aAAa,EACxB,kBAAkB,EAAE,sBAAsB,EAC1C,QAAQ,EAAE,SAAS,EACnB,MAAM,CAAC,EAAE,gBAAgB,YAAA;IAGtC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA0HxD;;;;;;;;;;;;;;OAcG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAQzD"}
@@ -0,0 +1,203 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { ERROR_CODES } from '@endora-commerce/contracts';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { recordAuditFromContext } from '@endora-commerce/platform/commands';
5
+ import { Shipment } from '../entities/shipment.entity.js';
6
+ /**
7
+ * What a `pending_manual` shipment says about itself, in one place so the row,
8
+ * the audit entry and the tests cannot drift apart (issue #250).
9
+ *
10
+ * English and persisted, like the payment side's refund reasons: this is the
11
+ * record of what happened, not a rendered string. The admin translates the
12
+ * *state* and shows this sentence as the detail that names the module — which
13
+ * is the one thing the operator needs and no translation can supply.
14
+ */
15
+ export function carrierNotContactedReason(moduleId) {
16
+ return (`The "${moduleId}" module is not switched on here, so the carrier was never ` +
17
+ `asked to create this shipment. Switch the module back on and generate the ` +
18
+ `shipment again.`);
19
+ }
20
+ /**
21
+ * ShipmentService (feature 035, FR-021/FR-024).
22
+ *
23
+ * `createShipment` raises `shipment_created`: it opens a pending Shipment
24
+ * against the Order, invokes the method adapter's `onShipmentCreated`, and
25
+ * emits `shipment.created.v1`. It is also the retry path (FR-024): called again
26
+ * after a failure it appends attempt n+1 and leaves the prior rows intact,
27
+ * refusing only once an attempt has succeeded. `listForOrder` powers the admin
28
+ * view.
29
+ *
30
+ * Issue #257 — there used to be a second attempt-opening path, `openRetry`, and
31
+ * it asked no adapter in any state, not only while a carrier module was off. It
32
+ * is gone rather than repaired: no admin surface reached it, its own contract
33
+ * described it as "equivalent to calling the generate route again", and the
34
+ * equivalence was false in the one way that mattered — the row appeared, the
35
+ * status read `pending`, and no carrier had heard of the parcel. Repairing it
36
+ * would have left a second copy of `createShipment` to keep honest forever;
37
+ * deleting it leaves one path, which is the one every caller already used.
38
+ *
39
+ * Feature 075 Phase C — the order and the delivery method are read over their
40
+ * owners' ports. Only the `Shipment` rows are this module's to write, and only
41
+ * those stay inside the transaction: the two reads are of rows nothing in this
42
+ * operation modifies, so moving them onto the owner's `EntityManager` costs no
43
+ * consistency. Both fail closed when their owner is off, which is right — a
44
+ * shipment opened against an order the platform will not read is a parcel with
45
+ * no addressee.
46
+ *
47
+ * Issue #250 — the adapter registry is a contribution point, so an adapter
48
+ * whose owner is switched off is filtered out at enumeration rather than
49
+ * throwing (D-39). That gate is right; what used to follow it was not. The
50
+ * hook was skipped and the row was still written `pending`, so a shipment with
51
+ * no label, no tracking number and no carrier that knew about it read exactly
52
+ * like one the carrier had accepted. It now opens `pending_manual` — the state
53
+ * the platform already uses on the payment side for "the money did not move,
54
+ * a person has to finish this" — with the module named in `failureReason` and
55
+ * an audit row recording the attempt.
56
+ */
57
+ export class ShipmentService {
58
+ emFactory;
59
+ registry;
60
+ orderRead;
61
+ deliveryMethodRead;
62
+ auditLog;
63
+ events;
64
+ constructor(emFactory, registry, orderRead, deliveryMethodRead, auditLog, events) {
65
+ this.emFactory = emFactory;
66
+ this.registry = registry;
67
+ this.orderRead = orderRead;
68
+ this.deliveryMethodRead = deliveryMethodRead;
69
+ this.auditLog = auditLog;
70
+ this.events = events;
71
+ }
72
+ async createShipment(orderId) {
73
+ // command-coverage-ignore: creates a pending shipment attempt + invokes the
74
+ // delivery adapter; fulfilment mechanics — the order "shipped" transition is
75
+ // audited in the orders flow.
76
+ const run = async () => {
77
+ const em = this.emFactory();
78
+ return em.transactional(async (tx) => {
79
+ const order = await this.orderRead.findById(orderId);
80
+ if (!order) {
81
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Order not found.');
82
+ }
83
+ const latest = await tx.findOne(Shipment, { orderId }, { orderBy: { attemptNo: 'desc' } });
84
+ if (latest && latest.status === 'success') {
85
+ throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'Order already has a successful shipment; nothing to generate.');
86
+ }
87
+ const method = await this.deliveryMethodRead.findById(order.deliveryMethodId);
88
+ const adapterKey = method?.adapter ?? order.deliveryMethodId;
89
+ const adapter = this.registry.get(adapterKey);
90
+ // Told apart deliberately: an adapter nobody ever contributed is an
91
+ // offline method that has always been finished by hand, and it keeps
92
+ // opening `pending`. Only a contributed adapter whose module is absent
93
+ // produces `pending_manual` — because only that one names a module an
94
+ // operator can switch back on.
95
+ const absentCarrierModule = adapter ? null : this.registry.absentOwnerFor(adapterKey);
96
+ const shipment = tx.create(Shipment, {
97
+ orderId,
98
+ deliveryMethodId: order.deliveryMethodId,
99
+ status: absentCarrierModule ? 'pending_manual' : 'pending',
100
+ ...(absentCarrierModule
101
+ ? { failureReason: carrierNotContactedReason(absentCarrierModule) }
102
+ : {}),
103
+ attemptNo: (latest?.attemptNo ?? 0) + 1,
104
+ });
105
+ await tx.persistAndFlush(shipment);
106
+ // Invoke the adapter's shipment_created hook. Status stays pending until
107
+ // receive_shipment; a synchronous `generated` result still deposits the
108
+ // tracking number on the row so admin can see it before terminal ingress.
109
+ // Carrier references (ShipX id, etc.) must be applied on *this*
110
+ // transactional EM — a forked emFactory inside the adapter cannot see
111
+ // the uncommitted row and would silently skip the write.
112
+ if (adapter) {
113
+ const started = await adapter.onShipmentCreated({
114
+ orderId,
115
+ shipmentId: shipment.id,
116
+ deliveryMethodId: order.deliveryMethodId,
117
+ attemptNo: shipment.attemptNo,
118
+ });
119
+ if (started.kind === 'pending' || started.kind === 'generated') {
120
+ // `externalReference` is the explicit carrier reference and wins;
121
+ // `trackingNumber` on a `generated` result is the older spelling an
122
+ // adapter that names no reference still relies on.
123
+ const reference = started.externalReference !== undefined
124
+ ? (started.externalReference ?? null)
125
+ : started.kind === 'generated' && started.trackingNumber
126
+ ? started.trackingNumber
127
+ : undefined;
128
+ if (reference !== undefined) {
129
+ shipment.externalReference = reference;
130
+ }
131
+ if (started.providerDetails) {
132
+ shipment.providerDetails = started.providerDetails;
133
+ }
134
+ if (reference !== undefined || started.providerDetails !== undefined) {
135
+ // `persistAndFlush` and not `flush`: the row is already managed by
136
+ // this transaction, so the two are equivalent here, and this is the
137
+ // spelling every other write in the method uses.
138
+ await tx.persistAndFlush(shipment);
139
+ }
140
+ }
141
+ }
142
+ else if (absentCarrierModule) {
143
+ // Co-transactional with the row it describes (Principle XIII): the
144
+ // shipment and the record of why it is unfinished commit together or
145
+ // not at all. `recordAuditFromContext` rather than the Command Bus
146
+ // for the reason its own docblock gives — this write already owns a
147
+ // transaction the bus would have to fork out of.
148
+ recordAuditFromContext(this.auditLog, tx, {
149
+ action: 'shipment.carrier_not_contacted',
150
+ objectType: 'shipment',
151
+ objectId: shipment.id,
152
+ stateAfter: {
153
+ orderId,
154
+ adapter: adapterKey,
155
+ absentModule: absentCarrierModule,
156
+ attemptNo: shipment.attemptNo,
157
+ status: shipment.status,
158
+ },
159
+ });
160
+ }
161
+ if (this.events) {
162
+ this.events.emit('shipment.created.v1', {
163
+ eventId: randomUUID(),
164
+ occurredAt: new Date().toISOString(),
165
+ orderId,
166
+ shipmentId: shipment.id,
167
+ deliveryMethodId: order.deliveryMethodId,
168
+ adapter: adapterKey,
169
+ attemptNo: shipment.attemptNo,
170
+ status: shipment.status,
171
+ });
172
+ }
173
+ return { shipment, adapterKey };
174
+ });
175
+ };
176
+ const result = this.events ? await this.events.run(run) : await run();
177
+ return result.shipment;
178
+ }
179
+ /**
180
+ * The order's shipment attempts — for the admin panel.
181
+ *
182
+ * The `orderRead` call is the tenant boundary, not a convenience: `Shipment`
183
+ * is `@GlobalEntity`, so `em.find(Shipment, { orderId })` carries no filter
184
+ * and answers for every order on the platform, while `Order` is `@OrgScoped`
185
+ * and this port reads it through a filtered EntityManager. An
186
+ * assignment-scoped administrator therefore gets `null` here and no tracking
187
+ * number from an organization they are not assigned to.
188
+ *
189
+ * `createShipment` above has always made this read for its own reasons and so
190
+ * was never exposed; this one had no reason to make it, which is exactly how
191
+ * the gap arrived. 404 rather than 403 — an out-of-scope order must read the
192
+ * same as one that is not there.
193
+ */
194
+ async listForOrder(orderId) {
195
+ const order = await this.orderRead.findById(orderId);
196
+ if (!order) {
197
+ throw new HttpError(404, ERROR_CODES.ORDER_NOT_FOUND, 'Order not found.');
198
+ }
199
+ const em = this.emFactory();
200
+ return em.find(Shipment, { orderId }, { orderBy: { attemptNo: 'asc' } });
201
+ }
202
+ }
203
+ //# sourceMappingURL=shipment-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-service.js","sourceRoot":"","sources":["../../../src/backend/services/shipment-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { ShipmentUsagePort } from '@endora-commerce/contracts';
3
+ /**
4
+ * The read behind `shipmentUsagePort` — see the port's contract for why the
5
+ * question belongs to this module and what its caller does when the module is
6
+ * off (feature 075, the `delivery_methods` shard).
7
+ *
8
+ * One `count`, on this module's own `EntityManager`. Deliberately not a method
9
+ * on {@link ShipmentService}: that service opens shipment attempts and calls
10
+ * carriers, and a delete guard has no business resolving an adapter registry,
11
+ * an order port and a delivery-method port to ask how many rows exist.
12
+ */
13
+ export declare class ShipmentUsageService implements ShipmentUsagePort {
14
+ private readonly emFactory;
15
+ constructor(emFactory: () => EntityManager);
16
+ /**
17
+ * Every status counts, `failure` and `pending_manual` included: a failed
18
+ * attempt is still history an operator can open, and it still names the
19
+ * method it was attempted with.
20
+ */
21
+ countForDeliveryMethod(deliveryMethodId: string): Promise<number>;
22
+ }
23
+ //# sourceMappingURL=shipment-usage.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-usage.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/shipment-usage.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAE3D;;;;OAIG;IACG,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGxE"}
@@ -0,0 +1,26 @@
1
+ import { Shipment } from '../entities/shipment.entity.js';
2
+ /**
3
+ * The read behind `shipmentUsagePort` — see the port's contract for why the
4
+ * question belongs to this module and what its caller does when the module is
5
+ * off (feature 075, the `delivery_methods` shard).
6
+ *
7
+ * One `count`, on this module's own `EntityManager`. Deliberately not a method
8
+ * on {@link ShipmentService}: that service opens shipment attempts and calls
9
+ * carriers, and a delete guard has no business resolving an adapter registry,
10
+ * an order port and a delivery-method port to ask how many rows exist.
11
+ */
12
+ export class ShipmentUsageService {
13
+ emFactory;
14
+ constructor(emFactory) {
15
+ this.emFactory = emFactory;
16
+ }
17
+ /**
18
+ * Every status counts, `failure` and `pending_manual` included: a failed
19
+ * attempt is still history an operator can open, and it still names the
20
+ * method it was attempted with.
21
+ */
22
+ async countForDeliveryMethod(deliveryMethodId) {
23
+ return this.emFactory().count(Shipment, { deliveryMethodId });
24
+ }
25
+ }
26
+ //# sourceMappingURL=shipment-usage.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-usage.service.js","sourceRoot":"","sources":["../../../src/backend/services/shipment-usage.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D;;;;OAIG;IACH,KAAK,CAAC,sBAAsB,CAAC,gBAAwB;QACnD,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shipping-method renderer registry for the transactional order-confirmation
3
+ * e-mail (feature 035, FR-016/FR-017). An adapter may register a custom e-mail
4
+ * renderer under a key (the adapter's `renderers.email`); when none is
5
+ * registered, the platform default text builder is used, so the shipping
6
+ * section always renders.
7
+ *
8
+ * E-mail bodies are plain text (see Mailer.MailerSendInput.text), so a renderer
9
+ * is a `(ctx) => string` builder rather than a React component.
10
+ */
11
+ export interface ShippingEmailContext {
12
+ /** Resolved display name of the shipping method. */
13
+ name: string;
14
+ /** Flat surcharge (`price`) applied for this method (order currency). */
15
+ cost: number;
16
+ currency: string;
17
+ }
18
+ export type ShippingEmailRenderer = (ctx: ShippingEmailContext) => string;
19
+ export declare const defaultShippingEmailRenderer: ShippingEmailRenderer;
20
+ export declare function registerShippingEmailRenderer(key: string, renderer: ShippingEmailRenderer): void;
21
+ export declare function resolveShippingEmailRenderer(rendererKey: string | null): ShippingEmailRenderer;
22
+ //# sourceMappingURL=shipping-email-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipping-email-renderer.d.ts","sourceRoot":"","sources":["../../../src/backend/services/shipping-email-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,oBAAoB,KAAK,MAAM,CAAC;AAE1E,eAAO,MAAM,4BAA4B,EAAE,qBACa,CAAC;AAIzD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAEhG;AAED,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,CAM9F"}
@@ -0,0 +1,14 @@
1
+ export const defaultShippingEmailRenderer = (ctx) => `${ctx.name} — ${ctx.cost.toFixed(2)} ${ctx.currency}`;
2
+ const registry = new Map();
3
+ export function registerShippingEmailRenderer(key, renderer) {
4
+ registry.set(key, renderer);
5
+ }
6
+ export function resolveShippingEmailRenderer(rendererKey) {
7
+ if (rendererKey) {
8
+ const found = registry.get(rendererKey);
9
+ if (found)
10
+ return found;
11
+ }
12
+ return defaultShippingEmailRenderer;
13
+ }
14
+ //# sourceMappingURL=shipping-email-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipping-email-renderer.js","sourceRoot":"","sources":["../../../src/backend/services/shipping-email-renderer.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,4BAA4B,GAA0B,CAAC,GAAG,EAAE,EAAE,CACzE,GAAG,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;AAEzD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiC,CAAC;AAE1D,MAAM,UAAU,6BAA6B,CAAC,GAAW,EAAE,QAA+B;IACxF,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,WAA0B;IACrE,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,4BAA4B,CAAC;AACtC,CAAC"}
@@ -0,0 +1,200 @@
1
+ export declare const shipmentsSettingsManifest: {
2
+ moduleCode: string;
3
+ groups: {
4
+ code: string;
5
+ name: string;
6
+ salesChannelCodes?: string[] | undefined;
7
+ isSystemProtected?: boolean | undefined;
8
+ }[];
9
+ settings: {
10
+ code: string;
11
+ name: string;
12
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
13
+ defaultValue: unknown;
14
+ description?: string | undefined;
15
+ groupCode?: string | undefined;
16
+ previousDefaultValues?: unknown[] | undefined;
17
+ salesChannelCodes?: string[] | undefined;
18
+ enumOptions?: string[] | undefined;
19
+ configurationType?: string | undefined;
20
+ hidden?: boolean | undefined;
21
+ }[];
22
+ };
23
+ /**
24
+ * Shipments module (feature 035) — the Shipment entity and its
25
+ * generate/receive lifecycle. The delivery-side twin of `payments`.
26
+ *
27
+ * Generating is also retrying (FR-024): a second call after a failure appends
28
+ * attempt n+1 and asks the carrier for it. The separate retry route that used
29
+ * to sit beside it was deleted by issue #257 — it opened the row and asked
30
+ * nobody.
31
+ *
32
+ * The shipping-method *catalog* (adapter registry, reconciler, eligibility)
33
+ * lives in the sibling `delivery_methods` module; this module owns the
34
+ * first-class Shipment record and the `receive_shipment` ingress. Since feature
35
+ * 072 (T124) it registers its own services and routes through `backend.ts`;
36
+ * before that `orders/plugin.ts` constructed and mounted them, which is why
37
+ * switching this module off used to do nothing. No install/uninstall hook —
38
+ * schema is owned by migration 052.
39
+ */
40
+ export declare const manifest: {
41
+ id: string;
42
+ name: string;
43
+ version: string;
44
+ dependencies: string[];
45
+ description?: string | undefined;
46
+ acknowledgedDependencies?: {
47
+ moduleId: string;
48
+ port: string;
49
+ reason: string;
50
+ }[] | undefined;
51
+ nonBindingDependencies?: {
52
+ moduleId: string;
53
+ name: string;
54
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
55
+ reason: string;
56
+ whenAbsent?: string | undefined;
57
+ }[] | undefined;
58
+ activation?: {
59
+ settingCode: string;
60
+ default: boolean;
61
+ } | {
62
+ nonDeactivatable: true;
63
+ reason: string;
64
+ } | undefined;
65
+ settings?: {
66
+ moduleCode: string;
67
+ groups: {
68
+ code: string;
69
+ name: string;
70
+ salesChannelCodes?: string[] | undefined;
71
+ isSystemProtected?: boolean | undefined;
72
+ }[];
73
+ settings: {
74
+ code: string;
75
+ name: string;
76
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
77
+ defaultValue: unknown;
78
+ description?: string | undefined;
79
+ groupCode?: string | undefined;
80
+ previousDefaultValues?: unknown[] | undefined;
81
+ salesChannelCodes?: string[] | undefined;
82
+ enumOptions?: string[] | undefined;
83
+ configurationType?: string | undefined;
84
+ hidden?: boolean | undefined;
85
+ }[];
86
+ } | undefined;
87
+ i18n?: {
88
+ bundlesDir: string;
89
+ } | undefined;
90
+ docs?: false | {
91
+ dir: string;
92
+ } | undefined;
93
+ demo?: false | {
94
+ summary: string;
95
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
96
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
97
+ after?: readonly string[] | undefined;
98
+ package?: string | undefined;
99
+ } | undefined;
100
+ actions?: {
101
+ id: string;
102
+ labelKey: string;
103
+ 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";
104
+ targetRoute: string;
105
+ keywords: string[];
106
+ weight: number;
107
+ descriptionKey?: string | undefined;
108
+ requiredPermission?: string | undefined;
109
+ }[] | undefined;
110
+ permissions?: {
111
+ code: string;
112
+ label: string;
113
+ module?: string | undefined;
114
+ description?: string | undefined;
115
+ requires?: string[] | undefined;
116
+ }[] | undefined;
117
+ transactionalEmails?: {
118
+ code: string;
119
+ name: string;
120
+ variables: {
121
+ key: string;
122
+ label: string;
123
+ sampleValue?: string | undefined;
124
+ description?: string | undefined;
125
+ }[];
126
+ description?: string | undefined;
127
+ group?: string | undefined;
128
+ }[] | undefined;
129
+ capabilities?: string[] | undefined;
130
+ exclusiveCapabilities?: {
131
+ key: string;
132
+ errorCode: string;
133
+ }[] | undefined;
134
+ errorCodes?: {
135
+ code: string;
136
+ tokens?: string[] | undefined;
137
+ }[] | undefined;
138
+ blocks?: {
139
+ name: string;
140
+ labelKey: string;
141
+ category: string;
142
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
143
+ fields: Record<string, {
144
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
145
+ label?: string | undefined;
146
+ required?: boolean | undefined;
147
+ options?: {
148
+ label: string;
149
+ value: string | number;
150
+ }[] | undefined;
151
+ refKind?: string | undefined;
152
+ }>;
153
+ descriptionKey?: string | undefined;
154
+ defaultProps?: Record<string, unknown> | undefined;
155
+ responsiveFields?: string[] | undefined;
156
+ previewIcon?: string | undefined;
157
+ weight?: number | undefined;
158
+ }[] | undefined;
159
+ blockCategories?: {
160
+ key: string;
161
+ titleKey: string;
162
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
163
+ weight?: number | undefined;
164
+ visible?: boolean | undefined;
165
+ }[] | undefined;
166
+ env?: {
167
+ name: string;
168
+ describes: {
169
+ en: string;
170
+ pl: string;
171
+ };
172
+ requirement: {
173
+ kind: "required";
174
+ } | {
175
+ kind: "requiredWhen";
176
+ input: string;
177
+ equals: string;
178
+ } | {
179
+ kind: "optional";
180
+ without: {
181
+ en: string;
182
+ pl: string;
183
+ };
184
+ };
185
+ secret: boolean;
186
+ generable: boolean;
187
+ owner: {
188
+ kind: "platform";
189
+ } | {
190
+ kind: "application";
191
+ application: "admin" | "backend" | "storefront";
192
+ } | {
193
+ kind: "module";
194
+ moduleId: string;
195
+ };
196
+ consumers: ("admin" | "backend" | "storefront")[];
197
+ addressOf: "admin" | "backend" | "storefront" | null;
198
+ }[] | undefined;
199
+ };
200
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;CAcpC,CAAC;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BnB,CAAC"}