@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.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/admin/index.d.ts +26 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +56 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/RfqCreatePage.d.ts +9 -0
- package/dist/admin/pages/RfqCreatePage.d.ts.map +1 -0
- package/dist/admin/pages/RfqCreatePage.js +163 -0
- package/dist/admin/pages/RfqCreatePage.js.map +1 -0
- package/dist/admin/pages/RfqDetail.d.ts +9 -0
- package/dist/admin/pages/RfqDetail.d.ts.map +1 -0
- package/dist/admin/pages/RfqDetail.js +399 -0
- package/dist/admin/pages/RfqDetail.js.map +1 -0
- package/dist/admin/pages/RfqList.d.ts +9 -0
- package/dist/admin/pages/RfqList.d.ts.map +1 -0
- package/dist/admin/pages/RfqList.js +214 -0
- package/dist/admin/pages/RfqList.js.map +1 -0
- package/dist/admin/validity.d.ts +52 -0
- package/dist/admin/validity.d.ts.map +1 -0
- package/dist/admin/validity.js +61 -0
- package/dist/admin/validity.js.map +1 -0
- package/dist/backend/entities/quote-request-event.entity.d.ts +24 -0
- package/dist/backend/entities/quote-request-event.entity.d.ts.map +1 -0
- package/dist/backend/entities/quote-request-event.entity.js +67 -0
- package/dist/backend/entities/quote-request-event.entity.js.map +1 -0
- package/dist/backend/entities/quote-request-item.entity.d.ts +34 -0
- package/dist/backend/entities/quote-request-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/quote-request-item.entity.js +119 -0
- package/dist/backend/entities/quote-request-item.entity.js.map +1 -0
- package/dist/backend/entities/quote-request-notification-event.entity.d.ts +23 -0
- package/dist/backend/entities/quote-request-notification-event.entity.d.ts.map +1 -0
- package/dist/backend/entities/quote-request-notification-event.entity.js +72 -0
- package/dist/backend/entities/quote-request-notification-event.entity.js.map +1 -0
- package/dist/backend/entities/quote-request-revision.entity.d.ts +35 -0
- package/dist/backend/entities/quote-request-revision.entity.d.ts.map +1 -0
- package/dist/backend/entities/quote-request-revision.entity.js +67 -0
- package/dist/backend/entities/quote-request-revision.entity.js.map +1 -0
- package/dist/backend/entities/quote-request.entity.d.ts +90 -0
- package/dist/backend/entities/quote-request.entity.d.ts.map +1 -0
- package/dist/backend/entities/quote-request.entity.js +191 -0
- package/dist/backend/entities/quote-request.entity.js.map +1 -0
- package/dist/backend/index.d.ts +120 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +361 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +95 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +152 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +13 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +82 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.customer.d.ts +18 -0
- package/dist/backend/routes.customer.d.ts.map +1 -0
- package/dist/backend/routes.customer.js +98 -0
- package/dist/backend/routes.customer.js.map +1 -0
- package/dist/backend/routes.sales-reps.d.ts +12 -0
- package/dist/backend/routes.sales-reps.d.ts.map +1 -0
- package/dist/backend/routes.sales-reps.js +57 -0
- package/dist/backend/routes.sales-reps.js.map +1 -0
- package/dist/backend/services/order-completion-reactor.d.ts +32 -0
- package/dist/backend/services/order-completion-reactor.d.ts.map +1 -0
- package/dist/backend/services/order-completion-reactor.js +57 -0
- package/dist/backend/services/order-completion-reactor.js.map +1 -0
- package/dist/backend/services/quote-request-business-id-generator.d.ts +46 -0
- package/dist/backend/services/quote-request-business-id-generator.d.ts.map +1 -0
- package/dist/backend/services/quote-request-business-id-generator.js +69 -0
- package/dist/backend/services/quote-request-business-id-generator.js.map +1 -0
- package/dist/backend/services/quote-request-read-port.d.ts +36 -0
- package/dist/backend/services/quote-request-read-port.d.ts.map +1 -0
- package/dist/backend/services/quote-request-read-port.js +96 -0
- package/dist/backend/services/quote-request-read-port.js.map +1 -0
- package/dist/backend/services/raised-on-channel.d.ts +23 -0
- package/dist/backend/services/raised-on-channel.d.ts.map +1 -0
- package/dist/backend/services/raised-on-channel.js +26 -0
- package/dist/backend/services/raised-on-channel.js.map +1 -0
- package/dist/backend/services/rfq-admin-service.d.ts +75 -0
- package/dist/backend/services/rfq-admin-service.d.ts.map +1 -0
- package/dist/backend/services/rfq-admin-service.js +487 -0
- package/dist/backend/services/rfq-admin-service.js.map +1 -0
- package/dist/backend/services/rfq-event-service.d.ts +32 -0
- package/dist/backend/services/rfq-event-service.d.ts.map +1 -0
- package/dist/backend/services/rfq-event-service.js +28 -0
- package/dist/backend/services/rfq-event-service.js.map +1 -0
- package/dist/backend/services/rfq-expiry-worker.d.ts +50 -0
- package/dist/backend/services/rfq-expiry-worker.d.ts.map +1 -0
- package/dist/backend/services/rfq-expiry-worker.js +70 -0
- package/dist/backend/services/rfq-expiry-worker.js.map +1 -0
- package/dist/backend/services/rfq-notification-service.d.ts +36 -0
- package/dist/backend/services/rfq-notification-service.d.ts.map +1 -0
- package/dist/backend/services/rfq-notification-service.js +52 -0
- package/dist/backend/services/rfq-notification-service.js.map +1 -0
- package/dist/backend/services/rfq-revision-service.d.ts +70 -0
- package/dist/backend/services/rfq-revision-service.d.ts.map +1 -0
- package/dist/backend/services/rfq-revision-service.js +120 -0
- package/dist/backend/services/rfq-revision-service.js.map +1 -0
- package/dist/backend/services/rfq-service.d.ts +140 -0
- package/dist/backend/services/rfq-service.d.ts.map +1 -0
- package/dist/backend/services/rfq-service.js +786 -0
- package/dist/backend/services/rfq-service.js.map +1 -0
- package/dist/backend/services/sales-channel-attributions.d.ts +38 -0
- package/dist/backend/services/sales-channel-attributions.d.ts.map +1 -0
- package/dist/backend/services/sales-channel-attributions.js +48 -0
- package/dist/backend/services/sales-channel-attributions.js.map +1 -0
- package/dist/manifest.d.ts +203 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +277 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260424T190112_quote_requests_init.d.ts +19 -0
- package/dist/migrations/20260424T190112_quote_requests_init.d.ts.map +1 -0
- package/dist/migrations/20260424T190112_quote_requests_init.js +65 -0
- package/dist/migrations/20260424T190112_quote_requests_init.js.map +1 -0
- package/dist/migrations/20260503T141344_quote_requests_workflow.d.ts +37 -0
- package/dist/migrations/20260503T141344_quote_requests_workflow.d.ts.map +1 -0
- package/dist/migrations/20260503T141344_quote_requests_workflow.js +251 -0
- package/dist/migrations/20260503T141344_quote_requests_workflow.js.map +1 -0
- package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.d.ts +10 -0
- package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.d.ts.map +1 -0
- package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.js +16 -0
- package/dist/migrations/20260611T140415_quote_requests_qr_item_packaging.js.map +1 -0
- package/dist/migrations/20260611T140417_quote_requests_business_id.d.ts +24 -0
- package/dist/migrations/20260611T140417_quote_requests_business_id.d.ts.map +1 -0
- package/dist/migrations/20260611T140417_quote_requests_business_id.js +34 -0
- package/dist/migrations/20260611T140417_quote_requests_business_id.js.map +1 -0
- package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.d.ts +21 -0
- package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.d.ts.map +1 -0
- package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.js +29 -0
- package/dist/migrations/20260617T095510_quote_requests_backfill_admin_created_awaiting.js.map +1 -0
- package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.d.ts +12 -0
- package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.d.ts.map +1 -0
- package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.js +16 -0
- package/dist/migrations/20260718T200342_quote_requests_quote_request_custom_field_values.js.map +1 -0
- package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts +34 -0
- package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts.map +1 -0
- package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js +57 -0
- package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js.map +1 -0
- package/dist/migrations/index.d.ts +53 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +61 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/status-mapping.d.ts +18 -0
- package/dist/migrations/status-mapping.d.ts.map +1 -0
- package/dist/migrations/status-mapping.js +28 -0
- package/dist/migrations/status-mapping.js.map +1 -0
- package/docs/quote_requests.md +168 -0
- package/i18n/en.json +14 -0
- package/i18n/pl.json +14 -0
- package/package.json +99 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* `quote_requests.sales_channel_id` — the channel a quote request was raised
|
|
4
|
+
* on (feature 005, FR-012).
|
|
5
|
+
*
|
|
6
|
+
* The column, its foreign key and its index were added by the platform's
|
|
7
|
+
* frozen `Migration20260430T170044CoreSalesChannelsPromote` until
|
|
8
|
+
* `specs/120-migration-closure-bridge-ownership/` Phase 3. Under D-226 a
|
|
9
|
+
* migration may name a table only if its own module creates it, a module in
|
|
10
|
+
* its transitive `dependencies` closure creates it, or the platform creates
|
|
11
|
+
* it — and the platform declares no dependencies, so it could never name
|
|
12
|
+
* `quote_requests` at all. An instance that omits this module had a frozen
|
|
13
|
+
* corpus altering a table nothing builds.
|
|
14
|
+
*
|
|
15
|
+
* Here the closure holds in both directions the SQL runs: `quote_requests` is
|
|
16
|
+
* this module's own table, and `sales_channels` is the kernel's, created
|
|
17
|
+
* inside the frozen prefix that every above-watermark migration runs after.
|
|
18
|
+
* `sales_channels` is in this module's `dependencies` besides.
|
|
19
|
+
*
|
|
20
|
+
* **Written idempotently**, because a database that has already applied the
|
|
21
|
+
* frozen migration carries all three objects. The storage keys on the class
|
|
22
|
+
* name and holds no checksum, so the reduced frozen body is not re-offered
|
|
23
|
+
* there; this migration is the no-op it reads as, and on a fresh database it
|
|
24
|
+
* is the change. The constraint add is guarded by a `pg_constraint` probe
|
|
25
|
+
* because PostgreSQL has no `add constraint if not exists`.
|
|
26
|
+
*
|
|
27
|
+
* The column stays NULLABLE, as it was: the backfill and the NOT NULL flip
|
|
28
|
+
* are a separate step (feature 005 T060) and are not this migration's.
|
|
29
|
+
*/
|
|
30
|
+
export declare class Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution extends Migration {
|
|
31
|
+
up(): Promise<void>;
|
|
32
|
+
down(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=20260912T125614_quote_requests_quote_request_channel_attribution.d.ts.map
|
package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260912T125614_quote_requests_quote_request_channel_attribution.d.ts","sourceRoot":"","sources":["../../src/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,mEAAoE,SAAQ,SAAS;IACjF,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAOrC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* `quote_requests.sales_channel_id` — the channel a quote request was raised
|
|
4
|
+
* on (feature 005, FR-012).
|
|
5
|
+
*
|
|
6
|
+
* The column, its foreign key and its index were added by the platform's
|
|
7
|
+
* frozen `Migration20260430T170044CoreSalesChannelsPromote` until
|
|
8
|
+
* `specs/120-migration-closure-bridge-ownership/` Phase 3. Under D-226 a
|
|
9
|
+
* migration may name a table only if its own module creates it, a module in
|
|
10
|
+
* its transitive `dependencies` closure creates it, or the platform creates
|
|
11
|
+
* it — and the platform declares no dependencies, so it could never name
|
|
12
|
+
* `quote_requests` at all. An instance that omits this module had a frozen
|
|
13
|
+
* corpus altering a table nothing builds.
|
|
14
|
+
*
|
|
15
|
+
* Here the closure holds in both directions the SQL runs: `quote_requests` is
|
|
16
|
+
* this module's own table, and `sales_channels` is the kernel's, created
|
|
17
|
+
* inside the frozen prefix that every above-watermark migration runs after.
|
|
18
|
+
* `sales_channels` is in this module's `dependencies` besides.
|
|
19
|
+
*
|
|
20
|
+
* **Written idempotently**, because a database that has already applied the
|
|
21
|
+
* frozen migration carries all three objects. The storage keys on the class
|
|
22
|
+
* name and holds no checksum, so the reduced frozen body is not re-offered
|
|
23
|
+
* there; this migration is the no-op it reads as, and on a fresh database it
|
|
24
|
+
* is the change. The constraint add is guarded by a `pg_constraint` probe
|
|
25
|
+
* because PostgreSQL has no `add constraint if not exists`.
|
|
26
|
+
*
|
|
27
|
+
* The column stays NULLABLE, as it was: the backfill and the NOT NULL flip
|
|
28
|
+
* are a separate step (feature 005 T060) and are not this migration's.
|
|
29
|
+
*/
|
|
30
|
+
export class Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution extends Migration {
|
|
31
|
+
async up() {
|
|
32
|
+
this.addSql('alter table "quote_requests" add column if not exists "sales_channel_id" uuid null;');
|
|
33
|
+
this.addSql(`
|
|
34
|
+
do $$
|
|
35
|
+
begin
|
|
36
|
+
if not exists (
|
|
37
|
+
select 1 from pg_constraint
|
|
38
|
+
where "conname" = 'quote_requests_sales_channel_fk'
|
|
39
|
+
and "conrelid" = '"quote_requests"'::regclass
|
|
40
|
+
) then
|
|
41
|
+
alter table "quote_requests"
|
|
42
|
+
add constraint "quote_requests_sales_channel_fk"
|
|
43
|
+
foreign key ("sales_channel_id") references "sales_channels" ("id")
|
|
44
|
+
on delete restrict;
|
|
45
|
+
end if;
|
|
46
|
+
end $$;
|
|
47
|
+
`);
|
|
48
|
+
this.addSql('create index if not exists "quote_requests_sales_channel_id_index" ' +
|
|
49
|
+
'on "quote_requests" ("sales_channel_id");');
|
|
50
|
+
}
|
|
51
|
+
async down() {
|
|
52
|
+
this.addSql('drop index if exists "quote_requests_sales_channel_id_index";');
|
|
53
|
+
this.addSql('alter table "quote_requests" drop constraint if exists "quote_requests_sales_channel_fk";');
|
|
54
|
+
this.addSql('alter table "quote_requests" drop column if exists "sales_channel_id";');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=20260912T125614_quote_requests_quote_request_channel_attribution.js.map
|
package/dist/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260912T125614_quote_requests_quote_request_channel_attribution.js","sourceRoot":"","sources":["../../src/migrations/20260912T125614_quote_requests_quote_request_channel_attribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,mEAAoE,SAAQ,SAAS;IACvF,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,qFAAqF,CACtF,CAAC;QACF,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;KAcX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,qEAAqE;YACnE,2CAA2C,CAC9C,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,+DAA+D,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,CACT,2FAA2F,CAC5F,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,wEAAwE,CAAC,CAAC;IACxF,CAAC;CACF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent — *"the `./migrations` export of
|
|
8
|
+
* @endora-commerce/mod-quote-requests exports no 'migrations' array"*. `blog`
|
|
9
|
+
* published only `export *` here until D-168, so an installed `blog` did not
|
|
10
|
+
* merely lose its schema, it stopped the platform from booting; the defect was
|
|
11
|
+
* invisible because a workspace member is not an installed package and the
|
|
12
|
+
* committed host registry names each class directly.
|
|
13
|
+
*
|
|
14
|
+
* **Six classes, and the order in this array is not the order they run in.**
|
|
15
|
+
* They are listed here in ascending timestamp, which is the order
|
|
16
|
+
* `backend/src/db/migration-order.ts` computes *within* a module — and within a
|
|
17
|
+
* module only. Where this module's block sits relative to every other module's
|
|
18
|
+
* is decided by the manifest `dependencies` graph, not by a timestamp and not by
|
|
19
|
+
* this file (feature 081). Two of these six share nothing but their day:
|
|
20
|
+
* `…T140415_…_qr_item_packaging` and `…T140417_…_business_id` are two minutes
|
|
21
|
+
* apart and both depend on the `…T141344_…_workflow` rewrite above them, which
|
|
22
|
+
* is exactly what "a timestamp orders a module's own migrations" buys.
|
|
23
|
+
*
|
|
24
|
+
* The **named** exports stay, and the asymmetry with `./backend` — which
|
|
25
|
+
* publishes an array and no named class (D-168) — is deliberate.
|
|
26
|
+
* `db/migrations-registry.generated.ts` imports each class by name from this
|
|
27
|
+
* specifier and hands it to `migration('quote_requests', …)`, and a migration
|
|
28
|
+
* class name is contract in a way an entity class name is not:
|
|
29
|
+
* `mikro_orm_migrations` persists it, so it is a string every already-migrated
|
|
30
|
+
* database holds. It also carries none of the hazard D-168 removes — no module
|
|
31
|
+
* has a reason to name another module's migration, and doing so buys nothing an
|
|
32
|
+
* entity import buys.
|
|
33
|
+
*
|
|
34
|
+
* A class that is in neither the array nor the barrel is a migration that does
|
|
35
|
+
* not run: `migration:pending` reports nothing pending and the first symptom is
|
|
36
|
+
* a query against a table nobody created. The composer refuses a migration file
|
|
37
|
+
* no declared subpath covers for exactly that reason, but it cannot see whether
|
|
38
|
+
* the barrel behind the subpath actually carries the class — that is this
|
|
39
|
+
* file's job.
|
|
40
|
+
*
|
|
41
|
+
* `status-mapping.ts` sits in this directory and is deliberately **not** here:
|
|
42
|
+
* it is a §4 helper, not a migration (see the note in `generate-composer.ts`).
|
|
43
|
+
*/
|
|
44
|
+
import { Migration20260424T190112QuoteRequestsInit } from './20260424T190112_quote_requests_init.js';
|
|
45
|
+
import { Migration20260503T141344QuoteRequestsWorkflow } from './20260503T141344_quote_requests_workflow.js';
|
|
46
|
+
import { Migration20260611T140415QuoteRequestsQrItemPackaging } from './20260611T140415_quote_requests_qr_item_packaging.js';
|
|
47
|
+
import { Migration20260611T140417QuoteRequestsBusinessId } from './20260611T140417_quote_requests_business_id.js';
|
|
48
|
+
import { Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting } from './20260617T095510_quote_requests_backfill_admin_created_awaiting.js';
|
|
49
|
+
import { Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues } from './20260718T200342_quote_requests_quote_request_custom_field_values.js';
|
|
50
|
+
import { Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution } from './20260912T125614_quote_requests_quote_request_channel_attribution.js';
|
|
51
|
+
export declare const migrations: (typeof Migration20260424T190112QuoteRequestsInit)[];
|
|
52
|
+
export { Migration20260424T190112QuoteRequestsInit, Migration20260503T141344QuoteRequestsWorkflow, Migration20260611T140415QuoteRequestsQrItemPackaging, Migration20260611T140417QuoteRequestsBusinessId, Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting, Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues, Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution, };
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,EAAE,6CAA6C,EAAE,MAAM,8CAA8C,CAAC;AAC7G,OAAO,EAAE,oDAAoD,EAAE,MAAM,uDAAuD,CAAC;AAC7H,OAAO,EAAE,+CAA+C,EAAE,MAAM,iDAAiD,CAAC;AAClH,OAAO,EAAE,iEAAiE,EAAE,MAAM,qEAAqE,CAAC;AACxJ,OAAO,EAAE,kEAAkE,EAAE,MAAM,uEAAuE,CAAC;AAC3J,OAAO,EAAE,mEAAmE,EAAE,MAAM,uEAAuE,CAAC;AAE5J,eAAO,MAAM,UAAU,sDAQtB,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,6CAA6C,EAC7C,oDAAoD,EACpD,+CAA+C,EAC/C,iEAAiE,EACjE,kEAAkE,EAClE,mEAAmE,GACpE,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent — *"the `./migrations` export of
|
|
8
|
+
* @endora-commerce/mod-quote-requests exports no 'migrations' array"*. `blog`
|
|
9
|
+
* published only `export *` here until D-168, so an installed `blog` did not
|
|
10
|
+
* merely lose its schema, it stopped the platform from booting; the defect was
|
|
11
|
+
* invisible because a workspace member is not an installed package and the
|
|
12
|
+
* committed host registry names each class directly.
|
|
13
|
+
*
|
|
14
|
+
* **Six classes, and the order in this array is not the order they run in.**
|
|
15
|
+
* They are listed here in ascending timestamp, which is the order
|
|
16
|
+
* `backend/src/db/migration-order.ts` computes *within* a module — and within a
|
|
17
|
+
* module only. Where this module's block sits relative to every other module's
|
|
18
|
+
* is decided by the manifest `dependencies` graph, not by a timestamp and not by
|
|
19
|
+
* this file (feature 081). Two of these six share nothing but their day:
|
|
20
|
+
* `…T140415_…_qr_item_packaging` and `…T140417_…_business_id` are two minutes
|
|
21
|
+
* apart and both depend on the `…T141344_…_workflow` rewrite above them, which
|
|
22
|
+
* is exactly what "a timestamp orders a module's own migrations" buys.
|
|
23
|
+
*
|
|
24
|
+
* The **named** exports stay, and the asymmetry with `./backend` — which
|
|
25
|
+
* publishes an array and no named class (D-168) — is deliberate.
|
|
26
|
+
* `db/migrations-registry.generated.ts` imports each class by name from this
|
|
27
|
+
* specifier and hands it to `migration('quote_requests', …)`, and a migration
|
|
28
|
+
* class name is contract in a way an entity class name is not:
|
|
29
|
+
* `mikro_orm_migrations` persists it, so it is a string every already-migrated
|
|
30
|
+
* database holds. It also carries none of the hazard D-168 removes — no module
|
|
31
|
+
* has a reason to name another module's migration, and doing so buys nothing an
|
|
32
|
+
* entity import buys.
|
|
33
|
+
*
|
|
34
|
+
* A class that is in neither the array nor the barrel is a migration that does
|
|
35
|
+
* not run: `migration:pending` reports nothing pending and the first symptom is
|
|
36
|
+
* a query against a table nobody created. The composer refuses a migration file
|
|
37
|
+
* no declared subpath covers for exactly that reason, but it cannot see whether
|
|
38
|
+
* the barrel behind the subpath actually carries the class — that is this
|
|
39
|
+
* file's job.
|
|
40
|
+
*
|
|
41
|
+
* `status-mapping.ts` sits in this directory and is deliberately **not** here:
|
|
42
|
+
* it is a §4 helper, not a migration (see the note in `generate-composer.ts`).
|
|
43
|
+
*/
|
|
44
|
+
import { Migration20260424T190112QuoteRequestsInit } from './20260424T190112_quote_requests_init.js';
|
|
45
|
+
import { Migration20260503T141344QuoteRequestsWorkflow } from './20260503T141344_quote_requests_workflow.js';
|
|
46
|
+
import { Migration20260611T140415QuoteRequestsQrItemPackaging } from './20260611T140415_quote_requests_qr_item_packaging.js';
|
|
47
|
+
import { Migration20260611T140417QuoteRequestsBusinessId } from './20260611T140417_quote_requests_business_id.js';
|
|
48
|
+
import { Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting } from './20260617T095510_quote_requests_backfill_admin_created_awaiting.js';
|
|
49
|
+
import { Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues } from './20260718T200342_quote_requests_quote_request_custom_field_values.js';
|
|
50
|
+
import { Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution } from './20260912T125614_quote_requests_quote_request_channel_attribution.js';
|
|
51
|
+
export const migrations = [
|
|
52
|
+
Migration20260424T190112QuoteRequestsInit,
|
|
53
|
+
Migration20260503T141344QuoteRequestsWorkflow,
|
|
54
|
+
Migration20260611T140415QuoteRequestsQrItemPackaging,
|
|
55
|
+
Migration20260611T140417QuoteRequestsBusinessId,
|
|
56
|
+
Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting,
|
|
57
|
+
Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues,
|
|
58
|
+
Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution,
|
|
59
|
+
];
|
|
60
|
+
export { Migration20260424T190112QuoteRequestsInit, Migration20260503T141344QuoteRequestsWorkflow, Migration20260611T140415QuoteRequestsQrItemPackaging, Migration20260611T140417QuoteRequestsBusinessId, Migration20260617T095510QuoteRequestsBackfillAdminCreatedAwaiting, Migration20260718T200342QuoteRequestsQuoteRequestCustomFieldValues, Migration20260912T125614QuoteRequestsQuoteRequestChannelAttribution, };
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,EAAE,6CAA6C,EAAE,MAAM,8CAA8C,CAAC;AAC7G,OAAO,EAAE,oDAAoD,EAAE,MAAM,uDAAuD,CAAC;AAC7H,OAAO,EAAE,+CAA+C,EAAE,MAAM,iDAAiD,CAAC;AAClH,OAAO,EAAE,iEAAiE,EAAE,MAAM,qEAAqE,CAAC;AACxJ,OAAO,EAAE,kEAAkE,EAAE,MAAM,uEAAuE,CAAC;AAC3J,OAAO,EAAE,mEAAmE,EAAE,MAAM,uEAAuE,CAAC;AAE5J,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,yCAAyC;IACzC,6CAA6C;IAC7C,oDAAoD;IACpD,+CAA+C;IAC/C,iEAAiE;IACjE,kEAAkE;IAClE,mEAAmE;CACpE,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,6CAA6C,EAC7C,oDAAoD,EACpD,+CAA+C,EAC/C,iEAAiE,EACjE,kEAAkE,EAClE,mEAAmE,GACpE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy → new status mapping for the Quote Requests workflow rewrite
|
|
3
|
+
* (feature 008). The mapping itself is pure so we can unit-test it
|
|
4
|
+
* without bringing up Postgres. The migration consumes this exact
|
|
5
|
+
* function in its data step.
|
|
6
|
+
*
|
|
7
|
+
* Spec reference: research.md §R1, data-model.md §8.
|
|
8
|
+
*/
|
|
9
|
+
export type LegacyStatus = 'draft' | 'new' | 'under_review' | 'quoted' | 'accepted' | 'rejected' | 'expired';
|
|
10
|
+
export type NewStatus = 'Created from admin' | 'Pending' | 'Canceled' | 'Approved' | 'Completed' | 'Expired';
|
|
11
|
+
export interface MappingResult {
|
|
12
|
+
/** Target status. `null` means the row should be deleted (legacy `draft`). */
|
|
13
|
+
status: NewStatus | null;
|
|
14
|
+
/** Whether to set `awaiting_customer_revision_acceptance = true`. */
|
|
15
|
+
awaitingCustomerRevisionAcceptance: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function mapLegacyStatus(legacy: LegacyStatus): MappingResult;
|
|
18
|
+
//# sourceMappingURL=status-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-mapping.d.ts","sourceRoot":"","sources":["../../src/migrations/status-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,KAAK,GACL,cAAc,GACd,QAAQ,GACR,UAAU,GACV,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,MAAM,SAAS,GACjB,oBAAoB,GACpB,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,qEAAqE;IACrE,kCAAkC,EAAE,OAAO,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAkBnE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy → new status mapping for the Quote Requests workflow rewrite
|
|
3
|
+
* (feature 008). The mapping itself is pure so we can unit-test it
|
|
4
|
+
* without bringing up Postgres. The migration consumes this exact
|
|
5
|
+
* function in its data step.
|
|
6
|
+
*
|
|
7
|
+
* Spec reference: research.md §R1, data-model.md §8.
|
|
8
|
+
*/
|
|
9
|
+
export function mapLegacyStatus(legacy) {
|
|
10
|
+
switch (legacy) {
|
|
11
|
+
case 'draft':
|
|
12
|
+
return { status: null, awaitingCustomerRevisionAcceptance: false };
|
|
13
|
+
case 'new':
|
|
14
|
+
case 'under_review':
|
|
15
|
+
return { status: 'Pending', awaitingCustomerRevisionAcceptance: false };
|
|
16
|
+
case 'quoted':
|
|
17
|
+
// Sales rep had issued a counter-quote; under the new model that is
|
|
18
|
+
// the "modified, awaiting customer accept/reject" state.
|
|
19
|
+
return { status: 'Pending', awaitingCustomerRevisionAcceptance: true };
|
|
20
|
+
case 'accepted':
|
|
21
|
+
return { status: 'Approved', awaitingCustomerRevisionAcceptance: false };
|
|
22
|
+
case 'rejected':
|
|
23
|
+
return { status: 'Canceled', awaitingCustomerRevisionAcceptance: false };
|
|
24
|
+
case 'expired':
|
|
25
|
+
return { status: 'Expired', awaitingCustomerRevisionAcceptance: false };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=status-mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-mapping.js","sourceRoot":"","sources":["../../src/migrations/status-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0BH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QACrE,KAAK,KAAK,CAAC;QACX,KAAK,cAAc;YACjB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QAC1E,KAAK,QAAQ;YACX,oEAAoE;YACpE,yDAAyD;YACzD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kCAAkC,EAAE,IAAI,EAAE,CAAC;QACzE,KAAK,UAAU;YACb,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QAC3E,KAAK,UAAU;YACb,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;QAC3E,KAAK,SAAS;YACZ,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC;IAC5E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: quote_requests
|
|
3
|
+
description: RFQ lifecycle (draft → quote → accept/reject)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `quote_requests`
|
|
7
|
+
|
|
8
|
+
The Quote Requests module implements the B2B negotiation loop.
|
|
9
|
+
A customer (or sales representative on the
|
|
10
|
+
customer's behalf) drafts a Quote Request, the other side reviews it,
|
|
11
|
+
either side may modify the request and require explicit re-acceptance,
|
|
12
|
+
and an approved Quote Request can be turned into an order through the
|
|
13
|
+
standard checkout. Every transition is captured in an append-only
|
|
14
|
+
event log so the customer-facing detail page and the admin detail page
|
|
15
|
+
both render the same chronological history.
|
|
16
|
+
|
|
17
|
+
## Statuses
|
|
18
|
+
|
|
19
|
+
Six values, replacing the foundation-era set:
|
|
20
|
+
|
|
21
|
+
- `Created from admin` — drafted by a sales rep / admin, awaiting customer acceptance.
|
|
22
|
+
- `Pending` — submitted by the customer, awaiting internal-side response.
|
|
23
|
+
- `Approved` — green-lit by the responsible party.
|
|
24
|
+
- `Completed` — an order has been placed from this Quote Request.
|
|
25
|
+
- `Canceled` — rejected by either party (with optional reason).
|
|
26
|
+
- `Expired` — auto-flipped by the expiry worker when the configured threshold elapses.
|
|
27
|
+
|
|
28
|
+
`Canceled`, `Completed`, and `Expired` are terminal.
|
|
29
|
+
|
|
30
|
+
## Public surface
|
|
31
|
+
|
|
32
|
+
Customer endpoints require a customer session; admin endpoints are
|
|
33
|
+
gated by `rfqs:handle`. PATCH-shaped endpoints accept `If-Match`
|
|
34
|
+
versions for optimistic concurrency, and customer accept/reject
|
|
35
|
+
revision additionally pin `expectedRevisionNumber`.
|
|
36
|
+
|
|
37
|
+
| Verb + Path | Audience | Purpose |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `GET /api/v1/quote-requests` | customer | List visible Quote Requests (org-admin role expands to whole-org visibility). |
|
|
40
|
+
| `GET /api/v1/quote-requests/:id` | customer | Detail with optional `comparisonAgainstLastSeen` block while awaiting acceptance. |
|
|
41
|
+
| `POST /api/v1/quote-requests` | customer | Create + submit in one call. |
|
|
42
|
+
| `PATCH /api/v1/quote-requests/:id` | customer | Edit a Pending RFQ that the internal side has not yet touched. |
|
|
43
|
+
| `POST /api/v1/quote-requests/:id/accept-revision` | customer | Accept the latest revision (also covers `Created from admin`). |
|
|
44
|
+
| `POST /api/v1/quote-requests/:id/reject-revision` | customer | Reject the latest revision with optional reason. |
|
|
45
|
+
| `POST /api/v1/quote-requests/:id/resubmit` | customer | Clone an old Quote Request into a new Pending one at the customer's current price list. |
|
|
46
|
+
| `GET /api/v1/admin/quote-requests` | admin | List, scoped by sales-rep assignment + filterable by status / organization. |
|
|
47
|
+
| `GET /api/v1/admin/quote-requests/:id` | admin | Detail with full actor identity in the event log. |
|
|
48
|
+
| `POST /api/v1/admin/quote-requests` | admin | Create on customer's behalf → status `Created from admin`. |
|
|
49
|
+
| `PATCH /api/v1/admin/quote-requests/:id` | admin | Modify a Pending or Created from admin RFQ → triggers customer re-acceptance. |
|
|
50
|
+
| `POST /api/v1/admin/quote-requests/:id/approve` | admin | Approve a Pending RFQ. |
|
|
51
|
+
| `POST /api/v1/admin/quote-requests/:id/cancel` | admin | Cancel with optional reason. |
|
|
52
|
+
| `POST /api/v1/admin/quote-requests/:id/assign` | admin | Set `assignedAdminUserId` (informational). |
|
|
53
|
+
| `GET /api/v1/admin/sales-reps/:adminUserId/organizations` | admin | Reverse view — organizations a rep is responsible for, with the count of open quote requests in each. |
|
|
54
|
+
| `GET /api/v1/storefront/settings/quote-requests` | public | Returns the two storefront visibility flags. |
|
|
55
|
+
|
|
56
|
+
The three endpoints that assign a sales representative *to* an organization —
|
|
57
|
+
`GET`, `POST` and `DELETE` under
|
|
58
|
+
`/api/v1/admin/organizations/:id/sales-reps` — belong to the **organizations**
|
|
59
|
+
module and are gated by `organizations:assign-sales-rep`, not by `rfqs:handle`.
|
|
60
|
+
They used to be registered here, and the split is not cosmetic: assigning a
|
|
61
|
+
representative qualifies an organization, so it must keep working when quote
|
|
62
|
+
requests is switched off, and it cannot be gated by a permission code declared
|
|
63
|
+
by a module that can disappear. The one endpoint left above is the one that
|
|
64
|
+
reads a quote request, and it is gated `rfqs:handle` precisely so that it
|
|
65
|
+
disappears with this module.
|
|
66
|
+
|
|
67
|
+
## Visibility model
|
|
68
|
+
|
|
69
|
+
A sales representative is a platform administrator with the
|
|
70
|
+
`sales_representative` role. The
|
|
71
|
+
`SalesRepAssignmentService.canSeeOrganization(adminUserId, organizationId)`
|
|
72
|
+
predicate centralises the visibility rule:
|
|
73
|
+
|
|
74
|
+
1. `platform_admin` role → sees every organization.
|
|
75
|
+
2. Otherwise the admin sees an organization iff a row in
|
|
76
|
+
`organization_sales_rep_assignments` ties them together, OR the
|
|
77
|
+
organization has zero rows in that table (the "unassigned-org
|
|
78
|
+
fallback" — visible to every sales rep).
|
|
79
|
+
|
|
80
|
+
A customer with the `org_admin` role on their own organization sees
|
|
81
|
+
every Quote Request in the organization, not just their own.
|
|
82
|
+
|
|
83
|
+
## Settings
|
|
84
|
+
|
|
85
|
+
Three settings drive the module — all live under the `quote_requests`
|
|
86
|
+
group and are configured through the existing settings module.
|
|
87
|
+
|
|
88
|
+
| Code | Type | Default | Effect |
|
|
89
|
+
| --- | --- | --- | --- |
|
|
90
|
+
| `quote_requests.expiry_days` | integer | `0` | Auto-expire Pending / Created from admin RFQs after N days. `0` disables. |
|
|
91
|
+
| `quote_requests.show_add_to_quote_on_card` | boolean | `true` | Toggle the "Add to quote" button on storefront product cards. |
|
|
92
|
+
| `quote_requests.show_add_to_quote_on_pdp` | boolean | `true` | Toggle the "Add to quote" button on product detail pages. |
|
|
93
|
+
|
|
94
|
+
## Background jobs
|
|
95
|
+
|
|
96
|
+
`RfqExpiryWorker.sweep()` runs every 30 minutes via the foundation
|
|
97
|
+
BullMQ scheduler. It reads `quote_requests.expiry_days` from the
|
|
98
|
+
settings module's resolved snapshot; if that value is 0 the sweep is
|
|
99
|
+
a no-op. Otherwise it transitions every Pending and Created from
|
|
100
|
+
admin row whose `updated_at < now() - INTERVAL <expiryDays> days` to
|
|
101
|
+
`Expired`, writes one `expired` event per row, and fans out
|
|
102
|
+
notifications to both parties.
|
|
103
|
+
|
|
104
|
+
## Data model
|
|
105
|
+
|
|
106
|
+
Three tables on top of the foundation `quote_requests` and
|
|
107
|
+
`quote_request_items`:
|
|
108
|
+
|
|
109
|
+
- `quote_request_revisions` — full snapshot per modify event.
|
|
110
|
+
- `quote_request_events` — append-only history (one row per state
|
|
111
|
+
transition or modification, with discriminated-union payload).
|
|
112
|
+
- `quote_request_notification_events` — one row per recipient ×
|
|
113
|
+
channel; unique on `(quote_request_id, source_event_id, recipient*,
|
|
114
|
+
channel)` so retries are idempotent.
|
|
115
|
+
|
|
116
|
+
`organization_sales_rep_assignments` — the m:n relation between
|
|
117
|
+
organizations and admin users that the visibility model above reads —
|
|
118
|
+
is **not** one of them: it is owned by the `organizations` module,
|
|
119
|
+
which qualifies the organization with it, and this module reaches it
|
|
120
|
+
through that module's `organizationSalesRepScopePort`.
|
|
121
|
+
|
|
122
|
+
The canonical `quote_requests` row carries the current state plus
|
|
123
|
+
`current_revision_number`, `last_customer_seen_revision_number`, and
|
|
124
|
+
`awaiting_customer_revision_acceptance`. The customer's "what changed
|
|
125
|
+
since I last visited" diff is computed on read by comparing the
|
|
126
|
+
revision identified by `last_customer_seen_revision_number` against
|
|
127
|
+
the revision identified by `current_revision_number`.
|
|
128
|
+
|
|
129
|
+
### `sales_channel_id` is nullable, and stays nullable
|
|
130
|
+
|
|
131
|
+
Every request records the sales channel it was raised on, taken from
|
|
132
|
+
the resolved request channel. The column is nullable and will remain
|
|
133
|
+
so.
|
|
134
|
+
|
|
135
|
+
It was added nullable on purpose: the deploy that shipped it must not
|
|
136
|
+
depend on the boot-time default-channel reconciler having already run,
|
|
137
|
+
which is the ordinary phased shape — add nullable, start writing,
|
|
138
|
+
backfill, flip to `NOT NULL`. The middle step is the one that cannot
|
|
139
|
+
be taken here. Nothing wrote the column between the migration that
|
|
140
|
+
added it and the change that started populating it, so every request
|
|
141
|
+
raised in that window carries `null`, and no record anywhere says
|
|
142
|
+
which channel it came from. Projecting the system-default channel over
|
|
143
|
+
that gap would not recover an attribution, it would invent one — and
|
|
144
|
+
the sales-channel delete guard would then start refusing deletions on
|
|
145
|
+
evidence the platform made up.
|
|
146
|
+
|
|
147
|
+
A deployment that genuinely needs the column non-nullable therefore
|
|
148
|
+
deletes the null tail, or takes a per-row answer from a source that
|
|
149
|
+
knows one. There is no backfill script, and there deliberately never
|
|
150
|
+
will be one.
|
|
151
|
+
|
|
152
|
+
## Notifications
|
|
153
|
+
|
|
154
|
+
Every state transition fans out through `RfqNotificationService` to
|
|
155
|
+
the appropriate recipients (customer for admin-side actions, sales
|
|
156
|
+
reps + platform admins for customer-side actions, both parties on
|
|
157
|
+
expiry). Email and in-account channels both fire. The unique
|
|
158
|
+
constraint on `quote_request_notification_events` guarantees once-only
|
|
159
|
+
delivery per (transition, recipient, channel).
|
|
160
|
+
|
|
161
|
+
## Conversion to order
|
|
162
|
+
|
|
163
|
+
When an order is created with a populated `source_quote_request_id`,
|
|
164
|
+
an event subscriber inside the module flips the originating Quote
|
|
165
|
+
Request to `Completed`, populates `converted_order_id`, and fires the
|
|
166
|
+
`completed` notification. The cart-creation step that locks RFQ
|
|
167
|
+
agreed prices into a checkout cart is delivered through the existing
|
|
168
|
+
cart and checkout flows.
|
package/i18n/en.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"actions.openInbox.label": "Open RFQ inbox",
|
|
3
|
+
"actions.openInbox.description": "Review pending quote requests",
|
|
4
|
+
"errors.RFQ_NOT_DRAFT": "Rfq Not Draft.",
|
|
5
|
+
"errors.RFQ_EMPTY": "Rfq Empty.",
|
|
6
|
+
"errors.RFQ_NOT_QUOTED": "Rfq Not Quoted.",
|
|
7
|
+
"errors.RFQ_NOT_ACCEPTED": "Rfq Not Accepted.",
|
|
8
|
+
"errors.RFQ_NOT_NEW": "Rfq Not New.",
|
|
9
|
+
"errors.RFQ_ALREADY_CLAIMED": "Rfq Already Claimed.",
|
|
10
|
+
"errors.RFQ_EXPIRED": "This quote is no longer valid — its validity period has ended. Ask your sales representative for a new quote, or submit the request again.",
|
|
11
|
+
"errors.QUOTE_INCOMPLETE": "Quote Incomplete.",
|
|
12
|
+
"errors.QUOTE_VALIDITY_ENDED": "The accepted quote is no longer valid — its validity period has ended. Ask your sales representative for a new quote before ordering.",
|
|
13
|
+
"nav.quoteRequests.label": "Quote requests"
|
|
14
|
+
}
|
package/i18n/pl.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"actions.openInbox.label": "Otwórz skrzynkę zapytań",
|
|
3
|
+
"actions.openInbox.description": "Przejrzyj oczekujące zapytania ofertowe",
|
|
4
|
+
"errors.RFQ_NOT_DRAFT": "Błąd: rfq not draft.",
|
|
5
|
+
"errors.RFQ_EMPTY": "Błąd: rfq empty.",
|
|
6
|
+
"errors.RFQ_NOT_QUOTED": "Błąd: rfq not quoted.",
|
|
7
|
+
"errors.RFQ_NOT_ACCEPTED": "Błąd: rfq not accepted.",
|
|
8
|
+
"errors.RFQ_NOT_NEW": "Błąd: rfq not new.",
|
|
9
|
+
"errors.RFQ_ALREADY_CLAIMED": "Błąd: rfq already claimed.",
|
|
10
|
+
"errors.RFQ_EXPIRED": "Ta oferta straciła ważność — termin jej ważności upłynął. Poproś opiekuna handlowego o nową ofertę lub złóż zapytanie ponownie.",
|
|
11
|
+
"errors.QUOTE_INCOMPLETE": "Błąd: quote incomplete.",
|
|
12
|
+
"errors.QUOTE_VALIDITY_ENDED": "Zaakceptowana oferta straciła ważność — termin jej ważności upłynął. Przed złożeniem zamówienia poproś opiekuna handlowego o nową ofertę.",
|
|
13
|
+
"nav.quoteRequests.label": "Zapytania ofertowe"
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@endora-commerce/mod-quote-requests",
|
|
3
|
+
"version": "0.100.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "Customer-initiated RFQ workflow with admin pricing, approvals, and expiry.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"endora": {
|
|
9
|
+
"type": "module",
|
|
10
|
+
"id": "quote_requests"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/endora-commerce/endora-commerce.git",
|
|
15
|
+
"directory": "packages/modules/quote_requests"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/manifest.d.ts",
|
|
23
|
+
"default": "./dist/manifest.js"
|
|
24
|
+
},
|
|
25
|
+
"./backend": {
|
|
26
|
+
"types": "./dist/backend/index.d.ts",
|
|
27
|
+
"default": "./dist/backend/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./migrations": {
|
|
30
|
+
"types": "./dist/migrations/index.d.ts",
|
|
31
|
+
"default": "./dist/migrations/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./admin": {
|
|
34
|
+
"types": "./dist/admin/index.d.ts",
|
|
35
|
+
"default": "./dist/admin/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./tailwind.css": "./tailwind.css",
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"i18n",
|
|
43
|
+
"docs",
|
|
44
|
+
"tailwind.css"
|
|
45
|
+
],
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22.18.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@mikro-orm/core": "^6",
|
|
51
|
+
"@mikro-orm/migrations": "^6",
|
|
52
|
+
"@mikro-orm/postgresql": "^6",
|
|
53
|
+
"fastify": "^5",
|
|
54
|
+
"lucide-react": "^1",
|
|
55
|
+
"react": "^19",
|
|
56
|
+
"react-router-dom": "^7",
|
|
57
|
+
"zod": "^4",
|
|
58
|
+
"@endora-commerce/admin-kit": "0.100.0",
|
|
59
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
60
|
+
"@endora-commerce/platform": "0.100.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@endora-commerce/admin-kit": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"lucide-react": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"react": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"react-router-dom": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@mikro-orm/core": "^6.6.13",
|
|
78
|
+
"@mikro-orm/migrations": "^6.6.13",
|
|
79
|
+
"@mikro-orm/postgresql": "^6.6.13",
|
|
80
|
+
"@types/node": "^22.9.0",
|
|
81
|
+
"@types/react": "^19.2.14",
|
|
82
|
+
"fastify": "^5.12.5",
|
|
83
|
+
"lucide-react": "^1.11.0",
|
|
84
|
+
"react": "^19.2.5",
|
|
85
|
+
"react-router-dom": "^7.18.2",
|
|
86
|
+
"typescript": "^5.9.3",
|
|
87
|
+
"vitest": "^4.1.11",
|
|
88
|
+
"zod": "^4.2.0",
|
|
89
|
+
"@endora-commerce/admin-kit": "0.100.0",
|
|
90
|
+
"@endora-commerce/platform": "0.100.0",
|
|
91
|
+
"@endora-commerce/contracts": "0.100.0"
|
|
92
|
+
},
|
|
93
|
+
"scripts": {
|
|
94
|
+
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.json",
|
|
95
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.ui.json --noEmit",
|
|
96
|
+
"lint": "eslint src",
|
|
97
|
+
"test": "vitest run"
|
|
98
|
+
}
|
|
99
|
+
}
|
package/tailwind.css
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* @endora-commerce/mod-quote-requests — AUTO-GENERATED by `pnpm --filter backend run manifests:generate`.
|
|
2
|
+
*
|
|
3
|
+
* The `@source` directives this package asks its host to scan
|
|
4
|
+
* (`specs/110-instance-repository/contracts/admin-stylesheet-composition.md` R1).
|
|
5
|
+
* They resolve relative to **this file**, so they hold wherever the package is
|
|
6
|
+
* installed — a workspace link here, `node_modules` in a client's instance.
|
|
7
|
+
*
|
|
8
|
+
* The `dist` line is what a published tarball ships and is what an instance
|
|
9
|
+
* scans; the `src` line is inert there and is what keeps `pnpm --filter admin
|
|
10
|
+
* run dev` reading source in this repository. Do not edit: run
|
|
11
|
+
* `pnpm --filter backend run manifests:generate`.
|
|
12
|
+
*/
|
|
13
|
+
@source "./dist/admin";
|
|
14
|
+
@source "./src/admin";
|