@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ <!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
2
+
3
+ # @endora-commerce/mod-shipments
4
+
5
+ Shipment record and the order_created / shipment_created / receive_shipment lifecycle.
6
+
7
+ ## What this is
8
+
9
+ An **Endora Commerce module package**. Its module id is `shipments` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
10
+
11
+ A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
12
+
13
+ ## Entry points
14
+
15
+ | Import | Contents |
16
+ | --- | --- |
17
+ | `@endora-commerce/mod-shipments` | the module manifest — its id, version, dependencies, settings and activation |
18
+ | `@endora-commerce/mod-shipments/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
19
+ | `@endora-commerce/mod-shipments/migrations` | the module’s own schema migrations, in the order the platform runs them |
20
+
21
+ ## Depends on
22
+
23
+ Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
24
+
25
+ **Endora packages**
26
+
27
+ - `@endora-commerce/contracts`
28
+ - `@endora-commerce/email-components`
29
+ - `@endora-commerce/platform`
30
+
31
+ **Third-party**
32
+
33
+ - `@mikro-orm/core` ^6
34
+ - `@mikro-orm/migrations` ^6
35
+ - `@mikro-orm/postgresql` ^6
36
+ - `fastify` ^5
37
+
38
+ ## What the tarball carries
39
+
40
+ - `dist/` — the compiled JavaScript and its type declarations
41
+ - `docs/` — the package’s own pages in the Endora Commerce documentation site
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pnpm add @endora-commerce/mod-shipments
47
+ ```
48
+
49
+ ## Licence
50
+
51
+ MIT — the text is in `LICENSE`, beside this file.
@@ -0,0 +1,6 @@
1
+ export declare const SHIPMENT_CREATED_DEFAULT: {
2
+ defaultSubject: Record<string, string>;
3
+ defaultContent: Record<string, unknown>;
4
+ languages: string[];
5
+ };
6
+ //# sourceMappingURL=transactional-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional-defaults.d.ts","sourceRoot":"","sources":["../../../src/backend/email-templates/transactional-defaults.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,wBAAwB;oBAI9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;oBAetB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;CAE7B,CAAC"}
@@ -0,0 +1,27 @@
1
+ // Default subject + content for the shipment_created transactional email
2
+ // (feature 047). Simple layout via shared header/footer embeds.
3
+ import { simpleEmailBodyTree } from '@endora-commerce/email-components/defaults/simple-email-body';
4
+ const LANGS = ['en-US', 'pl-PL'];
5
+ export const SHIPMENT_CREATED_DEFAULT = {
6
+ defaultSubject: {
7
+ 'en-US': 'Your order {{var order.businessId}} has shipped',
8
+ 'pl-PL': 'Twoje zamówienie {{var order.businessId}} zostało wysłane',
9
+ },
10
+ defaultContent: {
11
+ schema_version: 1,
12
+ languages: {
13
+ 'en-US': simpleEmailBodyTree({
14
+ idPrefix: 'shipment',
15
+ heading: 'Your order has shipped',
16
+ text: 'Good news — a shipment has been created for your order {{var order.businessId}}. You will receive tracking details soon.',
17
+ }),
18
+ 'pl-PL': simpleEmailBodyTree({
19
+ idPrefix: 'shipment',
20
+ heading: 'Zamówienie wysłane',
21
+ text: 'Dobra wiadomość — utworzono przesyłkę dla Twojego zamówienia {{var order.businessId}}. Wkrótce otrzymasz dane do śledzenia.',
22
+ }),
23
+ },
24
+ },
25
+ languages: LANGS,
26
+ };
27
+ //# sourceMappingURL=transactional-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional-defaults.js","sourceRoot":"","sources":["../../../src/backend/email-templates/transactional-defaults.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gEAAgE;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8DAA8D,CAAC;AAEnG,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAEjC,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,cAAc,EAAE;QACd,OAAO,EAAE,iDAAiD;QAC1D,OAAO,EAAE,2DAA2D;KAC3C;IAC3B,cAAc,EAAE;QACd,cAAc,EAAE,CAAC;QACjB,SAAS,EAAE;YACT,OAAO,EAAE,mBAAmB,CAAC;gBAC3B,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,wBAAwB;gBACjC,IAAI,EAAE,0HAA0H;aACjI,CAAC;YACF,OAAO,EAAE,mBAAmB,CAAC;gBAC3B,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,oBAAoB;gBAC7B,IAAI,EAAE,6HAA6H;aACpI,CAAC;SACH;KACyB;IAC5B,SAAS,EAAE,KAAK;CACjB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * Shipment (`Przesyłka` / `Paczka`) — a first-class record of one
4
+ * shipment-generation attempt against an Order (feature 035). An Order may
5
+ * have several: a failed generation followed by a successful retry, or a
6
+ * future split into multiple parcels. The delivery-side twin of `Payment`.
7
+ *
8
+ * `status` is the shipment-process status (pending → success | failure),
9
+ * distinct from the Order status the method maps to on a `receive_shipment`
10
+ * outcome.
11
+ */
12
+ export declare class Shipment {
13
+ [OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'status' | 'externalReference' | 'providerDetails' | 'failureReason' | 'attemptNo';
14
+ id: string;
15
+ orderId: string;
16
+ deliveryMethodId: string;
17
+ /**
18
+ * `pending` → `success` | `failure` is the carrier's path. `pending_manual`
19
+ * is the path where there was no carrier: the adapter that would have been
20
+ * asked belongs to a module that is not present, so the row was opened
21
+ * without a request ever leaving the platform and a person has to finish it
22
+ * (issue #250). Same word as the refund settlement state, on purpose.
23
+ */
24
+ status: 'pending' | 'pending_manual' | 'success' | 'failure';
25
+ /** Carrier/adapter reference (e.g. tracking number) for idempotent matching. */
26
+ externalReference?: string | null;
27
+ /** Adapter/carrier payload captured on receive_shipment. */
28
+ providerDetails?: Record<string, unknown> | null;
29
+ /**
30
+ * Populated on a failure outcome, and on `pending_manual` with the sentence
31
+ * naming the module that was not asked — the two cases where a row needs to
32
+ * say why it is not a plain success.
33
+ */
34
+ failureReason?: string | null;
35
+ /** Retry sequence per order: 1 = first attempt. */
36
+ attemptNo: number;
37
+ createdAt: Date;
38
+ updatedAt: Date;
39
+ }
40
+ //# sourceMappingURL=shipment.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/shipment.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;;;;;;GASG;AACH,qBAEa,QAAQ;IACnB,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,WAAW,GACX,QAAQ,GACR,mBAAmB,GACnB,iBAAiB,GACjB,eAAe,GACf,WAAW,CAAC;IAGhB,EAAE,EAAE,MAAM,CAAgB;IAI1B,OAAO,EAAG,MAAM,CAAC;IAGjB,gBAAgB,EAAG,MAAM,CAAC;IAE1B;;;;;;OAMG;IAGH,MAAM,EAAE,SAAS,GAAG,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAAa;IAEzE,gFAAgF;IAEhF,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAElC,4DAA4D;IAE5D,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAEjD;;;;OAIG;IAEH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B,mDAAmD;IAEnD,SAAS,EAAE,MAAM,CAAK;IAGtB,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,98 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ import { randomUUID } from 'crypto';
13
+ /**
14
+ * Shipment (`Przesyłka` / `Paczka`) — a first-class record of one
15
+ * shipment-generation attempt against an Order (feature 035). An Order may
16
+ * have several: a failed generation followed by a successful retry, or a
17
+ * future split into multiple parcels. The delivery-side twin of `Payment`.
18
+ *
19
+ * `status` is the shipment-process status (pending → success | failure),
20
+ * distinct from the Order status the method maps to on a `receive_shipment`
21
+ * outcome.
22
+ */
23
+ let Shipment = class Shipment {
24
+ [OptionalProps];
25
+ id = randomUUID();
26
+ orderId;
27
+ deliveryMethodId;
28
+ /**
29
+ * `pending` → `success` | `failure` is the carrier's path. `pending_manual`
30
+ * is the path where there was no carrier: the adapter that would have been
31
+ * asked belongs to a module that is not present, so the row was opened
32
+ * without a request ever leaving the platform and a person has to finish it
33
+ * (issue #250). Same word as the refund settlement state, on purpose.
34
+ */
35
+ status = 'pending';
36
+ /** Carrier/adapter reference (e.g. tracking number) for idempotent matching. */
37
+ externalReference;
38
+ /** Adapter/carrier payload captured on receive_shipment. */
39
+ providerDetails;
40
+ /**
41
+ * Populated on a failure outcome, and on `pending_manual` with the sentence
42
+ * naming the module that was not asked — the two cases where a row needs to
43
+ * say why it is not a plain success.
44
+ */
45
+ failureReason;
46
+ /** Retry sequence per order: 1 = first attempt. */
47
+ attemptNo = 1;
48
+ createdAt = new Date();
49
+ updatedAt = new Date();
50
+ };
51
+ __decorate([
52
+ PrimaryKey({ type: 'uuid' }),
53
+ __metadata("design:type", String)
54
+ ], Shipment.prototype, "id", void 0);
55
+ __decorate([
56
+ Property({ type: 'uuid' }),
57
+ Index(),
58
+ __metadata("design:type", String)
59
+ ], Shipment.prototype, "orderId", void 0);
60
+ __decorate([
61
+ Property({ type: 'uuid' }),
62
+ __metadata("design:type", String)
63
+ ], Shipment.prototype, "deliveryMethodId", void 0);
64
+ __decorate([
65
+ Property({ type: 'string', length: 32 }),
66
+ Index(),
67
+ __metadata("design:type", String)
68
+ ], Shipment.prototype, "status", void 0);
69
+ __decorate([
70
+ Property({ type: 'string', length: 255, nullable: true }),
71
+ __metadata("design:type", Object)
72
+ ], Shipment.prototype, "externalReference", void 0);
73
+ __decorate([
74
+ Property({ type: 'json', nullable: true }),
75
+ __metadata("design:type", Object)
76
+ ], Shipment.prototype, "providerDetails", void 0);
77
+ __decorate([
78
+ Property({ type: 'text', nullable: true }),
79
+ __metadata("design:type", Object)
80
+ ], Shipment.prototype, "failureReason", void 0);
81
+ __decorate([
82
+ Property({ type: 'integer' }),
83
+ __metadata("design:type", Number)
84
+ ], Shipment.prototype, "attemptNo", void 0);
85
+ __decorate([
86
+ Property({ type: 'datetime', onCreate: () => new Date() }),
87
+ __metadata("design:type", Date)
88
+ ], Shipment.prototype, "createdAt", void 0);
89
+ __decorate([
90
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
91
+ __metadata("design:type", Date)
92
+ ], Shipment.prototype, "updatedAt", void 0);
93
+ Shipment = __decorate([
94
+ GlobalEntity(),
95
+ Entity({ tableName: 'shipments' })
96
+ ], Shipment);
97
+ export { Shipment };
98
+ //# sourceMappingURL=shipment.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/shipment.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;;;;GASG;AAGI,IAAM,QAAQ,GAAd,MAAM,QAAQ;IACnB,CAAC,aAAa,CAAC,CAQC;IAGhB,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,OAAO,CAAU;IAGjB,gBAAgB,CAAU;IAE1B;;;;;;OAMG;IAGH,MAAM,GAAyD,SAAS,CAAC;IAEzE,gFAAgF;IAEhF,iBAAiB,CAAiB;IAElC,4DAA4D;IAE5D,eAAe,CAAkC;IAEjD;;;;OAIG;IAEH,aAAa,CAAiB;IAE9B,mDAAmD;IAEnD,SAAS,GAAW,CAAC,CAAC;IAGtB,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AA7CC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oCACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE;;yCACS;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;kDACD;AAW1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxC,KAAK,EAAE;;wCACiE;AAIzE;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACxB;AAIlC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACM;AAQjD;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CACb;AAI9B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;2CACR;AAGtB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;2CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;2CAAc;AAxDlB,QAAQ;IAFpB,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;GACtB,QAAQ,CAyDpB"}
@@ -0,0 +1,71 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { EventBus } from '@endora-commerce/platform/events';
3
+ import type { ShippingAdapterRegistryPort } from '@endora-commerce/contracts';
4
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
5
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
6
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
7
+ import { ShipmentService } from './services/shipment-service.js';
8
+ import { ReceiveShipmentHandler } from './services/receive-shipment-handler.js';
9
+ import { ShipmentEmailNotifier } from './services/shipment-email-notifier.js';
10
+ import type { ShipmentEmailNotifierDeps } from './services/shipment-email-notifier.js';
11
+ import { AutoShipmentOnPaidNotifier } from './services/auto-shipment-on-paid.js';
12
+ import { Shipment } from './entities/shipment.entity.js';
13
+ /**
14
+ * `shipments` — a module that owned everything except its own registration
15
+ * (feature 072, wave 2, T124).
16
+ *
17
+ * It has a manifest, an entity, two services, a route file, a transactional
18
+ * email and a migration — and none of it was wired by the module. `orders`
19
+ * imported both services and the route file, constructed them inside its own
20
+ * plugin and mounted them there, which its own manifest comment recorded as
21
+ * "routes are wired through the commerce composition root". So the shipment
22
+ * lifecycle existed for exactly as long as `orders` happened to be composed,
23
+ * and Principle I's "design every module so it can be detached" was false for
24
+ * it in the same literal way it was for `audit_logs` before T084.
25
+ *
26
+ * Nothing in the split needed inventing: both registries the services take are
27
+ * already ports `delivery_methods` provides, and the manifest already declares
28
+ * that dependency. What changes is who registers the routes — and therefore
29
+ * what switching `shipments` off does. Before, nothing: the module had no gate
30
+ * of its own and its routes answered whenever `orders` was on.
31
+ *
32
+ * The `shipment.created.v1` notifier moves in for the same reason. Both roots
33
+ * built it and called `attach(eventBus)` directly, so it subscribed to the raw
34
+ * bus rather than through `subscribeForModule` — a shipment-created e-mail went
35
+ * out whether or not this module was on. `ctx.subscribe` gates it.
36
+ *
37
+ * The transactional-email *sender* stays contributed: it is announced by
38
+ * `transactional_emails` through a callback a root holds, so a port would point
39
+ * the dependency at a module that does not yet have the value. Absent, the
40
+ * notifier is silent — which is exactly what it already did when the sender had
41
+ * not been exposed.
42
+ */
43
+ /** What `shipments` resolves from the container, and the names it owns. */
44
+ export interface ShipmentsCradle {
45
+ readonly emFactory: () => EntityManager;
46
+ readonly eventBus: EventBus;
47
+ /** Platform-owned name — the sink for the `pending_manual` audit row. */
48
+ readonly auditLogService: AuditPort;
49
+ readonly requireAdmin: RequireAdminFactory;
50
+ readonly shippingAdapterRegistry: ShippingAdapterRegistryPort;
51
+ readonly shipmentService: ShipmentService;
52
+ readonly receiveShipmentHandler: ReceiveShipmentHandler;
53
+ /** Contribution point: absent means a shipment-created e-mail is not sent. */
54
+ readonly shipmentEmailSender: ShipmentEmailNotifierDeps['getTransactionalEmailSender'];
55
+ readonly shipmentEmailNotifier: ShipmentEmailNotifier;
56
+ readonly autoShipmentOnPaid: AutoShipmentOnPaidNotifier;
57
+ }
58
+ export declare function registerModule(ctx: ModuleContext): void;
59
+ /**
60
+ * The module's persisted entity classes, on the `./backend` subpath, as one
61
+ * array and **no named class export** (D-168).
62
+ *
63
+ * This is the shape the platform reads when the package is *installed*: the
64
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
65
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
66
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
67
+ * the package pass of `check-entity-tenant-classification`. A missing array is
68
+ * answered with `[]` — zero entities registered, no error anywhere.
69
+ */
70
+ export declare const entities: (typeof Shipment)[];
71
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAUV,2BAA2B,EAE5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAIjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAGhF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,uBAAuB,EAAE,2BAA2B,CAAC;IAC9D,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAC;IACxD,8EAA8E;IAC9E,QAAQ,CAAC,mBAAmB,EAAE,yBAAyB,CAAC,6BAA6B,CAAC,CAAC;IACvF,QAAQ,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IACtD,QAAQ,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;CACzD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA+LvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,qBAEpB,CAAC"}
@@ -0,0 +1,151 @@
1
+ import { lazyPort } from '@endora-commerce/platform/kernel';
2
+ import { ShipmentService } from './services/shipment-service.js';
3
+ import { ShipmentUsageService } from './services/shipment-usage.service.js';
4
+ import { ShipmentReadService } from './services/shipment-read.service.js';
5
+ import { resolveShippingEmailRenderer } from './services/shipping-email-renderer.js';
6
+ import { ReceiveShipmentHandler } from './services/receive-shipment-handler.js';
7
+ import { registerShipmentsRoutes } from './routes.js';
8
+ import { ShipmentEmailNotifier } from './services/shipment-email-notifier.js';
9
+ import { AutoShipmentOnPaidNotifier } from './services/auto-shipment-on-paid.js';
10
+ import { SHIPMENT_CREATED_DEFAULT } from './email-templates/transactional-defaults.js';
11
+ import { Shipment } from './entities/shipment.entity.js';
12
+ export function registerModule(ctx) {
13
+ ctx.di.register({
14
+ // Contribution point, defaulted to no sender: a deployment without a
15
+ // transactional-email surface sends nothing rather than failing to ship.
16
+ shipmentEmailSender: ctx
17
+ .asFunction(() => () => undefined)
18
+ .singleton(),
19
+ shipmentEmailNotifier: ctx
20
+ .asFunction(({ emFactory }) => new ShipmentEmailNotifier({
21
+ emFactory,
22
+ orderRead: lazyPort(ctx, 'orderReadPort'),
23
+ customerAccountRead: lazyPort(ctx, 'customerAccountReadPort'),
24
+ // Read per call: a root contributes the sender after
25
+ // `transactional_emails` announces it, which is later than this.
26
+ getTransactionalEmailSender: () => ctx.cradle().shipmentEmailSender(),
27
+ }))
28
+ .singleton(),
29
+ autoShipmentOnPaid: ctx
30
+ .asFunction(({ emFactory }) => new AutoShipmentOnPaidNotifier(emFactory, lazyPort(ctx, 'shippingAdapterRegistry'), lazyPort(ctx, 'orderReadPort'), lazyPort(ctx, 'deliveryMethodReadPort'), lazyPort(ctx, 'shipmentService')))
31
+ .singleton(),
32
+ });
33
+ ctx.di.providePort('shipmentService', ctx
34
+ .asFunction(({ emFactory, eventBus, auditLogService }) => new ShipmentService(emFactory, lazyPort(ctx, 'shippingAdapterRegistry'), lazyPort(ctx, 'orderReadPort'), lazyPort(ctx, 'deliveryMethodReadPort'), auditLogService, eventBus))
35
+ .singleton());
36
+ /**
37
+ * Feature 075 — the delete guard `delivery_methods` used to write itself.
38
+ *
39
+ * That module counted this one's table in raw SQL, which names no import
40
+ * specifier and so crossed the boundary invisibly until D-87 gave
41
+ * `check:module-boundary` a second predicate. The count is a question about
42
+ * shipment rows, so it is answered here.
43
+ *
44
+ * A port and not a contribution into a registry `delivery_methods` owns:
45
+ * a contributed edge is filtered by contributor at enumeration, so switching
46
+ * this module off would silently withdraw the guard — and with no foreign key
47
+ * on `shipments.delivery_method_id` that means orphaned history rather than a
48
+ * refused delete. The consumer decides this module's presence itself and
49
+ * refuses; see its manifest's `nonBindingDependencies` entry.
50
+ */
51
+ ctx.di.providePort('shipmentUsagePort', ctx
52
+ .asFunction(({ emFactory }) => new ShipmentUsageService(emFactory))
53
+ .singleton());
54
+ /**
55
+ * Feature 068 — "what state is this shipment in?", asked by a carrier module.
56
+ *
57
+ * `inpost` loaded this module's `Shipment` entity for it: once to correlate an
58
+ * inbound ShipX webhook that names only the carrier's own id, and once for the
59
+ * admin label route. Both are reads of a row the caller does not write, so
60
+ * they are a port rather than an apply seam, and both are on this module's own
61
+ * `EntityManager`.
62
+ */
63
+ ctx.di.providePort('shipmentReadPort', ctx
64
+ .asFunction(({ emFactory }) => new ShipmentReadService(emFactory))
65
+ .singleton());
66
+ /**
67
+ * Feature 075, Phase P — the shipping line of the order-confirmation e-mail.
68
+ *
69
+ * The delivery-side twin of `payments`' `paymentEmailRendererPort`, and the
70
+ * same collapse: `orders` reaches this module's resolver and then calls the
71
+ * function it gets back. Doing both behind one call is what keeps the
72
+ * default text on this side — a consumer that resolved a renderer and got
73
+ * `undefined` would have to hold a copy of it, and two copies of a default
74
+ * are how a default stops being one.
75
+ */
76
+ ctx.di.providePort('shippingEmailRendererPort', ctx
77
+ .asFunction(() => ({
78
+ render: (rendererKey, emailContext) => resolveShippingEmailRenderer(rendererKey)(emailContext),
79
+ }))
80
+ .singleton());
81
+ /**
82
+ * The carrier callback ingress.
83
+ *
84
+ * **`orderTransitionPort` replaces two of the names this took** (feature 085
85
+ * Phase D). `orderStatusAnnouncePort` goes because the transition seam emits
86
+ * the templated `.after` events itself, so announcing beside it would double
87
+ * every subscriber's reaction; `shippingOrderStatusRegistry` goes because "is
88
+ * this a status code" was never the question a carrier callback needed
89
+ * answered, and the port answers the real one — may this order go there —
90
+ * against the configured graph.
91
+ */
92
+ ctx.di.providePort('receiveShipmentHandler', ctx
93
+ .asFunction(({ emFactory, eventBus }) => new ReceiveShipmentHandler(emFactory, lazyPort(ctx, 'deliveryMethodReadPort'), lazyPort(ctx, 'orderTransitionPort'), ctx.log, eventBus))
94
+ .singleton());
95
+ ctx.subscribe('shipment.created.v1', async (payload) => {
96
+ const { orderId, shipmentId, status } = payload;
97
+ // The state travels with the event because the notifier's decision depends
98
+ // on it: a shipment no carrier was asked for sends no "your order has
99
+ // shipped" (issue #250).
100
+ await ctx.cradle().shipmentEmailNotifier.notify(orderId, shipmentId, status);
101
+ });
102
+ ctx.subscribe('payment.received.v1', async (payload) => {
103
+ const { orderId } = payload;
104
+ await ctx.cradle().autoShipmentOnPaid.maybeCreate(orderId);
105
+ });
106
+ ctx.routes(async (app) => {
107
+ const { requireAdmin } = ctx.cradle();
108
+ await registerShipmentsRoutes(app, {
109
+ requireAdmin,
110
+ // Lazily, even though the module owns both ports: route *registration*
111
+ // runs inside `buildServer` whatever the module's effective state is, so
112
+ // destructuring the gates here would stop the next start instead of
113
+ // stopping the routes (D-40).
114
+ receiveHandler: lazyPort(ctx, 'receiveShipmentHandler'),
115
+ shipmentService: lazyPort(ctx, 'shipmentService'),
116
+ });
117
+ });
118
+ /**
119
+ * The default subject and content for the 1 transactional email this
120
+ * module declares in its manifest (T143a).
121
+ *
122
+ * These were fourteen `emailDefaultsRegistry.register(...)` calls in
123
+ * `composition.ts`, each importing a template constant out of the module that
124
+ * owns it — a root reaching into seven modules to hand their own content to
125
+ * an eighth. Each module registers its own now.
126
+ *
127
+ * `ctx.onBoot` rather than a registration: the registry is *read* once, by
128
+ * `transactional_emails`' boot reconciler inside its plugin body. Boot hooks
129
+ * run during composition and plugin bodies only when the Fastify app is
130
+ * built, so this always lands first — by construction, not by ordering luck.
131
+ */
132
+ ctx.onBoot(async () => {
133
+ const defaults = lazyPort(ctx, 'emailDefaultsPort');
134
+ defaults.register('shipment_created', SHIPMENT_CREATED_DEFAULT, 'shipments');
135
+ });
136
+ }
137
+ /**
138
+ * The module's persisted entity classes, on the `./backend` subpath, as one
139
+ * array and **no named class export** (D-168).
140
+ *
141
+ * This is the shape the platform reads when the package is *installed*: the
142
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
143
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
144
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
145
+ * the package pass of `check-entity-tenant-classification`. A missing array is
146
+ * answered with `[]` — zero entities registered, no error anywhere.
147
+ */
148
+ export const entities = [
149
+ Shipment,
150
+ ];
151
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { ReceiveShipmentHandler } from './services/receive-shipment-handler.js';
3
+ import type { ShipmentService } from './services/shipment-service.js';
4
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
5
+ /**
6
+ * Shipments routes (feature 035).
7
+ *
8
+ * POST /api/v1/admin/orders/:id/shipments — shipment_created (FR-021),
9
+ * and the retry path (FR-024)
10
+ * GET /api/v1/admin/orders/:id/shipments — full shipment history (FR-026)
11
+ * POST /api/v1/shipments/receive — receive_shipment ingress (FR-022)
12
+ *
13
+ * The ingress is admin-guarded for the MVP; a signed carrier-webhook auth path
14
+ * is a follow-up (the offline reference adapters are settled by an admin anyway).
15
+ *
16
+ * `POST .../shipments/retry` was removed by issue #257. It opened attempt n+1
17
+ * and contacted no carrier, which is exactly the silence issue #250 removed
18
+ * from the generate path; its own contract called it "equivalent to calling the
19
+ * generate route again", and the generate route is what every caller used —
20
+ * including the recovery button #250 added, which was pointed at generate
21
+ * precisely because retry asked nobody. Retrying is generating again.
22
+ */
23
+ export interface ShipmentsRoutesDeps {
24
+ requireAdmin: RequireAdminFactory;
25
+ receiveHandler: ReceiveShipmentHandler;
26
+ shipmentService: ShipmentService;
27
+ }
28
+ export declare function registerShipmentsRoutes(app: FastifyInstance, deps: ShipmentsRoutesDeps): Promise<void>;
29
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,mBAAmB,CAAC;IAClC,cAAc,EAAE,sBAAsB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;CAClC;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
@@ -0,0 +1,35 @@
1
+ import { receiveShipmentSchema } from '@endora-commerce/contracts';
2
+ export async function registerShipmentsRoutes(app, deps) {
3
+ app.post('/api/v1/admin/orders/:id/shipments', { preHandler: deps.requireAdmin('orders:write') }, async (request, reply) => {
4
+ const shipment = await deps.shipmentService.createShipment(request.params.id);
5
+ reply.status(201);
6
+ return { data: serializeShipment(shipment) };
7
+ });
8
+ app.get('/api/v1/admin/orders/:id/shipments', { preHandler: deps.requireAdmin('orders:read') }, async (request) => {
9
+ const shipments = await deps.shipmentService.listForOrder(request.params.id);
10
+ return { data: shipments.map(serializeShipment) };
11
+ });
12
+ app.post('/api/v1/shipments/receive', {
13
+ preHandler: deps.requireAdmin('orders:write'),
14
+ schema: { body: receiveShipmentSchema },
15
+ }, async (request) => {
16
+ const body = receiveShipmentSchema.parse(request.body);
17
+ const result = await deps.receiveHandler.receive(body);
18
+ return { data: result };
19
+ });
20
+ }
21
+ function serializeShipment(s) {
22
+ return {
23
+ id: s.id,
24
+ orderId: s.orderId,
25
+ deliveryMethodId: s.deliveryMethodId,
26
+ status: s.status,
27
+ externalReference: s.externalReference ?? null,
28
+ providerDetails: s.providerDetails ?? null,
29
+ failureReason: s.failureReason ?? null,
30
+ attemptNo: s.attemptNo,
31
+ createdAt: s.createdAt.toISOString(),
32
+ updatedAt: s.updatedAt.toISOString(),
33
+ };
34
+ }
35
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AA8BnE,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAoB,EACpB,IAAyB;IAEzB,GAAG,CAAC,IAAI,CACN,oCAAoC,EACpC,EAAE,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,EACjD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9E,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC/C,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,oCAAoC,EACpC,EAAE,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,EAChD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7E,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACpD,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,IAAI,CACN,2BAA2B,EAC3B;QACE,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC;QAC7C,MAAM,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE;KACxC,EACD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAW;IACpC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;QACpC,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,IAAI,IAAI;QAC9C,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,IAAI;QAC1C,aAAa,EAAE,CAAC,CAAC,aAAa,IAAI,IAAI;QACtC,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE;QACpC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { DeliveryMethodReadPort, OrderReadPort, ShippingAdapterRegistryPort } from '@endora-commerce/contracts';
3
+ import type { ShipmentService } from './shipment-service.js';
4
+ /**
5
+ * Creates a shipment when payment settles and the order's shipping adapter
6
+ * opts in via `shouldAutoCreateOnPaid()` (feature 068).
7
+ *
8
+ * Wired from `shipments/backend.ts` through `ctx.subscribe('payment.received.v1')`
9
+ * — never via a bare `eventBus.on` (issue #107). Best-effort: ordinary failures
10
+ * are swallowed so payment settlement is never blocked; `ModuleDisabledError`
11
+ * is re-thrown so fail-closed stays fail-closed.
12
+ *
13
+ * The order and the delivery method are read over their owners' ports, exactly
14
+ * as `ShipmentService` reads them (feature 075 Phase C): both rows are ones this
15
+ * operation does not modify, so nothing is lost by leaving them on the owner's
16
+ * EntityManager, and both fail closed when their owner is off.
17
+ */
18
+ export declare class AutoShipmentOnPaidNotifier {
19
+ private readonly emFactory;
20
+ private readonly registry;
21
+ private readonly orderRead;
22
+ private readonly deliveryMethodRead;
23
+ private readonly shipmentService;
24
+ constructor(emFactory: () => EntityManager, registry: ShippingAdapterRegistryPort, orderRead: OrderReadPort, deliveryMethodRead: DeliveryMethodReadPort, shipmentService: ShipmentService);
25
+ /** Exposed for unit tests. */
26
+ maybeCreate(orderId: string): Promise<void>;
27
+ }
28
+ //# sourceMappingURL=auto-shipment-on-paid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-shipment-on-paid.d.ts","sourceRoot":"","sources":["../../../src/backend/services/auto-shipment-on-paid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,sBAAsB,EACtB,aAAa,EACb,2BAA2B,EAC5B,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;;;;;;GAaG;AACH,qBAAa,0BAA0B;IAEnC,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,eAAe;gBAJf,SAAS,EAAE,MAAM,aAAa,EAC9B,QAAQ,EAAE,2BAA2B,EACrC,SAAS,EAAE,aAAa,EACxB,kBAAkB,EAAE,sBAAsB,EAC1C,eAAe,EAAE,eAAe;IAGnD,8BAA8B;IACxB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4BlD"}