@endora-commerce/mod-quote-requests 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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/admin/index.d.ts +26 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +56 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/RfqCreatePage.d.ts +9 -0
  8. package/dist/admin/pages/RfqCreatePage.d.ts.map +1 -0
  9. package/dist/admin/pages/RfqCreatePage.js +163 -0
  10. package/dist/admin/pages/RfqCreatePage.js.map +1 -0
  11. package/dist/admin/pages/RfqDetail.d.ts +9 -0
  12. package/dist/admin/pages/RfqDetail.d.ts.map +1 -0
  13. package/dist/admin/pages/RfqDetail.js +399 -0
  14. package/dist/admin/pages/RfqDetail.js.map +1 -0
  15. package/dist/admin/pages/RfqList.d.ts +9 -0
  16. package/dist/admin/pages/RfqList.d.ts.map +1 -0
  17. package/dist/admin/pages/RfqList.js +214 -0
  18. package/dist/admin/pages/RfqList.js.map +1 -0
  19. package/dist/admin/validity.d.ts +52 -0
  20. package/dist/admin/validity.d.ts.map +1 -0
  21. package/dist/admin/validity.js +61 -0
  22. package/dist/admin/validity.js.map +1 -0
  23. package/dist/backend/entities/quote-request-event.entity.d.ts +24 -0
  24. package/dist/backend/entities/quote-request-event.entity.d.ts.map +1 -0
  25. package/dist/backend/entities/quote-request-event.entity.js +67 -0
  26. package/dist/backend/entities/quote-request-event.entity.js.map +1 -0
  27. package/dist/backend/entities/quote-request-item.entity.d.ts +34 -0
  28. package/dist/backend/entities/quote-request-item.entity.d.ts.map +1 -0
  29. package/dist/backend/entities/quote-request-item.entity.js +119 -0
  30. package/dist/backend/entities/quote-request-item.entity.js.map +1 -0
  31. package/dist/backend/entities/quote-request-notification-event.entity.d.ts +23 -0
  32. package/dist/backend/entities/quote-request-notification-event.entity.d.ts.map +1 -0
  33. package/dist/backend/entities/quote-request-notification-event.entity.js +72 -0
  34. package/dist/backend/entities/quote-request-notification-event.entity.js.map +1 -0
  35. package/dist/backend/entities/quote-request-revision.entity.d.ts +35 -0
  36. package/dist/backend/entities/quote-request-revision.entity.d.ts.map +1 -0
  37. package/dist/backend/entities/quote-request-revision.entity.js +67 -0
  38. package/dist/backend/entities/quote-request-revision.entity.js.map +1 -0
  39. package/dist/backend/entities/quote-request.entity.d.ts +90 -0
  40. package/dist/backend/entities/quote-request.entity.d.ts.map +1 -0
  41. package/dist/backend/entities/quote-request.entity.js +191 -0
  42. package/dist/backend/entities/quote-request.entity.js.map +1 -0
  43. package/dist/backend/index.d.ts +120 -0
  44. package/dist/backend/index.d.ts.map +1 -0
  45. package/dist/backend/index.js +361 -0
  46. package/dist/backend/index.js.map +1 -0
  47. package/dist/backend/plugin.d.ts +95 -0
  48. package/dist/backend/plugin.d.ts.map +1 -0
  49. package/dist/backend/plugin.js +152 -0
  50. package/dist/backend/plugin.js.map +1 -0
  51. package/dist/backend/routes.admin.d.ts +13 -0
  52. package/dist/backend/routes.admin.d.ts.map +1 -0
  53. package/dist/backend/routes.admin.js +82 -0
  54. package/dist/backend/routes.admin.js.map +1 -0
  55. package/dist/backend/routes.customer.d.ts +18 -0
  56. package/dist/backend/routes.customer.d.ts.map +1 -0
  57. package/dist/backend/routes.customer.js +98 -0
  58. package/dist/backend/routes.customer.js.map +1 -0
  59. package/dist/backend/routes.sales-reps.d.ts +12 -0
  60. package/dist/backend/routes.sales-reps.d.ts.map +1 -0
  61. package/dist/backend/routes.sales-reps.js +57 -0
  62. package/dist/backend/routes.sales-reps.js.map +1 -0
  63. package/dist/backend/services/order-completion-reactor.d.ts +32 -0
  64. package/dist/backend/services/order-completion-reactor.d.ts.map +1 -0
  65. package/dist/backend/services/order-completion-reactor.js +57 -0
  66. package/dist/backend/services/order-completion-reactor.js.map +1 -0
  67. package/dist/backend/services/quote-request-business-id-generator.d.ts +46 -0
  68. package/dist/backend/services/quote-request-business-id-generator.d.ts.map +1 -0
  69. package/dist/backend/services/quote-request-business-id-generator.js +69 -0
  70. package/dist/backend/services/quote-request-business-id-generator.js.map +1 -0
  71. package/dist/backend/services/quote-request-read-port.d.ts +36 -0
  72. package/dist/backend/services/quote-request-read-port.d.ts.map +1 -0
  73. package/dist/backend/services/quote-request-read-port.js +96 -0
  74. package/dist/backend/services/quote-request-read-port.js.map +1 -0
  75. package/dist/backend/services/raised-on-channel.d.ts +23 -0
  76. package/dist/backend/services/raised-on-channel.d.ts.map +1 -0
  77. package/dist/backend/services/raised-on-channel.js +26 -0
  78. package/dist/backend/services/raised-on-channel.js.map +1 -0
  79. package/dist/backend/services/rfq-admin-service.d.ts +75 -0
  80. package/dist/backend/services/rfq-admin-service.d.ts.map +1 -0
  81. package/dist/backend/services/rfq-admin-service.js +487 -0
  82. package/dist/backend/services/rfq-admin-service.js.map +1 -0
  83. package/dist/backend/services/rfq-event-service.d.ts +32 -0
  84. package/dist/backend/services/rfq-event-service.d.ts.map +1 -0
  85. package/dist/backend/services/rfq-event-service.js +28 -0
  86. package/dist/backend/services/rfq-event-service.js.map +1 -0
  87. package/dist/backend/services/rfq-expiry-worker.d.ts +50 -0
  88. package/dist/backend/services/rfq-expiry-worker.d.ts.map +1 -0
  89. package/dist/backend/services/rfq-expiry-worker.js +70 -0
  90. package/dist/backend/services/rfq-expiry-worker.js.map +1 -0
  91. package/dist/backend/services/rfq-notification-service.d.ts +36 -0
  92. package/dist/backend/services/rfq-notification-service.d.ts.map +1 -0
  93. package/dist/backend/services/rfq-notification-service.js +52 -0
  94. package/dist/backend/services/rfq-notification-service.js.map +1 -0
  95. package/dist/backend/services/rfq-revision-service.d.ts +70 -0
  96. package/dist/backend/services/rfq-revision-service.d.ts.map +1 -0
  97. package/dist/backend/services/rfq-revision-service.js +120 -0
  98. package/dist/backend/services/rfq-revision-service.js.map +1 -0
  99. package/dist/backend/services/rfq-service.d.ts +140 -0
  100. package/dist/backend/services/rfq-service.d.ts.map +1 -0
  101. package/dist/backend/services/rfq-service.js +786 -0
  102. package/dist/backend/services/rfq-service.js.map +1 -0
  103. package/dist/backend/services/sales-channel-attributions.d.ts +38 -0
  104. package/dist/backend/services/sales-channel-attributions.d.ts.map +1 -0
  105. package/dist/backend/services/sales-channel-attributions.js +48 -0
  106. package/dist/backend/services/sales-channel-attributions.js.map +1 -0
  107. package/dist/manifest.d.ts +203 -0
  108. package/dist/manifest.d.ts.map +1 -0
  109. package/dist/manifest.js +277 -0
  110. package/dist/manifest.js.map +1 -0
  111. package/dist/migrations/20260424T190112_quote_requests_init.d.ts +19 -0
  112. package/dist/migrations/20260424T190112_quote_requests_init.d.ts.map +1 -0
  113. package/dist/migrations/20260424T190112_quote_requests_init.js +65 -0
  114. package/dist/migrations/20260424T190112_quote_requests_init.js.map +1 -0
  115. package/dist/migrations/20260503T141344_quote_requests_workflow.d.ts +37 -0
  116. package/dist/migrations/20260503T141344_quote_requests_workflow.d.ts.map +1 -0
  117. package/dist/migrations/20260503T141344_quote_requests_workflow.js +251 -0
  118. package/dist/migrations/20260503T141344_quote_requests_workflow.js.map +1 -0
  119. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.d.ts +10 -0
  120. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.d.ts.map +1 -0
  121. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.js +16 -0
  122. package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.js.map +1 -0
  123. package/dist/migrations/20260611T140417_quote_requests_business_id.d.ts +24 -0
  124. package/dist/migrations/20260611T140417_quote_requests_business_id.d.ts.map +1 -0
  125. package/dist/migrations/20260611T140417_quote_requests_business_id.js +34 -0
  126. package/dist/migrations/20260611T140417_quote_requests_business_id.js.map +1 -0
  127. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.d.ts +21 -0
  128. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.d.ts.map +1 -0
  129. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.js +29 -0
  130. package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.js.map +1 -0
  131. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.d.ts +12 -0
  132. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.d.ts.map +1 -0
  133. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.js +16 -0
  134. package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.js.map +1 -0
  135. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts +34 -0
  136. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts.map +1 -0
  137. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js +57 -0
  138. package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js.map +1 -0
  139. package/dist/migrations/index.d.ts +53 -0
  140. package/dist/migrations/index.d.ts.map +1 -0
  141. package/dist/migrations/index.js +61 -0
  142. package/dist/migrations/index.js.map +1 -0
  143. package/dist/migrations/status-mapping.d.ts +18 -0
  144. package/dist/migrations/status-mapping.d.ts.map +1 -0
  145. package/dist/migrations/status-mapping.js +28 -0
  146. package/dist/migrations/status-mapping.js.map +1 -0
  147. package/docs/quote_requests.md +168 -0
  148. package/i18n/en.json +14 -0
  149. package/i18n/pl.json +14 -0
  150. package/package.json +99 -0
  151. package/tailwind.css +14 -0
