@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,96 @@
1
+ import { OPEN_QUOTE_REQUEST_STATUSES } from '@endora-commerce/contracts';
2
+ import { QuoteRequest } from '../entities/quote-request.entity.js';
3
+ import { QuoteRequestItem } from '../entities/quote-request-item.entity.js';
4
+ /**
5
+ * The row-level read model `quote_requests` publishes (feature 075, Phase P).
6
+ *
7
+ * Two consumers, two questions. `carts` resolves the quote a cart was
8
+ * converted from; `organizations` counts the open quotes per organisation on
9
+ * the sales-rep screen, where the three open statuses had been written out as
10
+ * a literal array in a module that does not own the lifecycle.
11
+ *
12
+ * The full customer-facing projection stays `RfqService` — this is the row,
13
+ * for the callers that only need to count or cross-reference one.
14
+ */
15
+ export class QuoteRequestReadService {
16
+ emFactory;
17
+ constructor(emFactory) {
18
+ this.emFactory = emFactory;
19
+ }
20
+ async findById(id) {
21
+ const quote = await this.emFactory().findOne(QuoteRequest, { id });
22
+ return quote ? toQuoteRequestRecord(quote) : null;
23
+ }
24
+ /**
25
+ * The business id is unique by construction (`QR-<uuid>` unless a caller
26
+ * passes one) and the column carries a unique index, so `findOne` is the
27
+ * whole of the lookup.
28
+ */
29
+ async findByBusinessId(businessId) {
30
+ const quote = await this.emFactory().findOne(QuoteRequest, { businessId });
31
+ return quote ? toQuoteRequestRecord(quote) : null;
32
+ }
33
+ async listOpenForOrganizations(organizationIds) {
34
+ // A rep assigned nothing sees nothing. Answering the unfiltered set here
35
+ // would be the opposite of what the caller means, and the caller is a
36
+ // visibility screen.
37
+ if (organizationIds.length === 0)
38
+ return [];
39
+ const quotes = await this.emFactory().find(QuoteRequest, {
40
+ organizationId: { $in: [...organizationIds] },
41
+ status: { $in: [...OPEN_QUOTE_REQUEST_STATUSES] },
42
+ });
43
+ return quotes.map(toQuoteRequestRecord);
44
+ }
45
+ /**
46
+ * Added in feature 075's `carts` cut. The quote-to-cart conversion read
47
+ * `QuoteRequestItem` itself; the money columns are deliberately not on the
48
+ * record, because that conversion re-resolves every price against the
49
+ * buyer's current list (FR-017) and a caller that cannot see the quoted
50
+ * price cannot carry it over by accident.
51
+ */
52
+ async listItems(quoteRequestId) {
53
+ const items = await this.emFactory().find(QuoteRequestItem, { quoteRequestId }, { orderBy: { createdAt: 'asc' } });
54
+ return items.map((item) => ({
55
+ id: item.id,
56
+ quoteRequestId: item.quoteRequestId,
57
+ productId: item.productId,
58
+ productName: item.productName,
59
+ variantId: item.variantId ?? null,
60
+ quantity: item.quantity,
61
+ packagingUnitName: item.packagingUnitName ?? null,
62
+ packagingUnitBaseQuantity: item.packagingUnitBaseQuantity ?? null,
63
+ lineCurrency: item.lineCurrency,
64
+ agreedUnitPrice: item.agreedUnitPrice ?? null,
65
+ desiredUnitPrice: item.desiredUnitPrice ?? null,
66
+ }));
67
+ }
68
+ }
69
+ export function toQuoteRequestRecord(quote) {
70
+ return {
71
+ id: quote.id,
72
+ businessId: quote.businessId,
73
+ organizationId: quote.organizationId,
74
+ customerAccountId: quote.customerAccountId,
75
+ createdByAdminUserId: quote.createdByAdminUserId ?? null,
76
+ assignedAdminUserId: quote.assignedAdminUserId ?? null,
77
+ status: quote.status,
78
+ headerNote: quote.headerNote ?? null,
79
+ cancellationReason: quote.cancellationReason ?? null,
80
+ awaitingCustomerRevisionAcceptance: quote.awaitingCustomerRevisionAcceptance,
81
+ lastCustomerSeenRevisionNumber: quote.lastCustomerSeenRevisionNumber,
82
+ currentRevisionNumber: quote.currentRevisionNumber,
83
+ submittedAt: quote.submittedAt ?? null,
84
+ approvedAt: quote.approvedAt ?? null,
85
+ canceledAt: quote.canceledAt ?? null,
86
+ completedAt: quote.completedAt ?? null,
87
+ expiredAt: quote.expiredAt ?? null,
88
+ expiresAt: quote.expiresAt ?? null,
89
+ convertedOrderId: quote.convertedOrderId ?? null,
90
+ customFieldValues: quote.customFieldValues ?? {},
91
+ version: quote.version,
92
+ createdAt: quote.createdAt,
93
+ updatedAt: quote.updatedAt,
94
+ };
95
+ }
96
+ //# sourceMappingURL=quote-request-read-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-request-read-port.js","sourceRoot":"","sources":["../../../src/backend/services/quote-request-read-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The sales channel a quote request is being raised on (issue #266).
3
+ *
4
+ * **Read, never resolved.** The kernel's resolver middleware runs once as an
5
+ * `onRequest` hook on every `/api/v1/*` path and puts the answer on the request
6
+ * scope; this reads that slot. There is no header re-parse, no `sales_channels`
7
+ * query and no second call to the resolver here — MR !775 removed four such
8
+ * re-resolutions and this is not the fifth (feature 053 / FR-011, Principle
9
+ * XII).
10
+ *
11
+ * **`null` is a real answer and the only shape "no channel" is allowed to
12
+ * take.** On a storefront request it cannot happen: step 4 of the resolver
13
+ * falls back to the system-default channel, which always exists. It happens
14
+ * where there is no request scope at all — a CLI entry point, a BullMQ worker,
15
+ * a test or fixture calling the service directly — and there `null` is the
16
+ * truth. It is not filled in with the system default, because that would make
17
+ * a request nobody raised on a channel indistinguishable from one that was,
18
+ * and the FR-006 delete guard would then refuse a channel on evidence the
19
+ * platform made up. An empty string, the nil uuid and `randomUUID()` are the
20
+ * other three spellings of this, and all three are defects (D-47…D-51).
21
+ */
22
+ export declare function raisedOnChannelId(): string | null;
23
+ //# sourceMappingURL=raised-on-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raised-on-channel.d.ts","sourceRoot":"","sources":["../../../src/backend/services/raised-on-channel.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD"}
@@ -0,0 +1,26 @@
1
+ import { currentSalesChannel } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * The sales channel a quote request is being raised on (issue #266).
4
+ *
5
+ * **Read, never resolved.** The kernel's resolver middleware runs once as an
6
+ * `onRequest` hook on every `/api/v1/*` path and puts the answer on the request
7
+ * scope; this reads that slot. There is no header re-parse, no `sales_channels`
8
+ * query and no second call to the resolver here — MR !775 removed four such
9
+ * re-resolutions and this is not the fifth (feature 053 / FR-011, Principle
10
+ * XII).
11
+ *
12
+ * **`null` is a real answer and the only shape "no channel" is allowed to
13
+ * take.** On a storefront request it cannot happen: step 4 of the resolver
14
+ * falls back to the system-default channel, which always exists. It happens
15
+ * where there is no request scope at all — a CLI entry point, a BullMQ worker,
16
+ * a test or fixture calling the service directly — and there `null` is the
17
+ * truth. It is not filled in with the system default, because that would make
18
+ * a request nobody raised on a channel indistinguishable from one that was,
19
+ * and the FR-006 delete guard would then refuse a channel on evidence the
20
+ * platform made up. An empty string, the nil uuid and `randomUUID()` are the
21
+ * other three spellings of this, and all three are defects (D-47…D-51).
22
+ */
23
+ export function raisedOnChannelId() {
24
+ return currentSalesChannel()?.id ?? null;
25
+ }
26
+ //# sourceMappingURL=raised-on-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raised-on-channel.js","sourceRoot":"","sources":["../../../src/backend/services/raised-on-channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,mBAAmB,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,75 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type AdminCreateQuoteRequest, type AdminPatchQuoteRequest, type QuoteRequest as RfqDto, type QuoteRequestSummary, type AdminUserReadPort, type CatalogProductReadPort, type CustomerAccountReadPort, type CustomFieldValuePort, type OrganizationDetailsPort, type SalesRepAssignmentPort } from '@endora-commerce/contracts';
3
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
4
+ import { type QuoteRequestStatus } from '../entities/quote-request.entity.js';
5
+ import type { RfqService } from './rfq-service.js';
6
+ import { type RfqEventBus } from './rfq-service.js';
7
+ import type { RfqEventService } from './rfq-event-service.js';
8
+ import type { RfqRevisionService } from './rfq-revision-service.js';
9
+ import type { RfqNotificationService } from './rfq-notification-service.js';
10
+ /**
11
+ * Admin-facing Quote Requests service — feature 008 workflow.
12
+ *
13
+ * Endpoints implemented:
14
+ * - listAll(scope) (US2)
15
+ * - getById (US2)
16
+ * - approve (US2)
17
+ * - cancel (US2)
18
+ * - assign (US2)
19
+ * - modify (US3)
20
+ * - createOnBehalf (US4)
21
+ *
22
+ * Visibility scoping is centralised in `SalesRepAssignmentService.canSeeOrganization`
23
+ * (research §R2). Platform admins skip the predicate.
24
+ */
25
+ export interface AdminContext {
26
+ adminUserId: string;
27
+ isPlatformAdmin: boolean;
28
+ /** Display label embedded in event records ('Sales representative', 'Platform administrator', …) */
29
+ roleLabel: string;
30
+ }
31
+ export type AdminAssignmentScope = 'mine' | 'unassigned' | 'all';
32
+ export interface AdminListFilter {
33
+ scope?: AdminAssignmentScope;
34
+ status?: QuoteRequestStatus | QuoteRequestStatus[];
35
+ organizationId?: string;
36
+ }
37
+ export interface RfqAdminServiceDeps {
38
+ emFactory: () => EntityManager;
39
+ events: RfqEventBus;
40
+ rfqService: RfqService;
41
+ eventService: RfqEventService;
42
+ revisionService: RfqRevisionService;
43
+ notificationService: RfqNotificationService;
44
+ salesRepAssignment: SalesRepAssignmentPort;
45
+ /** Feature 054 — audits RFQ admin writes co-transactionally when provided. */
46
+ auditLog?: AuditPort;
47
+ /** Feature 055 — validates + merges custom-field values on RFQ edit. */
48
+ customFieldValues?: CustomFieldValuePort;
49
+ /**
50
+ * Feature 075, Phase C — the three rows the admin list and detail read and
51
+ * none of which this module owns. They were `em.find` calls against
52
+ * `catalog`'s, `customer_accounts`' and `organizations`' tables; over their
53
+ * published ports the same reads fail closed with their owners.
54
+ */
55
+ catalogProducts: CatalogProductReadPort;
56
+ customerAccounts: CustomerAccountReadPort;
57
+ organizations: OrganizationDetailsPort;
58
+ adminUsers: AdminUserReadPort;
59
+ }
60
+ export declare class RfqAdminService {
61
+ #private;
62
+ private readonly deps;
63
+ constructor(deps: RfqAdminServiceDeps);
64
+ listAll(ctx: AdminContext, filter?: AdminListFilter): Promise<QuoteRequestSummary[]>;
65
+ getById(ctx: AdminContext, rfqId: string): Promise<RfqDto>;
66
+ approve(ctx: AdminContext, rfqId: string, expectedVersion: number | null, note: string | undefined): Promise<RfqDto>;
67
+ cancel(ctx: AdminContext, rfqId: string, expectedVersion: number | null, reason: string | undefined): Promise<RfqDto>;
68
+ assign(ctx: AdminContext, rfqId: string, targetAdminUserId: string): Promise<RfqDto>;
69
+ modify(ctx: AdminContext, rfqId: string, expectedVersion: number | null, body: AdminPatchQuoteRequest): Promise<RfqDto>;
70
+ createOnBehalf(ctx: AdminContext, body: AdminCreateQuoteRequest): Promise<RfqDto>;
71
+ private findVisibleForAdmin;
72
+ private assertVersion;
73
+ private isUnassigned;
74
+ }
75
+ //# sourceMappingURL=rfq-admin-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rfq-admin-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/rfq-admin-service.ts"],"names":[],"mappings":"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"}