@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,361 @@
1
+ import { z } from 'zod';
2
+ import { ERROR_CODES } from '@endora-commerce/contracts';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { QuoteRequestReadService } from './services/quote-request-read-port.js';
5
+ import { registerQuoteRequestSalesChannelAttributions } from './services/sales-channel-attributions.js';
6
+ import { lazyPort, SettingNotRegistered, SettingOutOfScopeForChannel, } from '@endora-commerce/platform/kernel';
7
+ import { QUOTE_REQUESTS_SETTING_CODES } from '../manifest.js';
8
+ import { quoteRequestsModule } from './plugin.js';
9
+ import { QuoteRequest } from './entities/quote-request.entity.js';
10
+ import { QuoteRequestEvent } from './entities/quote-request-event.entity.js';
11
+ import { QuoteRequestItem } from './entities/quote-request-item.entity.js';
12
+ import { QuoteRequestNotificationEvent } from './entities/quote-request-notification-event.entity.js';
13
+ import { QuoteRequestRevision } from './entities/quote-request-revision.entity.js';
14
+ /**
15
+ * `setting_values.sales_channel_id` is a `uuid` column, so a channel id that is
16
+ * not one cannot match a row — PostgreSQL rejects the comparison outright. The
17
+ * check is here rather than in the settings service because this is the caller
18
+ * that has somewhere to degrade to.
19
+ */
20
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
21
+ /**
22
+ * Conditions this module has already reported. Module scope and never reset, so
23
+ * the guard is deliberately **per process** rather than per read: an unresolved
24
+ * channel or a mis-scoped setting is a deployment fact that holds for every
25
+ * subsequent read, and RFQ settings are read on every quote creation and every
26
+ * storefront page load. One line per condition is what makes it findable; one
27
+ * line per read is what makes it invisible. Same shape as
28
+ * `transactional_emails`' `noTransportWarned`.
29
+ */
30
+ const warnedConditions = new Set();
31
+ function warnOnce(condition, message) {
32
+ if (warnedConditions.has(condition))
33
+ return;
34
+ warnedConditions.add(condition);
35
+ console.warn(message);
36
+ }
37
+ /**
38
+ * The two actor-shaped names this module reads, defaulted here
39
+ * (`specs/117-instance-bring-up/` Phase 6, FR-033).
40
+ *
41
+ * They were contributed by `backend/src/composition.ts` and by
42
+ * `backend/test/helpers/test-server.ts` and by nothing else, so a composition
43
+ * that is neither of those two — which is every client instance, `composeApp`
44
+ * being the whole of what one calls — could not resolve them. `composeApp`'s
45
+ * own options say what that state is: `contribute` is *"the one slot where a
46
+ * value a module defaults may be overwritten"*, and a name **nothing** defaults
47
+ * supplied through it is a required value wearing an override's clothes.
48
+ *
49
+ * Both bodies are this module's own and carry no deployment judgement. They
50
+ * read `request.actor`, which is the platform's decoration since T118b, and
51
+ * three ports this manifest already declares as binding `dependencies`:
52
+ * `customer_accounts`, `admin_users` and `admin_roles`. A root that wants a
53
+ * different answer still contributes one — the test harness does, and its
54
+ * divergence is deliberate — which is what the window is for.
55
+ */
56
+ function registerActorResolvers(ctx) {
57
+ ctx.di.register({
58
+ /**
59
+ * Who is asking, in this module's shape: the account, its Organization and
60
+ * whether the caller administers it.
61
+ *
62
+ * The Organization guard asserts an invariant rather than describing a
63
+ * business state (D-178) — `customer_accounts.organization_id` is `NOT NULL`
64
+ * and an individual is backed by a personal organisation — so a caller
65
+ * reaching it is a broken invariant and not a buyer who should be told to
66
+ * attach something they cannot attach. It is kept rather than deleted
67
+ * because `Actor`'s `organizationId` is `string | null` at this seam and
68
+ * `CustomerContext`'s is `string`.
69
+ */
70
+ rfqCustomerContextResolver: ctx
71
+ .asFunction(() => {
72
+ const accounts = lazyPort(ctx, 'customerAccountReadPort');
73
+ return async (request) => {
74
+ const actor = request.actor;
75
+ if (actor?.kind !== 'customer') {
76
+ throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Customer session required.');
77
+ }
78
+ if (!actor.organizationId) {
79
+ throw new HttpError(500, ERROR_CODES.INTERNAL, 'Invariant violated: a customer account has no Organization (Principle XI).', { code: 'customer_account_organization_missing' });
80
+ }
81
+ const account = await accounts.findById(actor.customerAccountId);
82
+ return {
83
+ customerAccountId: actor.customerAccountId,
84
+ organizationId: actor.organizationId,
85
+ isOrgAdmin: account?.role === 'organization_admin',
86
+ };
87
+ };
88
+ })
89
+ .singleton(),
90
+ /**
91
+ * Who is administering, with the role the admin surface renders beside the
92
+ * RFQ and scopes its list by.
93
+ *
94
+ * `getById` rather than a nullable lookup, and it cannot 404 here:
95
+ * `admin_users_admin_role_fk` is `on delete restrict`, so a non-null
96
+ * `adminRoleId` names a row that exists.
97
+ */
98
+ rfqAdminContextResolver: ctx
99
+ .asFunction(() => {
100
+ const admins = lazyPort(ctx, 'adminUserReadPort');
101
+ const roles = lazyPort(ctx, 'adminRolePort');
102
+ return async (request) => {
103
+ const actor = request.actor;
104
+ if (actor?.kind !== 'admin') {
105
+ throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Admin session required.');
106
+ }
107
+ const adminUser = await admins.findById(actor.adminUserId);
108
+ const role = adminUser?.adminRoleId ? await roles.getById(adminUser.adminRoleId) : null;
109
+ return {
110
+ adminUserId: actor.adminUserId,
111
+ isPlatformAdmin: role?.code === 'platform_admin',
112
+ roleLabel: role?.code === 'platform_admin'
113
+ ? 'Platform administrator'
114
+ : role?.code === 'sales_representative'
115
+ ? 'Sales representative'
116
+ : (role?.name ?? 'Administrator'),
117
+ };
118
+ };
119
+ })
120
+ .singleton(),
121
+ });
122
+ }
123
+ /**
124
+ * The rate a quoted line is priced at (`specs/117-instance-bring-up/` Phase 6,
125
+ * FR-033).
126
+ *
127
+ * Two ports, both already binding `dependencies` of this manifest: the
128
+ * organisation's VAT status and country from `organizations`, and the rate
129
+ * itself from `taxes`.
130
+ *
131
+ * **No `catch`, and that is the design** (issue #84). `taxRateFor` answers
132
+ * "nothing applies" as a *value* — `{ source: 'none' }`, with no rate to read —
133
+ * so the only errors that reach this seam are a failing database and a
134
+ * switched-off owner, and returning 0 for either would quote a zero-VAT price
135
+ * on an operator's behalf and call it an answer. Both owners are
136
+ * `nonDeactivatable`, so neither has an off state here; the refusal is what
137
+ * reaches a caller if that ever changes.
138
+ */
139
+ function registerTaxRateResolver(ctx) {
140
+ ctx.di.register({
141
+ rfqTaxRateResolver: ctx
142
+ .asFunction(() => {
143
+ const taxProfiles = lazyPort(ctx, 'organizationTaxProfilePort');
144
+ const taxes = lazyPort(ctx, 'taxService');
145
+ return async (organizationId) => {
146
+ const org = await taxProfiles.taxProfileOf(organizationId);
147
+ const vatStatus = org?.vatStatus ?? 'vat_payer';
148
+ if (vatStatus !== 'vat_payer')
149
+ return 0;
150
+ const resolved = await taxes.taxRateFor({
151
+ country: org?.country ?? 'PL',
152
+ productType: 'simple',
153
+ vatStatus,
154
+ });
155
+ // `none` is the operator's own configuration state — `taxes` is
156
+ // present and holds no rule that applies and no default — so a quote
157
+ // is priced net and the quote view drops its VAT row rather than
158
+ // printing a 0% one.
159
+ return resolved.source === 'none' ? 0 : resolved.rate;
160
+ };
161
+ })
162
+ .singleton(),
163
+ });
164
+ }
165
+ export function registerModule(ctx) {
166
+ registerActorResolvers(ctx);
167
+ registerTaxRateResolver(ctx);
168
+ ctx.di.register({
169
+ quoteRequests: ctx
170
+ .asFunction(({ emFactory, eventBus, auditLogService }) => {
171
+ /**
172
+ * This module's own settings, read through the port it already holds,
173
+ * against the **resolved** sales channel.
174
+ *
175
+ * It used to pass the literal `'default'` as the `salesChannelId`, a
176
+ * spelling that came down from the composition root T132 lifted this
177
+ * out of. `'default'` is a channel *code*; `setting_values.sales_channel_id`
178
+ * is `uuid`, so PostgreSQL rejected every one of these reads
179
+ * (`invalid input syntax for type uuid: "default"`), the bare `catch`
180
+ * below swallowed it, and all four settings answered with their module
181
+ * fallback on every call — expiry never applied, RFQ numbers never
182
+ * carried the configured prefix or suffix, and an operator switching a
183
+ * storefront flag off changed nothing.
184
+ *
185
+ * What the handler may absorb is now enumerated, because a bare `catch`
186
+ * around a port call turns fail-closed into fail-open
187
+ * (`docs/docs/architecture/kernel.md`; the composition checklist's rule 7):
188
+ *
189
+ * - `SettingNotRegistered` — "no value configured yet", the normal
190
+ * state before the manifest reconciler has run. Quiet fallback.
191
+ * - `SettingOutOfScopeForChannel` — the operator scoped this setting to
192
+ * other channels, so no value applies here. The module default is the
193
+ * right answer, but the scoping is almost certainly unintended, so it
194
+ * is warned about.
195
+ * - everything else — a shape mismatch, a driver error, a
196
+ * `ModuleDisabledError` from a switched-off owner — propagates.
197
+ */
198
+ const setting = async (code, schema, fallback) => {
199
+ const cradle = ctx.cradle();
200
+ const channelId = await cradle.settingsChannelResolver();
201
+ if (channelId === null || !UUID_PATTERN.test(channelId)) {
202
+ // Both roots answer this resolver with the system-default channel's
203
+ // id, falling back to `ORGANIZATIONS_SETTINGS_CHANNEL_ID` — an env
204
+ // knob whose own default is the string `'default'`. So "no channel"
205
+ // arrives here in two shapes, `null` and a non-uuid placeholder, and
206
+ // neither can address a `setting_values` row. Degrading to the
207
+ // module default keeps RFQs working, but silently is exactly the
208
+ // failure above, so it is reported.
209
+ warnOnce('unresolved-channel', `[quote_requests] no sales channel resolved for settings (got ` +
210
+ `${channelId === null ? 'null' : `"${channelId}"`}) — every ` +
211
+ `quote_requests.* setting falls back to its module default ` +
212
+ `(logged once per process).`);
213
+ return fallback;
214
+ }
215
+ try {
216
+ return await cradle.settingsReadPort.get(code, channelId, schema);
217
+ }
218
+ catch (error) {
219
+ if (error instanceof SettingNotRegistered)
220
+ return fallback;
221
+ if (error instanceof SettingOutOfScopeForChannel) {
222
+ warnOnce(`out-of-scope:${code}`, `[quote_requests] setting "${code}" is not in scope for sales channel ` +
223
+ `"${channelId}" — falling back to the module default ` +
224
+ `(logged once per process).`);
225
+ return fallback;
226
+ }
227
+ throw error;
228
+ }
229
+ };
230
+ return quoteRequestsModule({
231
+ emFactory,
232
+ eventBus,
233
+ auditLog: auditLogService,
234
+ customFieldValues: lazyPort(ctx, 'customFieldValueService'),
235
+ requireAdmin: (permission) => async (req, reply) => ctx.cradle().requireAdmin(permission)(req, reply),
236
+ requireCustomer: (req, reply) => ctx.cradle().requireCustomer(req, reply),
237
+ resolveCustomerContext: (req) => ctx.cradle().rfqCustomerContextResolver(req),
238
+ resolveAdminContext: (req) => ctx.cradle().rfqAdminContextResolver(req),
239
+ resolveTaxRate: (organizationId) => ctx.cradle().rfqTaxRateResolver(organizationId),
240
+ // Issue #108 — `organizations`' scope, resolved. This used to be the
241
+ // two feature-056 subtree deps, out of which this module built its own
242
+ // `SalesRepAssignmentService`: one of three assemblies of one class,
243
+ // and the shape whose optional third argument `customers` omitted.
244
+ salesRepAssignment: lazyPort(ctx, 'organizationSalesRepScopePort'),
245
+ // Feature 075, Phase C — the five owners this module read out of
246
+ // directly, and the one it wrote into. Each was an `em.find` /
247
+ // `em.create` against another module's table, which no gate can see;
248
+ // all six are already binding `dependencies` of this manifest, and
249
+ // `carts.replaceItemsForCustomer` moves the quote-to-cart conversion
250
+ // inside the module that owns the tables (D-78 rule 1).
251
+ catalogProducts: lazyPort(ctx, 'catalogProductReadPort'),
252
+ // Issue #259 — the channel assortment gate on the three quote-line
253
+ // seams. A kernel registration, so there is no seventh module edge.
254
+ channelMembership: lazyPort(ctx, 'salesChannelMembershipPort'),
255
+ customerAccounts: lazyPort(ctx, 'customerAccountReadPort'),
256
+ organizations: lazyPort(ctx, 'organizationDetailsPort'),
257
+ adminUsers: lazyPort(ctx, 'adminUserReadPort'),
258
+ orders: lazyPort(ctx, 'orderReadPort'),
259
+ carts: lazyPort(ctx, 'cartWritePort'),
260
+ resolveExpiryDays: () => setting(QUOTE_REQUESTS_SETTING_CODES.EXPIRY_DAYS, z.number().int().nonnegative(), 0),
261
+ resolveBoolSetting: (key) => setting(key === 'show_add_to_quote_on_card'
262
+ ? QUOTE_REQUESTS_SETTING_CODES.SHOW_ADD_TO_QUOTE_ON_CARD
263
+ : QUOTE_REQUESTS_SETTING_CODES.SHOW_ADD_TO_QUOTE_ON_PDP, z.boolean(), true),
264
+ resolveBusinessIdPrefix: () => setting(QUOTE_REQUESTS_SETTING_CODES.BUSINESS_ID_PREFIX, z.string(), ''),
265
+ resolveBusinessIdSuffix: () => setting(QUOTE_REQUESTS_SETTING_CODES.BUSINESS_ID_SUFFIX, z.string(), ''),
266
+ });
267
+ })
268
+ .singleton(),
269
+ });
270
+ // The type parameter is feature 075 Phase P's compile-time proof that the
271
+ // service still satisfies `RfqCustomerPort` — the two methods `carts`,
272
+ // `orders`, `shopping_lists`, `quick_order` and `customers` measurably call.
273
+ // Both already answer with contract DTOs, so no adapter was needed.
274
+ ctx.di.providePort('quoteRequestReadPort', ctx
275
+ .asFunction(({ emFactory }) => new QuoteRequestReadService(emFactory))
276
+ .singleton());
277
+ ctx.di.providePort('rfqService', ctx
278
+ .asFunction(({ quoteRequests }) => quoteRequests.handle().rfqService)
279
+ .singleton());
280
+ /**
281
+ * US5 — an order created from a quote request completes it (issue #107).
282
+ *
283
+ * This was a bare `eventBus.on` in the plugin body: with `quote_requests`
284
+ * switched off it still flipped the RFQ to Completed, appended a `completed`
285
+ * event row and enqueued the customer notification — while every route that
286
+ * could show the customer that quote request refused. `ctx.subscribe` makes
287
+ * the module's effective state decide.
288
+ */
289
+ ctx.subscribe('order.created.v1', async (payload) => {
290
+ await ctx
291
+ .cradle()
292
+ .quoteRequests.handle()
293
+ .orderCompletionReactor.onOrderCreated(payload);
294
+ });
295
+ /**
296
+ * How many quote requests are attributed to a sales channel (feature 075,
297
+ * D-87).
298
+ *
299
+ * `sales_channels` used to count them itself, naming this module's table and
300
+ * this module's column in raw SQL. A read port would have been the wrong
301
+ * repair and this module is the reason: `sales_channels` is
302
+ * `nonDeactivatable`, and the lifecycle refuses to disable a module a
303
+ * non-deactivatable one depends on, so a `dependencies` entry pointing this
304
+ * way would have made this module permanently undeactivatable in order to
305
+ * count rows before a channel delete.
306
+ *
307
+ * A **contribution** hook: it pushes an inert counter into
308
+ * `salesChannelAttributionRegistry`, an ungated registry, and carries no
309
+ * presence probe (D-67/D-68). It must not carry one for a second reason here
310
+ * — the registry's policy is to honour an absent contributor, because the
311
+ * rows survive a deactivation and `quote_requests_sales_channel_fk` is
312
+ * `on delete restrict`, so a probe would turn a 422 naming this module into a
313
+ * raw constraint violation.
314
+ */
315
+ ctx.onBoot(() => {
316
+ registerQuoteRequestSalesChannelAttributions(lazyPort(ctx, 'salesChannelAttributionRegistry'), ctx.cradle().emFactory);
317
+ });
318
+ ctx.routes(async (app) => {
319
+ await ctx.cradle().quoteRequests.register(app);
320
+ });
321
+ }
322
+ /**
323
+ * The module's persisted entity classes, on the `./backend` subpath, as one
324
+ * array and **no named class export** (D-168).
325
+ *
326
+ * This is the shape the platform already reads, in two places: the boot-time
327
+ * loader (`src/packages/package-runtime.ts`, `exported['entities']`) and the
328
+ * static declaration reader (`scripts/lib/package-declarations.ts`), which is
329
+ * the third source of `check:module-boundary`'s `table→owner` map and the
330
+ * package pass of `check-entity-tenant-classification`. `blog` published
331
+ * `export *` lines instead until D-168, which satisfied only the committed host
332
+ * registry — the one path that stops being taken the day the module is
333
+ * *installed* rather than linked, at which point the loader read `undefined`,
334
+ * returned `[]`, and registered zero entities without a word.
335
+ *
336
+ * The absence of a named export is the load-bearing half. With one,
337
+ * `import type { QuoteRequest } from '@endora-commerce/mod-quote-requests/backend'`
338
+ * compiles in any consumer — ours, a deployment's, a stranger's — and only a
339
+ * check whose population is *this* repository could ever object. Without it,
340
+ * that import is TS2459 in the consumer's own tree (the barrel imports the
341
+ * classes to build the array, so TypeScript gives the more precise "declares it
342
+ * locally, but it is not exported"), which is Principle I holding by
343
+ * construction. Nothing legitimate is lost: D-32 already forbids an ORM
344
+ * relation from another module into these classes, and every other cross-module
345
+ * read goes through a contract type — `QuoteRequestReadPort` and
346
+ * `RfqCustomerPort`, both published above.
347
+ *
348
+ * Identity matters more here than anywhere else in the package: MikroORM keys
349
+ * its metadata on the class, so a consumer that reached these files by a second
350
+ * specifier would register a second `QuoteRequest` and lose one of them at
351
+ * discovery (D-160.6, measured). One array behind one declared subpath is the
352
+ * only way in.
353
+ */
354
+ export const entities = [
355
+ QuoteRequest,
356
+ QuoteRequestEvent,
357
+ QuoteRequestItem,
358
+ QuoteRequestNotificationEvent,
359
+ QuoteRequestRevision,
360
+ ];
361
+ //# 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,95 @@
1
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import type { EventBus } from '@endora-commerce/platform/events';
4
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
5
+ import type { AdminUserReadPort, CartWritePort, CatalogProductReadPort, CustomerAccountReadPort, CustomFieldValuePort, OrderReadPort, OrganizationDetailsPort, SalesRepAssignmentPort } from '@endora-commerce/contracts';
6
+ import { RfqService } from './services/rfq-service.js';
7
+ import { RfqAdminService } from './services/rfq-admin-service.js';
8
+ import { RfqExpiryWorker } from './services/rfq-expiry-worker.js';
9
+ import { createOrderCompletionReactor } from './services/order-completion-reactor.js';
10
+ import { type CustomerContextResolver, type RequireCustomerGuard } from './routes.customer.js';
11
+ import { type AdminContextResolver } from './routes.admin.js';
12
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
13
+ import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
14
+ export interface QuoteRequestsModuleOptions {
15
+ emFactory: () => EntityManager;
16
+ eventBus: EventBus;
17
+ requireCustomer: RequireCustomerGuard;
18
+ requireAdmin: RequireAdminFactory;
19
+ resolveCustomerContext: CustomerContextResolver;
20
+ resolveAdminContext: AdminContextResolver;
21
+ /** Reads the current `quote_requests.expiry_days` from the settings module. */
22
+ resolveExpiryDays: () => Promise<number>;
23
+ /** Reads the storefront-visibility flags from settings. */
24
+ resolveBoolSetting: (key: 'show_add_to_quote_on_card' | 'show_add_to_quote_on_pdp') => Promise<boolean>;
25
+ /**
26
+ * Reads the business Quote Request ID prefix/suffix from settings
27
+ * (`quote_requests.business_id.*`). Optional — when omitted, generated IDs
28
+ * are the bare sequence number.
29
+ */
30
+ resolveBusinessIdPrefix?: () => Promise<string>;
31
+ resolveBusinessIdSuffix?: () => Promise<string>;
32
+ /**
33
+ * Feature 026 — refuses RFQ submission when the Customer's Organization is
34
+ * not `active`.
35
+ */
36
+ assertOrganizationCanTransact?: (organizationId: string) => Promise<void>;
37
+ /**
38
+ * Resolves the VAT rate (fraction, e.g. `0.23`) applied to a quote's net
39
+ * prices for the given Organization. Supplied by composition (reads the
40
+ * Organization VAT status + tax rules).
41
+ *
42
+ * Required since issue #124: omitting it quoted every organization at 0% VAT
43
+ * and called that a price, which is indistinguishable from a deployment whose
44
+ * rules genuinely say 0%. An absent `taxes` module now reaches the caller as
45
+ * the 503 `MODULE_DISABLED` envelope through this resolver.
46
+ */
47
+ resolveTaxRate: (organizationId: string) => Promise<number>;
48
+ /** Feature 054 — audits RFQ lifecycle writes co-transactionally when provided. */
49
+ auditLog?: AuditPort;
50
+ /** Feature 055 — validates + reads RFQ custom-field values on the admin edit path. */
51
+ customFieldValues?: CustomFieldValuePort;
52
+ /**
53
+ * Feature 075, Phase C — the five owners this module reads, and the one it
54
+ * writes. Each replaces an `em.find` against another module's table, which
55
+ * deactivation cannot reach; each owner is a binding dependency of this
56
+ * manifest, so every one of them fails closed.
57
+ */
58
+ catalogProducts: CatalogProductReadPort;
59
+ /**
60
+ * The sanctioned bridge accessor (Constitution XII), for the assortment gate
61
+ * a quote line owes (issue #259). Kernel-registered, so it is not one of the
62
+ * six module edges above.
63
+ */
64
+ channelMembership: SalesChannelMembershipPort;
65
+ customerAccounts: CustomerAccountReadPort;
66
+ organizations: OrganizationDetailsPort;
67
+ adminUsers: AdminUserReadPort;
68
+ orders: OrderReadPort;
69
+ carts: CartWritePort;
70
+ /**
71
+ * `organizations`' sales-rep assignment port (issue #108).
72
+ *
73
+ * This used to be `salesRepSubtree`, the feature-056 deps out of which this
74
+ * module built its **own** `SalesRepAssignmentService`. One class, assembled
75
+ * in three places — its owner and two consumers — and the assembly here was
76
+ * complete; the one in `customers` omitted the optional argument that carries
77
+ * the roll-up, which is what a copy makes possible. The scope is
78
+ * `organizations`', so it is resolved, not rebuilt.
79
+ */
80
+ salesRepAssignment: SalesRepAssignmentPort;
81
+ }
82
+ export interface QuoteRequestsModuleHandle {
83
+ /** US5 reactor; `backend.ts` owns its subscription. */
84
+ orderCompletionReactor: ReturnType<typeof createOrderCompletionReactor>;
85
+ expiryWorker: RfqExpiryWorker;
86
+ rfqService: RfqService;
87
+ adminService: RfqAdminService;
88
+ salesRepAssignment: SalesRepAssignmentPort;
89
+ }
90
+ export declare function quoteRequestsModule(options: QuoteRequestsModuleOptions): {
91
+ register: (app: FastifyInstance) => Promise<void>;
92
+ handle: () => QuoteRequestsModuleHandle;
93
+ };
94
+ export type { CustomerContextResolver, AdminContextResolver, RequireCustomerGuard, FastifyRequest };
95
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAoB,MAAM,2BAA2B,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAIlE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAWnF,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,oBAAoB,CAAC;IACtC,YAAY,EAAE,mBAAmB,CAAC;IAClC,sBAAsB,EAAE,uBAAuB,CAAC;IAChD,mBAAmB,EAAE,oBAAoB,CAAC;IAC1C,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,2DAA2D;IAC3D,kBAAkB,EAAE,CAAC,GAAG,EAAE,2BAA2B,GAAG,0BAA0B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxG;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;OAGG;IACH,6BAA6B,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E;;;;;;;;;OASG;IACH,cAAc,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,kFAAkF;IAClF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;IACzC;;;;;OAKG;IACH,eAAe,EAAE,sBAAsB,CAAC;IACxC;;;;OAIG;IACH,iBAAiB,EAAE,0BAA0B,CAAC;IAC9C,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,aAAa,EAAE,uBAAuB,CAAC;IACvC,UAAU,EAAE,iBAAiB,CAAC;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,aAAa,CAAC;IACrB;;;;;;;;;OASG;IACH,kBAAkB,EAAE,sBAAsB,CAAC;CAC5C;AAED,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,sBAAsB,EAAE,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC;IACxE,YAAY,EAAE,eAAe,CAAC;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,eAAe,CAAC;IAC9B,kBAAkB,EAAE,sBAAsB,CAAC;CAC5C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG;IACxE,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,EAAE,MAAM,yBAAyB,CAAC;CACzC,CA6IA;AAED,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,152 @@
1
+ import { RfqService } from './services/rfq-service.js';
2
+ import { createQuoteRequestBusinessIdGenerator } from './services/quote-request-business-id-generator.js';
3
+ import { RfqAdminService } from './services/rfq-admin-service.js';
4
+ import { RfqEventService } from './services/rfq-event-service.js';
5
+ import { RfqRevisionService } from './services/rfq-revision-service.js';
6
+ import { RfqNotificationService } from './services/rfq-notification-service.js';
7
+ import { RfqExpiryWorker } from './services/rfq-expiry-worker.js';
8
+ import { createOrderCompletionReactor } from './services/order-completion-reactor.js';
9
+ import { registerQuoteRequestsCustomerRoutes, } from './routes.customer.js';
10
+ import { registerQuoteRequestsAdminRoutes, } from './routes.admin.js';
11
+ import { registerQuoteRequestsSalesRepRoutes } from './routes.sales-reps.js';
12
+ import { ModuleDisabledError } from '@endora-commerce/platform/kernel';
13
+ /**
14
+ * Conditions the storefront flag handler has already reported (D-43). Per
15
+ * process: these two flags are read on every product card render, so one line
16
+ * per condition is what makes a settings outage findable and one line per read
17
+ * is what buries it.
18
+ */
19
+ const storefrontFlagWarned = new Set();
20
+ export function quoteRequestsModule(options) {
21
+ const eventService = new RfqEventService(options.emFactory);
22
+ const revisionService = new RfqRevisionService(options.emFactory);
23
+ const notificationService = new RfqNotificationService(options.emFactory);
24
+ const salesRepAssignment = options.salesRepAssignment;
25
+ // Business Quote Request ID generator — adapts the composition-wired
26
+ // prefix/suffix resolver closures (SettingsService-backed) to the
27
+ // generator's settings port; the generator itself draws the sequence.
28
+ const businessIdGenerator = createQuoteRequestBusinessIdGenerator(options.resolveBusinessIdPrefix || options.resolveBusinessIdSuffix
29
+ ? {
30
+ resolvePrefix: () => options.resolveBusinessIdPrefix?.() ?? Promise.resolve(''),
31
+ resolveSuffix: () => options.resolveBusinessIdSuffix?.() ?? Promise.resolve(''),
32
+ }
33
+ : undefined);
34
+ const rfqService = new RfqService({
35
+ emFactory: options.emFactory,
36
+ events: options.eventBus,
37
+ eventService,
38
+ revisionService,
39
+ notificationService,
40
+ salesRepAssignment,
41
+ catalogProducts: options.catalogProducts,
42
+ channelMembership: options.channelMembership,
43
+ customerAccounts: options.customerAccounts,
44
+ adminUsers: options.adminUsers,
45
+ carts: options.carts,
46
+ businessId: businessIdGenerator,
47
+ resolveTaxRate: options.resolveTaxRate,
48
+ ...(options.auditLog ? { auditLog: options.auditLog } : {}),
49
+ });
50
+ const adminService = new RfqAdminService({
51
+ emFactory: options.emFactory,
52
+ events: options.eventBus,
53
+ rfqService,
54
+ eventService,
55
+ revisionService,
56
+ notificationService,
57
+ salesRepAssignment,
58
+ catalogProducts: options.catalogProducts,
59
+ customerAccounts: options.customerAccounts,
60
+ organizations: options.organizations,
61
+ adminUsers: options.adminUsers,
62
+ ...(options.auditLog ? { auditLog: options.auditLog } : {}),
63
+ ...(options.customFieldValues ? { customFieldValues: options.customFieldValues } : {}),
64
+ });
65
+ const expiryWorker = new RfqExpiryWorker({
66
+ emFactory: options.emFactory,
67
+ events: options.eventBus,
68
+ eventService,
69
+ notificationService,
70
+ salesRepAssignment,
71
+ adminUsers: options.adminUsers,
72
+ resolveExpiryDays: options.resolveExpiryDays,
73
+ });
74
+ // US5 — the `order.created.v1` reactor. `backend.ts` registers it through
75
+ // `ctx.subscribe`, so a switched-off module completes no quote request.
76
+ const orderCompletionReactor = createOrderCompletionReactor({
77
+ emFactory: options.emFactory,
78
+ orders: options.orders,
79
+ eventService,
80
+ notificationService,
81
+ });
82
+ return {
83
+ register: async (app) => {
84
+ await registerQuoteRequestsCustomerRoutes(app, {
85
+ rfqService,
86
+ requireCustomer: options.requireCustomer,
87
+ resolveCustomerContext: options.resolveCustomerContext,
88
+ ...(options.assertOrganizationCanTransact
89
+ ? { assertOrganizationCanTransact: options.assertOrganizationCanTransact }
90
+ : {}),
91
+ });
92
+ await registerQuoteRequestsAdminRoutes(app, {
93
+ adminService,
94
+ requireAdmin: options.requireAdmin,
95
+ resolveAdminContext: options.resolveAdminContext,
96
+ });
97
+ // D-166 — this module's one endpoint of the sales-rep pair, in this
98
+ // module's own file. It used to `import()` `organizations`' route file
99
+ // and register all four of its endpoints, which is what put a
100
+ // `quote_requests` gate on the assignment screen and an entry in each
101
+ // module's cross-module-import shard. The three endpoints that qualify an
102
+ // organisation are registered by `organizations` from its own
103
+ // `backend.ts` now, under a code that module declares.
104
+ await registerQuoteRequestsSalesRepRoutes(app, {
105
+ emFactory: options.emFactory,
106
+ requireAdmin: options.requireAdmin,
107
+ salesRepAssignment,
108
+ organizations: options.organizations,
109
+ });
110
+ // Storefront-public Quote Requests settings (FR-032 / FR-033) so the
111
+ // storefront can show/hide "Add to quote" buttons without going through
112
+ // the admin-gated settings endpoint. Reads through `resolveBoolSetting`,
113
+ // supplied by `backend.ts`, which already routes the two conditions D-43
114
+ // allows to the module default.
115
+ //
116
+ // The policy — a settings outage must not hide "Add to quote" — is right
117
+ // and stays. What was wrong is that a bare `catch` answered `true` for a
118
+ // **switched-off module** too, which Constitution XVII rule 5 forbids: a
119
+ // module that is off contributes no storefront element. So
120
+ // `ModuleDisabledError` is re-thrown, and anything else is reported once
121
+ // before the fail-open, because a fail-open nobody can see is the defect
122
+ // this whole cluster is about.
123
+ app.get('/api/v1/storefront/settings/quote-requests', async (_request, reply) => {
124
+ reply.header('cache-control', 'public, max-age=60');
125
+ try {
126
+ const card = await options.resolveBoolSetting('show_add_to_quote_on_card');
127
+ const pdp = await options.resolveBoolSetting('show_add_to_quote_on_pdp');
128
+ return { data: { showAddToQuoteOnCard: card, showAddToQuoteOnPdp: pdp } };
129
+ }
130
+ catch (error) {
131
+ if (error instanceof ModuleDisabledError)
132
+ throw error;
133
+ const name = error.name ?? 'Error';
134
+ if (!storefrontFlagWarned.has(name)) {
135
+ storefrontFlagWarned.add(name);
136
+ app.log.warn({ err: error }, '[quote_requests] storefront flag settings unreadable — showing the ' +
137
+ 'quote affordances (logged once per condition per process)');
138
+ }
139
+ return { data: { showAddToQuoteOnCard: true, showAddToQuoteOnPdp: true } };
140
+ }
141
+ });
142
+ },
143
+ handle: () => ({
144
+ orderCompletionReactor,
145
+ expiryWorker,
146
+ rfqService,
147
+ adminService,
148
+ salesRepAssignment,
149
+ }),
150
+ };
151
+ }
152
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}