@@ -0,0 +1,23 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * QuoteRequestNotificationEvent — one row per recipient × channel ×
4
+ * source event. The unique index on
5
+ * (quote_request_id, source_event_id, recipient*, channel) enforces
6
+ * once-only delivery (FR-030 + research §R4).
7
+ */
8
+ export type QuoteRequestNotificationChannel = 'email' | 'in_app';
9
+ export type QuoteRequestNotificationStatus = 'queued' | 'sent' | 'failed';
10
+ export declare class QuoteRequestNotificationEvent {
11
+ [OptionalProps]?: 'id' | 'enqueuedAt' | 'sentAt' | 'error' | 'recipientAdminUserId' | 'recipientCustomerAccountId' | 'status';
12
+ id: string;
13
+ quoteRequestId: string;
14
+ sourceEventId: string;
15
+ recipientAdminUserId?: string | null;
16
+ recipientCustomerAccountId?: string | null;
17
+ channel: QuoteRequestNotificationChannel;
18
+ status: QuoteRequestNotificationStatus;
19
+ error?: string | null;
20
+ enqueuedAt: Date;
21
+ sentAt?: Date | null;
22
+ }
23
+ //# sourceMappingURL=quote-request-notification-event.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request-notification-event.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/quote-request-notification-event.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;;GAKG;AACH,MAAM,MAAM,+BAA+B,GAAG,OAAO,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,8BAA8B,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1E,qBAEa,6BAA6B;IACxC,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,YAAY,GACZ,QAAQ,GACR,OAAO,GACP,sBAAsB,GACtB,4BAA4B,GAC5B,QAAQ,CAAC;IAGb,EAAE,EAAE,MAAM,CAAgB;IAI1B,cAAc,EAAG,MAAM,CAAC;IAGxB,aAAa,EAAG,MAAM,CAAC;IAGvB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrC,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG3C,OAAO,EAAG,+BAA+B,CAAC;IAG1C,MAAM,EAAE,8BAA8B,CAAY;IAGlD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGtB,UAAU,EAAE,IAAI,CAAc;IAG9B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB"}
@@ -0,0 +1,72 @@
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
+ let QuoteRequestNotificationEvent = class QuoteRequestNotificationEvent {
14
+ [OptionalProps];
15
+ id = randomUUID();
16
+ quoteRequestId;
17
+ sourceEventId;
18
+ recipientAdminUserId;
19
+ recipientCustomerAccountId;
20
+ channel;
21
+ status = 'queued';
22
+ error;
23
+ enqueuedAt = new Date();
24
+ sentAt;
25
+ };
26
+ __decorate([
27
+ PrimaryKey({ type: 'uuid' }),
28
+ __metadata("design:type", String)
29
+ ], QuoteRequestNotificationEvent.prototype, "id", void 0);
30
+ __decorate([
31
+ Property({ type: 'uuid' }),
32
+ Index(),
33
+ __metadata("design:type", String)
34
+ ], QuoteRequestNotificationEvent.prototype, "quoteRequestId", void 0);
35
+ __decorate([
36
+ Property({ type: 'uuid' }),
37
+ __metadata("design:type", String)
38
+ ], QuoteRequestNotificationEvent.prototype, "sourceEventId", void 0);
39
+ __decorate([
40
+ Property({ type: 'uuid', nullable: true }),
41
+ __metadata("design:type", Object)
42
+ ], QuoteRequestNotificationEvent.prototype, "recipientAdminUserId", void 0);
43
+ __decorate([
44
+ Property({ type: 'uuid', nullable: true }),
45
+ __metadata("design:type", Object)
46
+ ], QuoteRequestNotificationEvent.prototype, "recipientCustomerAccountId", void 0);
47
+ __decorate([
48
+ Property({ type: 'string', length: 16 }),
49
+ __metadata("design:type", String)
50
+ ], QuoteRequestNotificationEvent.prototype, "channel", void 0);
51
+ __decorate([
52
+ Property({ type: 'string', length: 16 }),
53
+ __metadata("design:type", String)
54
+ ], QuoteRequestNotificationEvent.prototype, "status", void 0);
55
+ __decorate([
56
+ Property({ type: 'text', nullable: true }),
57
+ __metadata("design:type", Object)
58
+ ], QuoteRequestNotificationEvent.prototype, "error", void 0);
59
+ __decorate([
60
+ Property({ type: 'datetime', onCreate: () => new Date() }),
61
+ __metadata("design:type", Date)
62
+ ], QuoteRequestNotificationEvent.prototype, "enqueuedAt", void 0);
63
+ __decorate([
64
+ Property({ type: 'datetime', nullable: true }),
65
+ __metadata("design:type", Object)
66
+ ], QuoteRequestNotificationEvent.prototype, "sentAt", void 0);
67
+ QuoteRequestNotificationEvent = __decorate([
68
+ GlobalEntity(),
69
+ Entity({ tableName: 'quote_request_notification_events' })
70
+ ], QuoteRequestNotificationEvent);
71
+ export { QuoteRequestNotificationEvent };
72
+ //# sourceMappingURL=quote-request-notification-event.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request-notification-event.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/quote-request-notification-event.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;AAa7B,IAAM,6BAA6B,GAAnC,MAAM,6BAA6B;IACxC,CAAC,aAAa,CAAC,CAOF;IAGb,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,cAAc,CAAU;IAGxB,aAAa,CAAU;IAGvB,oBAAoB,CAAiB;IAGrC,0BAA0B,CAAiB;IAG3C,OAAO,CAAmC;IAG1C,MAAM,GAAmC,QAAQ,CAAC;IAGlD,KAAK,CAAiB;IAGtB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IAG9B,MAAM,CAAe;CACtB,CAAA;AA7BC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;yDACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE;;qEACgB;AAGxB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oEACJ;AAGvB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2EACN;AAGrC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iFACA;AAG3C;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;8DACC;AAG1C;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;6DACS;AAGlD;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4DACrB;AAGtB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAC/C,IAAI;iEAAc;AAG9B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6DAC1B;AAvCV,6BAA6B;IAFzC,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,mCAAmC,EAAE,CAAC;GAC9C,6BAA6B,CAwCzC"}
@@ -0,0 +1,35 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * QuoteRequestRevision — coarse-grained snapshot taken at every modify
4
+ * event. Drives the storefront comparison-with-last-seen view (US3).
5
+ *
6
+ * The `itemsSnapshot` jsonb stores the full set of lines as an array
7
+ * of plain objects so the diff calculator can compare two revisions
8
+ * with one read each. Diffs are not persisted — they are computed on
9
+ * the fly by RfqRevisionService.
10
+ */
11
+ export interface QuoteRequestRevisionLine {
12
+ productId: string;
13
+ variantId: string | null;
14
+ productName: string;
15
+ productSlug: string | null;
16
+ quantity: number;
17
+ desiredUnitPrice: number | null;
18
+ agreedUnitPrice: number | null;
19
+ lineNote: string | null;
20
+ lineCurrency: string;
21
+ discountPercent: number | null;
22
+ }
23
+ export declare class QuoteRequestRevision {
24
+ [OptionalProps]?: 'id' | 'createdAt' | 'createdByAdminUserId' | 'createdByCustomerAccountId' | 'headerNoteSnapshot' | 'previousRevisionId';
25
+ id: string;
26
+ quoteRequestId: string;
27
+ revisionNumber: number;
28
+ createdByAdminUserId?: string | null;
29
+ createdByCustomerAccountId?: string | null;
30
+ headerNoteSnapshot?: string | null;
31
+ itemsSnapshot: QuoteRequestRevisionLine[];
32
+ previousRevisionId?: string | null;
33
+ createdAt: Date;
34
+ }
35
+ //# sourceMappingURL=quote-request-revision.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request-revision.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/quote-request-revision.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;;;;;GAQG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,qBAEa,oBAAoB;IAC/B,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,sBAAsB,GACtB,4BAA4B,GAC5B,oBAAoB,GACpB,oBAAoB,CAAC;IAGzB,EAAE,EAAE,MAAM,CAAgB;IAI1B,cAAc,EAAG,MAAM,CAAC;IAGxB,cAAc,EAAG,MAAM,CAAC;IAGxB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrC,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG3C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGnC,aAAa,EAAG,wBAAwB,EAAE,CAAC;IAG3C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGnC,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,67 @@
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
+ let QuoteRequestRevision = class QuoteRequestRevision {
14
+ [OptionalProps];
15
+ id = randomUUID();
16
+ quoteRequestId;
17
+ revisionNumber;
18
+ createdByAdminUserId;
19
+ createdByCustomerAccountId;
20
+ headerNoteSnapshot;
21
+ itemsSnapshot;
22
+ previousRevisionId;
23
+ createdAt = new Date();
24
+ };
25
+ __decorate([
26
+ PrimaryKey({ type: 'uuid' }),
27
+ __metadata("design:type", String)
28
+ ], QuoteRequestRevision.prototype, "id", void 0);
29
+ __decorate([
30
+ Property({ type: 'uuid' }),
31
+ Index(),
32
+ __metadata("design:type", String)
33
+ ], QuoteRequestRevision.prototype, "quoteRequestId", void 0);
34
+ __decorate([
35
+ Property({ type: 'integer' }),
36
+ __metadata("design:type", Number)
37
+ ], QuoteRequestRevision.prototype, "revisionNumber", void 0);
38
+ __decorate([
39
+ Property({ type: 'uuid', nullable: true }),
40
+ __metadata("design:type", Object)
41
+ ], QuoteRequestRevision.prototype, "createdByAdminUserId", void 0);
42
+ __decorate([
43
+ Property({ type: 'uuid', nullable: true }),
44
+ __metadata("design:type", Object)
45
+ ], QuoteRequestRevision.prototype, "createdByCustomerAccountId", void 0);
46
+ __decorate([
47
+ Property({ type: 'text', nullable: true }),
48
+ __metadata("design:type", Object)
49
+ ], QuoteRequestRevision.prototype, "headerNoteSnapshot", void 0);
50
+ __decorate([
51
+ Property({ type: 'json' }),
52
+ __metadata("design:type", Array)
53
+ ], QuoteRequestRevision.prototype, "itemsSnapshot", void 0);
54
+ __decorate([
55
+ Property({ type: 'uuid', nullable: true }),
56
+ __metadata("design:type", Object)
57
+ ], QuoteRequestRevision.prototype, "previousRevisionId", void 0);
58
+ __decorate([
59
+ Property({ type: 'datetime', onCreate: () => new Date() }),
60
+ __metadata("design:type", Date)
61
+ ], QuoteRequestRevision.prototype, "createdAt", void 0);
62
+ QuoteRequestRevision = __decorate([
63
+ GlobalEntity(),
64
+ Entity({ tableName: 'quote_request_revisions' })
65
+ ], QuoteRequestRevision);
66
+ export { QuoteRequestRevision };
67
+ //# sourceMappingURL=quote-request-revision.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request-revision.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/quote-request-revision.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;AA0B7B,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAC/B,CAAC,aAAa,CAAC,CAMU;IAGzB,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,cAAc,CAAU;IAGxB,cAAc,CAAU;IAGxB,oBAAoB,CAAiB;IAGrC,0BAA0B,CAAiB;IAG3C,kBAAkB,CAAiB;IAGnC,aAAa,CAA8B;IAG3C,kBAAkB,CAAiB;IAGnC,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AA1BC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE;;4DACgB;AAGxB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;4DACN;AAGxB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kEACN;AAGrC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wEACA;AAG3C;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gEACR;AAGnC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;2DACgB;AAG3C;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gEACR;AAGnC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;uDAAc;AAnClB,oBAAoB;IAFhC,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,yBAAyB,EAAE,CAAC;GACpC,oBAAoB,CAoChC"}
@@ -0,0 +1,90 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * QuoteRequest — feature 008 workflow rewrite.
4
+ *
5
+ * Statuses (replacing the foundation 001 set):
6
+ * Created from admin → Approved | Canceled | Expired
7
+ * Pending → Approved | Canceled | Expired
8
+ * Approved → Completed
9
+ * Canceled, Completed, Expired are terminal.
10
+ *
11
+ * `version` is bumped on every row mutation; PATCH-shaped endpoints
12
+ * enforce optimistic concurrency via the `If-Match` header (foundation
13
+ * 001 convention). Customer accept/reject revision additionally pin
14
+ * `expectedRevisionNumber` against `current_revision_number`
15
+ * (research §R3).
16
+ */
17
+ export type QuoteRequestStatus = 'Created from admin' | 'Pending' | 'Canceled' | 'Approved' | 'Completed' | 'Expired';
18
+ export declare class QuoteRequest {
19
+ [OptionalProps]?: 'customFieldValues' | 'id' | 'businessId' | 'createdAt' | 'updatedAt' | 'status' | 'headerNote' | 'createdByAdminUserId' | 'cancellationReason' | 'awaitingCustomerRevisionAcceptance' | 'lastCustomerSeenRevisionNumber' | 'currentRevisionNumber' | 'assignedAdminUserId' | 'submittedAt' | 'approvedAt' | 'canceledAt' | 'completedAt' | 'expiredAt' | 'expiresAt' | 'convertedOrderId' | 'salesChannelId' | 'version';
20
+ id: string;
21
+ /**
22
+ * Customer-facing business Quote Request ID, distinct from `id`. Real RFQs
23
+ * are stamped by RfqService / RfqAdminService via the business-ID sequence +
24
+ * prefix/suffix settings. The placeholder default keeps direct
25
+ * `em.create(QuoteRequest, …)` fixtures unique without forcing every caller
26
+ * to pass it (mirrors `Order.businessId`).
27
+ */
28
+ businessId: string;
29
+ organizationId: string;
30
+ customerAccountId: string;
31
+ createdByAdminUserId?: string | null;
32
+ status: QuoteRequestStatus;
33
+ headerNote?: string | null;
34
+ cancellationReason?: string | null;
35
+ awaitingCustomerRevisionAcceptance: boolean;
36
+ lastCustomerSeenRevisionNumber: number;
37
+ currentRevisionNumber: number;
38
+ assignedAdminUserId?: string | null;
39
+ submittedAt?: Date | null;
40
+ approvedAt?: Date | null;
41
+ canceledAt?: Date | null;
42
+ completedAt?: Date | null;
43
+ expiredAt?: Date | null;
44
+ expiresAt?: Date | null;
45
+ convertedOrderId?: string | null;
46
+ /**
47
+ * The sales channel this request was raised on — feature 005 / FR-012,
48
+ * recorded since issue #266.
49
+ *
50
+ * The column, its index and its `on delete restrict` foreign key have existed
51
+ * since `20260430T170044_core_sales_channels_promote`; the property that was
52
+ * supposed to follow never landed, so nothing on the request path wrote it
53
+ * and the FR-006 channel-delete guard has never seen an RFQ attribution.
54
+ * Written from the **resolved request channel** (`currentSalesChannel()`),
55
+ * never from a second lookup or a default of this module's choosing
56
+ * (Principle XII).
57
+ *
58
+ * Held as a plain uuid FK target, exactly as `Order.salesChannelId` is,
59
+ * rather than as an ORM relation: the schema's own foreign key is what keeps
60
+ * the value honest.
61
+ *
62
+ * **Nullable, and it stays nullable.** Two shapes answer `null`: every row
63
+ * raised before this property existed — the owner ruled those are developer
64
+ * data not worth backfilling, and projecting them onto the system default
65
+ * would invent an attribution the guard would then refuse on — and a request
66
+ * created with no channel on the scope at all (a CLI, a worker, a fixture
67
+ * that calls the service directly). "No channel" is spelled `null`, never an
68
+ * empty string, a nil uuid or a fresh one (D-47…D-51).
69
+ *
70
+ * **Why the column was born nullable, and what a `NOT NULL` flip would cost**
71
+ * (D-167, from the deleted `scripts/backfill-quote-channel.ts`). Feature 005
72
+ * added it nullable on purpose, so the deploy that shipped it did not depend
73
+ * on the boot-time `DefaultChannelReconciler` having already run — the
74
+ * standard phased shape: add nullable, start writing, backfill, then flip.
75
+ * The flip deliberately never followed and cannot, because the middle step
76
+ * has no honest input: nothing wrote the column between the migration and
77
+ * issue #266, so every request raised in that window carries `null` and no
78
+ * record anywhere says which channel it came from. A deployment that
79
+ * genuinely needs the column non-nullable therefore deletes the null tail or
80
+ * takes a per-row answer from a source that knows one; it does not project
81
+ * the system default over the gap, which would hand the FR-006 channel-delete
82
+ * guard evidence the platform invented.
83
+ */
84
+ salesChannelId?: string | null;
85
+ version: number;
86
+ createdAt: Date;
87
+ updatedAt: Date;
88
+ customFieldValues: Record<string, unknown>;
89
+ }
90
+ //# sourceMappingURL=quote-request.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/quote-request.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAgC,MAAM,iBAAiB,CAAC;AAI7F;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,GACX,SAAS,CAAC;AAEd,qBAEa,YAAY;IACvB,CAAC,aAAa,CAAC,CAAC,EACZ,mBAAmB,GACnB,IAAI,GACJ,YAAY,GACZ,WAAW,GACX,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,sBAAsB,GACtB,oBAAoB,GACpB,oCAAoC,GACpC,gCAAgC,GAChC,uBAAuB,GACvB,qBAAqB,GACrB,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,WAAW,GACX,WAAW,GACX,kBAAkB,GAClB,gBAAgB,GAChB,SAAS,CAAC;IAGd,EAAE,EAAE,MAAM,CAAgB;IAE1B;;;;;;OAMG;IAGH,UAAU,EAAE,MAAM,CAAwB;IAI1C,cAAc,EAAG,MAAM,CAAC;IAIxB,iBAAiB,EAAG,MAAM,CAAC;IAG3B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAIrC,MAAM,EAAE,kBAAkB,CAAa;IAGvC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG3B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGnC,kCAAkC,EAAE,OAAO,CAAS;IAGpD,8BAA8B,EAAE,MAAM,CAAK;IAG3C,qBAAqB,EAAE,MAAM,CAAK;IAGlC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGpC,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAG1B,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGzB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGzB,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAG1B,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGxB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IAEH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG/B,OAAO,EAAE,MAAM,CAAK;IAGpB,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;IAI7B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;CACjD"}
@@ -0,0 +1,191 @@
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, Unique } from '@mikro-orm/core';
11
+ import { OrgScoped } from '@endora-commerce/platform/tenancy';
12
+ import { randomUUID } from 'crypto';
13
+ let QuoteRequest = class QuoteRequest {
14
+ [OptionalProps];
15
+ id = randomUUID();
16
+ /**
17
+ * Customer-facing business Quote Request ID, distinct from `id`. Real RFQs
18
+ * are stamped by RfqService / RfqAdminService via the business-ID sequence +
19
+ * prefix/suffix settings. The placeholder default keeps direct
20
+ * `em.create(QuoteRequest, …)` fixtures unique without forcing every caller
21
+ * to pass it (mirrors `Order.businessId`).
22
+ */
23
+ businessId = `QR-${randomUUID()}`;
24
+ organizationId;
25
+ customerAccountId;
26
+ createdByAdminUserId;
27
+ status = 'Pending';
28
+ headerNote;
29
+ cancellationReason;
30
+ awaitingCustomerRevisionAcceptance = false;
31
+ lastCustomerSeenRevisionNumber = 0;
32
+ currentRevisionNumber = 0;
33
+ assignedAdminUserId;
34
+ submittedAt;
35
+ approvedAt;
36
+ canceledAt;
37
+ completedAt;
38
+ expiredAt;
39
+ expiresAt;
40
+ convertedOrderId;
41
+ /**
42
+ * The sales channel this request was raised on — feature 005 / FR-012,
43
+ * recorded since issue #266.
44
+ *
45
+ * The column, its index and its `on delete restrict` foreign key have existed
46
+ * since `20260430T170044_core_sales_channels_promote`; the property that was
47
+ * supposed to follow never landed, so nothing on the request path wrote it
48
+ * and the FR-006 channel-delete guard has never seen an RFQ attribution.
49
+ * Written from the **resolved request channel** (`currentSalesChannel()`),
50
+ * never from a second lookup or a default of this module's choosing
51
+ * (Principle XII).
52
+ *
53
+ * Held as a plain uuid FK target, exactly as `Order.salesChannelId` is,
54
+ * rather than as an ORM relation: the schema's own foreign key is what keeps
55
+ * the value honest.
56
+ *
57
+ * **Nullable, and it stays nullable.** Two shapes answer `null`: every row
58
+ * raised before this property existed — the owner ruled those are developer
59
+ * data not worth backfilling, and projecting them onto the system default
60
+ * would invent an attribution the guard would then refuse on — and a request
61
+ * created with no channel on the scope at all (a CLI, a worker, a fixture
62
+ * that calls the service directly). "No channel" is spelled `null`, never an
63
+ * empty string, a nil uuid or a fresh one (D-47…D-51).
64
+ *
65
+ * **Why the column was born nullable, and what a `NOT NULL` flip would cost**
66
+ * (D-167, from the deleted `scripts/backfill-quote-channel.ts`). Feature 005
67
+ * added it nullable on purpose, so the deploy that shipped it did not depend
68
+ * on the boot-time `DefaultChannelReconciler` having already run — the
69
+ * standard phased shape: add nullable, start writing, backfill, then flip.
70
+ * The flip deliberately never followed and cannot, because the middle step
71
+ * has no honest input: nothing wrote the column between the migration and
72
+ * issue #266, so every request raised in that window carries `null` and no
73
+ * record anywhere says which channel it came from. A deployment that
74
+ * genuinely needs the column non-nullable therefore deletes the null tail or
75
+ * takes a per-row answer from a source that knows one; it does not project
76
+ * the system default over the gap, which would hand the FR-006 channel-delete
77
+ * guard evidence the platform invented.
78
+ */
79
+ salesChannelId;
80
+ version = 0;
81
+ createdAt = new Date();
82
+ updatedAt = new Date();
83
+ // Feature 055 — Custom Fields Layer value bag (inherits host tenant scope).
84
+ customFieldValues = {};
85
+ };
86
+ __decorate([
87
+ PrimaryKey({ type: 'uuid' }),
88
+ __metadata("design:type", String)
89
+ ], QuoteRequest.prototype, "id", void 0);
90
+ __decorate([
91
+ Property({ type: 'string', length: 128 }),
92
+ Unique(),
93
+ __metadata("design:type", String)
94
+ ], QuoteRequest.prototype, "businessId", void 0);
95
+ __decorate([
96
+ Property({ type: 'uuid' }),
97
+ Index(),
98
+ __metadata("design:type", String)
99
+ ], QuoteRequest.prototype, "organizationId", void 0);
100
+ __decorate([
101
+ Property({ type: 'uuid' }),
102
+ Index(),
103
+ __metadata("design:type", String)
104
+ ], QuoteRequest.prototype, "customerAccountId", void 0);
105
+ __decorate([
106
+ Property({ type: 'uuid', nullable: true }),
107
+ __metadata("design:type", Object)
108
+ ], QuoteRequest.prototype, "createdByAdminUserId", void 0);
109
+ __decorate([
110
+ Property({ type: 'string', length: 32 }),
111
+ Index(),
112
+ __metadata("design:type", String)
113
+ ], QuoteRequest.prototype, "status", void 0);
114
+ __decorate([
115
+ Property({ type: 'text', nullable: true, fieldName: 'header_note' }),
116
+ __metadata("design:type", Object)
117
+ ], QuoteRequest.prototype, "headerNote", void 0);
118
+ __decorate([
119
+ Property({ type: 'text', nullable: true }),
120
+ __metadata("design:type", Object)
121
+ ], QuoteRequest.prototype, "cancellationReason", void 0);
122
+ __decorate([
123
+ Property({ type: 'boolean' }),
124
+ __metadata("design:type", Boolean)
125
+ ], QuoteRequest.prototype, "awaitingCustomerRevisionAcceptance", void 0);
126
+ __decorate([
127
+ Property({ type: 'integer' }),
128
+ __metadata("design:type", Number)
129
+ ], QuoteRequest.prototype, "lastCustomerSeenRevisionNumber", void 0);
130
+ __decorate([
131
+ Property({ type: 'integer' }),
132
+ __metadata("design:type", Number)
133
+ ], QuoteRequest.prototype, "currentRevisionNumber", void 0);
134
+ __decorate([
135
+ Property({ type: 'uuid', nullable: true }),
136
+ __metadata("design:type", Object)
137
+ ], QuoteRequest.prototype, "assignedAdminUserId", void 0);
138
+ __decorate([
139
+ Property({ type: 'datetime', nullable: true }),
140
+ __metadata("design:type", Object)
141
+ ], QuoteRequest.prototype, "submittedAt", void 0);
142
+ __decorate([
143
+ Property({ type: 'datetime', nullable: true }),
144
+ __metadata("design:type", Object)
145
+ ], QuoteRequest.prototype, "approvedAt", void 0);
146
+ __decorate([
147
+ Property({ type: 'datetime', nullable: true }),
148
+ __metadata("design:type", Object)
149
+ ], QuoteRequest.prototype, "canceledAt", void 0);
150
+ __decorate([
151
+ Property({ type: 'datetime', nullable: true }),
152
+ __metadata("design:type", Object)
153
+ ], QuoteRequest.prototype, "completedAt", void 0);
154
+ __decorate([
155
+ Property({ type: 'datetime', nullable: true }),
156
+ __metadata("design:type", Object)
157
+ ], QuoteRequest.prototype, "expiredAt", void 0);
158
+ __decorate([
159
+ Property({ type: 'datetime', nullable: true }),
160
+ __metadata("design:type", Object)
161
+ ], QuoteRequest.prototype, "expiresAt", void 0);
162
+ __decorate([
163
+ Property({ type: 'uuid', nullable: true }),
164
+ __metadata("design:type", Object)
165
+ ], QuoteRequest.prototype, "convertedOrderId", void 0);
166
+ __decorate([
167
+ Property({ type: 'uuid', nullable: true }),
168
+ __metadata("design:type", Object)
169
+ ], QuoteRequest.prototype, "salesChannelId", void 0);
170
+ __decorate([
171
+ Property({ type: 'integer' }),
172
+ __metadata("design:type", Number)
173
+ ], QuoteRequest.prototype, "version", void 0);
174
+ __decorate([
175
+ Property({ type: 'datetime', onCreate: () => new Date() }),
176
+ __metadata("design:type", Date)
177
+ ], QuoteRequest.prototype, "createdAt", void 0);
178
+ __decorate([
179
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
180
+ __metadata("design:type", Date)
181
+ ], QuoteRequest.prototype, "updatedAt", void 0);
182
+ __decorate([
183
+ Property({ type: 'json' }),
184
+ __metadata("design:type", Object)
185
+ ], QuoteRequest.prototype, "customFieldValues", void 0);
186
+ QuoteRequest = __decorate([
187
+ OrgScoped(),
188
+ Entity({ tableName: 'quote_requests' })
189
+ ], QuoteRequest);
190
+ export { QuoteRequest };
191
+ //# sourceMappingURL=quote-request.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/quote-request.entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
3
+ import type { EventBus } from '@endora-commerce/platform/events';
4
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
5
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
6
+ import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
7
+ import { quoteRequestsModule, type QuoteRequestsModuleOptions } from './plugin.js';
8
+ import { QuoteRequest } from './entities/quote-request.entity.js';
9
+ import { QuoteRequestEvent } from './entities/quote-request-event.entity.js';
10
+ import { QuoteRequestItem } from './entities/quote-request-item.entity.js';
11
+ import { QuoteRequestNotificationEvent } from './entities/quote-request-notification-event.entity.js';
12
+ import { QuoteRequestRevision } from './entities/quote-request-revision.entity.js';
13
+ /**
14
+ * `quote_requests` — four settings resolvers that belonged to the module
15
+ * (feature 072, wave 3, T132).
16
+ *
17
+ * `resolveExpiryDays`, `resolveBoolSetting`, `resolveBusinessIdPrefix` and
18
+ * `resolveBusinessIdSuffix` each read a `quote_requests.*` setting and fall back
19
+ * to this module's own default. All four were written out in a composition root
20
+ * — twice, once per root, with the same try/catch — so that the module "isn't
21
+ * coupled to the settings read API". It resolves a settings port already; the
22
+ * indirection put four of the module's own knobs somewhere they could drift,
23
+ * and they did: the harness passed neither business-ID resolver, so RFQ numbers
24
+ * in tests never carried the prefix or suffix production stamps on them.
25
+ *
26
+ * The sales-rep scope has the same story with a sharper edge. Absent, the RFQ
27
+ * admin scope silently stops being subtree-aware, so a sales representative
28
+ * holding `organizations:rollup` sees only their own rows. The harness passed
29
+ * nothing, so feature 056's rollup was exercised by no test at all. It became a
30
+ * `salesRepSubtree` contribution out of which this module assembled its own
31
+ * `SalesRepAssignmentService`; issue #108 found the third such assembly, in
32
+ * `customers`, missing the optional argument that carries the roll-up. So the
33
+ * assembly moved to its owner and this module resolves
34
+ * `organizationSalesRepScopePort` — one implementation, nothing to forget.
35
+ *
36
+ * The two actor resolvers stay root-shaped for the reason they always were:
37
+ * production reads `request.actor` and throws a 401 envelope, the harness reads
38
+ * `request.testActor`. That is a composition's answer to "who is asking".
39
+ */
40
+ /** What `quote_requests` resolves from the container, and the names it owns. */
41
+ export interface QuoteRequestsCradle {
42
+ readonly emFactory: () => EntityManager;
43
+ readonly eventBus: EventBus;
44
+ readonly auditLogService: AuditPort;
45
+ readonly requireAdmin: RequireAdminFactory;
46
+ readonly requireCustomer: QuoteRequestsModuleOptions['requireCustomer'];
47
+ readonly settingsReadPort: SettingsReadPort;
48
+ /**
49
+ * The channel a global-scope settings read resolves against: the deployment's
50
+ * system-default sales channel, or `null` when it has none. A root input
51
+ * (`check-port-dependencies.ts`), resolved by `stripe`, `autopay`,
52
+ * `inventory` and others under this same name.
53
+ */
54
+ readonly settingsChannelResolver: () => Promise<string | null>;
55
+ readonly customFieldValueService: NonNullable<QuoteRequestsModuleOptions['customFieldValues']>;
56
+ /**
57
+ * Defaulted by this module since `specs/117-instance-bring-up/` Phase 6 — see
58
+ * `registerActorResolvers` below. A root that answers differently still
59
+ * contributes over it; the test harness does, reading its own
60
+ * `request.testActor` and widening `isPlatformAdmin` on purpose.
61
+ */
62
+ readonly rfqCustomerContextResolver: QuoteRequestsModuleOptions['resolveCustomerContext'];
63
+ readonly rfqAdminContextResolver: QuoteRequestsModuleOptions['resolveAdminContext'];
64
+ /**
65
+ * The organization's effective tax rate for a quoted line, defaulted by this
66
+ * module since `specs/117-instance-bring-up/` Phase 6 — see
67
+ * `registerTaxRateResolver` below.
68
+ */
69
+ readonly rfqTaxRateResolver: NonNullable<QuoteRequestsModuleOptions['resolveTaxRate']>;
70
+ /**
71
+ * The sales-rep assignment scope, owned by `organizations` (issue #108).
72
+ *
73
+ * T138 split it into the two ports it is *built* from — the org tree and the
74
+ * `organizations:rollup` capability check — and this module assembled a
75
+ * `SalesRepAssignmentService` out of them. That put the assembly in three
76
+ * places, and the one place that got it wrong (`customers`, which omitted the
77
+ * optional subtree argument entirely) compiled. The assembled port is one
78
+ * name, and nobody can assemble it differently.
79
+ */
80
+ readonly organizationSalesRepScopePort: QuoteRequestsModuleOptions['salesRepAssignment'];
81
+ readonly quoteRequests: ReturnType<typeof quoteRequestsModule>;
82
+ readonly rfqService: ReturnType<typeof quoteRequestsModule>['handle'] extends () => infer H ? H extends {
83
+ rfqService: infer S;
84
+ } ? S : never : never;
85
+ }
86
+ export declare function registerModule(ctx: ModuleContext): void;
87
+ /**
88
+ * The module's persisted entity classes, on the `./backend` subpath, as one
89
+ * array and **no named class export** (D-168).
90
+ *
91
+ * This is the shape the platform already reads, in two places: the boot-time
92
+ * loader (`src/packages/package-runtime.ts`, `exported['entities']`) and the
93
+ * static declaration reader (`scripts/lib/package-declarations.ts`), which is
94
+ * the third source of `check:module-boundary`'s `table→owner` map and the
95
+ * package pass of `check-entity-tenant-classification`. `blog` published
96
+ * `export *` lines instead until D-168, which satisfied only the committed host
97
+ * registry — the one path that stops being taken the day the module is
98
+ * *installed* rather than linked, at which point the loader read `undefined`,
99
+ * returned `[]`, and registered zero entities without a word.
100
+ *
101
+ * The absence of a named export is the load-bearing half. With one,
102
+ * `import type { QuoteRequest } from '@endora-commerce/mod-quote-requests/backend'`
103
+ * compiles in any consumer — ours, a deployment's, a stranger's — and only a
104
+ * check whose population is *this* repository could ever object. Without it,
105
+ * that import is TS2459 in the consumer's own tree (the barrel imports the
106
+ * classes to build the array, so TypeScript gives the more precise "declares it
107
+ * locally, but it is not exported"), which is Principle I holding by
108
+ * construction. Nothing legitimate is lost: D-32 already forbids an ORM
109
+ * relation from another module into these classes, and every other cross-module
110
+ * read goes through a contract type — `QuoteRequestReadPort` and
111
+ * `RfqCustomerPort`, both published above.
112
+ *
113
+ * Identity matters more here than anywhere else in the package: MikroORM keys
114
+ * its metadata on the class, so a consumer that reached these files by a second
115
+ * specifier would register a second `QuoteRequest` and lose one of them at
116
+ * discovery (D-160.6, measured). One array behind one declared subpath is the
117
+ * only way in.
118
+ */
119
+ export declare const entities: (typeof QuoteRequest | typeof QuoteRequestItem | typeof QuoteRequestEvent | typeof QuoteRequestRevision | typeof QuoteRequestNotificationEvent)[];
120
+ //# 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;AAG3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAkBjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAQtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,eAAe,EAAE,0BAA0B,CAAC,iBAAiB,CAAC,CAAC;IACxE,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,uBAAuB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/D,QAAQ,CAAC,uBAAuB,EAAE,WAAW,CAC3C,0BAA0B,CAAC,mBAAmB,CAAC,CAChD,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,0BAA0B,EAAE,0BAA0B,CAAC,wBAAwB,CAAC,CAAC;IAC1F,QAAQ,CAAC,uBAAuB,EAAE,0BAA0B,CAAC,qBAAqB,CAAC,CAAC;IACpF;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAC,0BAA0B,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACvF;;;;;;;;;OASG;IACH,QAAQ,CAAC,6BAA6B,EAAE,0BAA0B,CAAC,oBAAoB,CAAC,CAAC;IACzF,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;IAC/D,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,QAAQ,CAAC,SAAS,MAAM,MAAM,CAAC,GACvF,CAAC,SAAS;QAAE,UAAU,EAAE,MAAM,CAAC,CAAA;KAAE,GAC/B,CAAC,GACD,KAAK,GACP,KAAK,CAAC;CACX;AAqKD,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA2MvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,QAAQ,mJAMpB,CAAC"}