@endora-commerce/mod-shopping-lists 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 (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/backend/entities/shopping-list-item.entity.d.ts +20 -0
  4. package/dist/backend/entities/shopping-list-item.entity.d.ts.map +1 -0
  5. package/dist/backend/entities/shopping-list-item.entity.js +70 -0
  6. package/dist/backend/entities/shopping-list-item.entity.js.map +1 -0
  7. package/dist/backend/entities/shopping-list.entity.d.ts +22 -0
  8. package/dist/backend/entities/shopping-list.entity.d.ts.map +1 -0
  9. package/dist/backend/entities/shopping-list.entity.js +68 -0
  10. package/dist/backend/entities/shopping-list.entity.js.map +1 -0
  11. package/dist/backend/index.d.ts +87 -0
  12. package/dist/backend/index.d.ts.map +1 -0
  13. package/dist/backend/index.js +74 -0
  14. package/dist/backend/index.js.map +1 -0
  15. package/dist/backend/plugin.d.ts +42 -0
  16. package/dist/backend/plugin.d.ts.map +1 -0
  17. package/dist/backend/plugin.js +15 -0
  18. package/dist/backend/plugin.js.map +1 -0
  19. package/dist/backend/routes.d.ts +19 -0
  20. package/dist/backend/routes.d.ts.map +1 -0
  21. package/dist/backend/routes.js +136 -0
  22. package/dist/backend/routes.js.map +1 -0
  23. package/dist/backend/services/shopping-list-service.d.ts +134 -0
  24. package/dist/backend/services/shopping-list-service.d.ts.map +1 -0
  25. package/dist/backend/services/shopping-list-service.js +331 -0
  26. package/dist/backend/services/shopping-list-service.js.map +1 -0
  27. package/dist/manifest.d.ts +191 -0
  28. package/dist/manifest.d.ts.map +1 -0
  29. package/dist/manifest.js +105 -0
  30. package/dist/manifest.js.map +1 -0
  31. package/dist/migrations/20260426T135443_shopping_lists_init.d.ts +18 -0
  32. package/dist/migrations/20260426T135443_shopping_lists_init.d.ts.map +1 -0
  33. package/dist/migrations/20260426T135443_shopping_lists_init.js +52 -0
  34. package/dist/migrations/20260426T135443_shopping_lists_init.js.map +1 -0
  35. package/dist/migrations/20260611T140418_shopping_lists_default.d.ts +16 -0
  36. package/dist/migrations/20260611T140418_shopping_lists_default.d.ts.map +1 -0
  37. package/dist/migrations/20260611T140418_shopping_lists_default.js +31 -0
  38. package/dist/migrations/20260611T140418_shopping_lists_default.js.map +1 -0
  39. package/dist/migrations/index.d.ts +28 -0
  40. package/dist/migrations/index.d.ts.map +1 -0
  41. package/dist/migrations/index.js +31 -0
  42. package/dist/migrations/index.js.map +1 -0
  43. package/docs/shopping_lists.md +70 -0
  44. package/i18n/en.json +4 -0
  45. package/i18n/pl.json +4 -0
  46. package/package.json +66 -0
@@ -0,0 +1,191 @@
1
+ export declare const shoppingListsSettingsManifest: {
2
+ moduleCode: string;
3
+ groups: {
4
+ code: string;
5
+ name: string;
6
+ salesChannelCodes?: string[] | undefined;
7
+ isSystemProtected?: boolean | undefined;
8
+ }[];
9
+ settings: {
10
+ code: string;
11
+ name: string;
12
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
13
+ defaultValue: unknown;
14
+ description?: string | undefined;
15
+ groupCode?: string | undefined;
16
+ previousDefaultValues?: unknown[] | undefined;
17
+ salesChannelCodes?: string[] | undefined;
18
+ enumOptions?: string[] | undefined;
19
+ configurationType?: string | undefined;
20
+ hidden?: boolean | undefined;
21
+ }[];
22
+ };
23
+ /**
24
+ * Shopping Lists module — manifest backfill (Module Lifecycle, feature 018).
25
+ *
26
+ * Predates the lifecycle system; this manifest is the static record
27
+ * required so the module participates in the registry. No install /
28
+ * uninstall hook today — the module's schema is owned by earlier
29
+ * platform-wide migrations.
30
+ */
31
+ export declare const manifest: {
32
+ id: string;
33
+ name: string;
34
+ version: string;
35
+ dependencies: string[];
36
+ description?: string | undefined;
37
+ acknowledgedDependencies?: {
38
+ moduleId: string;
39
+ port: string;
40
+ reason: string;
41
+ }[] | undefined;
42
+ nonBindingDependencies?: {
43
+ moduleId: string;
44
+ name: string;
45
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
46
+ reason: string;
47
+ whenAbsent?: string | undefined;
48
+ }[] | undefined;
49
+ activation?: {
50
+ settingCode: string;
51
+ default: boolean;
52
+ } | {
53
+ nonDeactivatable: true;
54
+ reason: string;
55
+ } | undefined;
56
+ settings?: {
57
+ moduleCode: string;
58
+ groups: {
59
+ code: string;
60
+ name: string;
61
+ salesChannelCodes?: string[] | undefined;
62
+ isSystemProtected?: boolean | undefined;
63
+ }[];
64
+ settings: {
65
+ code: string;
66
+ name: string;
67
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
68
+ defaultValue: unknown;
69
+ description?: string | undefined;
70
+ groupCode?: string | undefined;
71
+ previousDefaultValues?: unknown[] | undefined;
72
+ salesChannelCodes?: string[] | undefined;
73
+ enumOptions?: string[] | undefined;
74
+ configurationType?: string | undefined;
75
+ hidden?: boolean | undefined;
76
+ }[];
77
+ } | undefined;
78
+ i18n?: {
79
+ bundlesDir: string;
80
+ } | undefined;
81
+ docs?: false | {
82
+ dir: string;
83
+ } | undefined;
84
+ demo?: false | {
85
+ summary: string;
86
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
87
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
88
+ after?: readonly string[] | undefined;
89
+ package?: string | undefined;
90
+ } | undefined;
91
+ actions?: {
92
+ id: string;
93
+ labelKey: string;
94
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
95
+ targetRoute: string;
96
+ keywords: string[];
97
+ weight: number;
98
+ descriptionKey?: string | undefined;
99
+ requiredPermission?: string | undefined;
100
+ }[] | undefined;
101
+ permissions?: {
102
+ code: string;
103
+ label: string;
104
+ module?: string | undefined;
105
+ description?: string | undefined;
106
+ requires?: string[] | undefined;
107
+ }[] | undefined;
108
+ transactionalEmails?: {
109
+ code: string;
110
+ name: string;
111
+ variables: {
112
+ key: string;
113
+ label: string;
114
+ sampleValue?: string | undefined;
115
+ description?: string | undefined;
116
+ }[];
117
+ description?: string | undefined;
118
+ group?: string | undefined;
119
+ }[] | undefined;
120
+ capabilities?: string[] | undefined;
121
+ exclusiveCapabilities?: {
122
+ key: string;
123
+ errorCode: string;
124
+ }[] | undefined;
125
+ errorCodes?: {
126
+ code: string;
127
+ tokens?: string[] | undefined;
128
+ }[] | undefined;
129
+ blocks?: {
130
+ name: string;
131
+ labelKey: string;
132
+ category: string;
133
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
134
+ fields: Record<string, {
135
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
136
+ label?: string | undefined;
137
+ required?: boolean | undefined;
138
+ options?: {
139
+ label: string;
140
+ value: string | number;
141
+ }[] | undefined;
142
+ refKind?: string | undefined;
143
+ }>;
144
+ descriptionKey?: string | undefined;
145
+ defaultProps?: Record<string, unknown> | undefined;
146
+ responsiveFields?: string[] | undefined;
147
+ previewIcon?: string | undefined;
148
+ weight?: number | undefined;
149
+ }[] | undefined;
150
+ blockCategories?: {
151
+ key: string;
152
+ titleKey: string;
153
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
154
+ weight?: number | undefined;
155
+ visible?: boolean | undefined;
156
+ }[] | undefined;
157
+ env?: {
158
+ name: string;
159
+ describes: {
160
+ en: string;
161
+ pl: string;
162
+ };
163
+ requirement: {
164
+ kind: "required";
165
+ } | {
166
+ kind: "requiredWhen";
167
+ input: string;
168
+ equals: string;
169
+ } | {
170
+ kind: "optional";
171
+ without: {
172
+ en: string;
173
+ pl: string;
174
+ };
175
+ };
176
+ secret: boolean;
177
+ generable: boolean;
178
+ owner: {
179
+ kind: "platform";
180
+ } | {
181
+ kind: "application";
182
+ application: "admin" | "backend" | "storefront";
183
+ } | {
184
+ kind: "module";
185
+ moduleId: string;
186
+ };
187
+ consumers: ("admin" | "backend" | "storefront")[];
188
+ addressOf: "admin" | "backend" | "storefront" | null;
189
+ }[] | undefined;
190
+ };
191
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;CAcxC,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiFnB,CAAC"}
@@ -0,0 +1,105 @@
1
+ import { defineModuleManifest, defineModuleSettingsManifest } from '@endora-commerce/contracts';
2
+ export const shoppingListsSettingsManifest = defineModuleSettingsManifest({
3
+ moduleCode: 'shopping_lists',
4
+ groups: [{ code: 'shopping_lists', name: 'Shopping lists' }],
5
+ settings: [
6
+ {
7
+ code: 'shopping_lists.enabled',
8
+ name: 'Shopping lists enabled',
9
+ description: 'Switches customer shopping lists on or off: creating and sharing lists, saving a cart line to one, and converting a list into a quote request. Nothing is dropped — every list, its items and its sharing state stay in the database.',
10
+ groupCode: 'shopping_lists',
11
+ valueType: 'boolean',
12
+ defaultValue: true,
13
+ },
14
+ ],
15
+ });
16
+ /**
17
+ * Shopping Lists module — manifest backfill (Module Lifecycle, feature 018).
18
+ *
19
+ * Predates the lifecycle system; this manifest is the static record
20
+ * required so the module participates in the registry. No install /
21
+ * uninstall hook today — the module's schema is owned by earlier
22
+ * platform-wide migrations.
23
+ */
24
+ export const manifest = defineModuleManifest({
25
+ id: 'shopping_lists',
26
+ name: 'Shopping Lists',
27
+ description: 'Customer-owned saved shopping lists.',
28
+ version: '1.0.0',
29
+ // Feature 072 (T133) — the edges were always there; the conversion is what
30
+ // made them declarations.
31
+ //
32
+ // `carts` joins them in feature 075 Phase C. It was absent on the grounds
33
+ // that "the service handed back through `shoppingListServiceSink` points
34
+ // outward, and declaring the consumer would invert the direction" — true of
35
+ // the sink, and not of the other edge: `convertToCart` resolves `carts`'
36
+ // `cartWritePort` and calls it, which points inward. The check accepted it
37
+ // only through the transitive path `orders` -> `carts`, so the declaration
38
+ // was a property of a neighbour's manifest. No cycle: nothing in the tree
39
+ // depends on `shopping_lists`.
40
+ dependencies: [
41
+ 'auth',
42
+ 'carts',
43
+ 'catalog',
44
+ 'orders',
45
+ 'organizations',
46
+ 'quote_requests',
47
+ 'settings',
48
+ ],
49
+ settings: shoppingListsSettingsManifest,
50
+ // Feature 073 (Constitution XVII) — the operator's activation control. It
51
+ // covered the quick-order surfaces too while this module hosted them; T139
52
+ // separated the two, so this now switches shopping lists and nothing else.
53
+ activation: { settingCode: 'shopping_lists.enabled', default: true },
54
+ /**
55
+ * This module's first i18n bundle — D-129's remaining sweep, MR 3.
56
+ *
57
+ * It ships two keys and nothing else, and it exists because a **declaring**
58
+ * module that contributes no bundle file is `check:error-translations` exit
59
+ * 2 for the whole tree rather than a finding
60
+ * (`specs/090-module-owned-error-codes/contracts/error-translation-population.md`
61
+ * §2.1, §2.3; `d129-sweep.md` §3.4). The bundle lives at the **package
62
+ * root**, not under `dist`: `manifest-locations.ts` resolves a packaged
63
+ * module's `manifestPath` to its `package.json`, so `dirname` is the package
64
+ * directory. The files are a **flat** `{"a.b.c": "text"}` map, because a
65
+ * nested object fails `TranslationBundleEntriesSchema` and the boot
66
+ * reconciler logs and skips it — silently.
67
+ */
68
+ i18n: { bundlesDir: 'i18n' },
69
+ docs: { dir: 'docs' },
70
+ /**
71
+ * The `SHOPPING_LIST_*` codes — D-129's remaining sweep, Tier A
72
+ * (`specs/090-module-owned-error-codes/d129-sweep.md` §5.2, Appendix A;
73
+ * MR 3).
74
+ *
75
+ * Both were declared by `_i18n` until this merge request, not because
76
+ * anybody judged them the platform's but because the deleted prefix chain
77
+ * had no rule for them and its last line was `return 'core'`. **D-121 T1
78
+ * puts them here**: the noun in both is a shopping list, which is this
79
+ * module's own entity, and both refusals are invariants of it — the default
80
+ * list is the customer's permanent anchor and cannot be deleted, and an
81
+ * account always keeps at least one list. This module is also the only one
82
+ * that raises them, from `services/shopping-list-service.ts`.
83
+ *
84
+ * **Unlike the rest of Tier A, these two arrive with a sentence.** They had
85
+ * none in either language anywhere in the tree and were on
86
+ * `UNTRANSLATED_ERROR_CODES` under `_i18n`; §5.4's default for a receiver
87
+ * with nothing to put in its new bundle is an empty `{}` plus a ledger entry
88
+ * under the receiver's group, and it says in terms that writing the prose
89
+ * instead is always available and is not a re-opening of D-186 §2 — that
90
+ * ruling refuses *carrying* a placeholder, not *writing* a sentence. Two
91
+ * codes is four strings, both refusals are read by a **buyer** on the
92
+ * storefront rather than by an operator, and each sentence written deletes a
93
+ * ledger entry instead of adding one. So they are written, and this module's
94
+ * bundle is not an empty one.
95
+ *
96
+ * **`tokens` is derived from the raise sites, not from the bundle**
97
+ * (runbook §5), and there are none: both raises are a bare
98
+ * `HttpError(409, code, message)` with no `details`.
99
+ */
100
+ errorCodes: [
101
+ { code: 'SHOPPING_LIST_CANNOT_DELETE_DEFAULT' },
102
+ { code: 'SHOPPING_LIST_CANNOT_DELETE_LAST' },
103
+ ],
104
+ });
105
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAEhG,MAAM,CAAC,MAAM,6BAA6B,GAAG,4BAA4B,CAAC;IACxE,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAC5D,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,wBAAwB;YAC9B,IAAI,EAAE,wBAAwB;YAC9B,WAAW,EACT,uOAAuO;YACzO,SAAS,EAAE,gBAAgB;YAC3B,SAAS,EAAE,SAAS;YACpB,YAAY,EAAE,IAAI;SACnB;KACF;CACF,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,gBAAgB;IACpB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,sCAAsC;IACxC,OAAO,EAAE,OAAO;IAChB,2EAA2E;IAC3E,0BAA0B;IAC1B,EAAE;IACF,0EAA0E;IAC1E,yEAAyE;IACzE,4EAA4E;IAC5E,yEAAyE;IACzE,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,+BAA+B;IAC/B,YAAY,EAAE;QACZ,MAAM;QACN,OAAO;QACP,SAAS;QACT,QAAQ;QACR,eAAe;QACf,gBAAgB;QAChB,UAAU;KACX;IACD,QAAQ,EAAE,6BAA6B;IACvC,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,UAAU,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,OAAO,EAAE,IAAI,EAAE;IACpE;;;;;;;;;;;;;OAaG;IACH,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IAC5B,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,UAAU,EAAE;QACV,EAAE,IAAI,EAAE,qCAAqC,EAAE;QAC/C,EAAE,IAAI,EAAE,kCAAkC,EAAE;KAC7C;CACF,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * shopping_lists module — Phase 5 (T200).
4
+ *
5
+ * Schema:
6
+ * - shopping_lists: scoped by (organization_id, customer_account_id) for
7
+ * owner queries. No FK constraint on customer_account_id /
8
+ * organization_id since those tables already exist; we keep the same
9
+ * "plain uuid columns" pattern the rest of the codebase uses.
10
+ * - shopping_list_items: cascade delete on the parent list, plain uuid
11
+ * FK to products so product archival doesn't drop list rows (the
12
+ * conversion service surfaces archived rows in a skip report).
13
+ */
14
+ export declare class Migration20260426T135443ShoppingListsInit extends Migration {
15
+ up(): Promise<void>;
16
+ down(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=20260426T135443_shopping_lists_init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260426T135443_shopping_lists_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260426T135443_shopping_lists_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;GAWG;AACH,qBAAa,yCAA0C,SAAQ,SAAS;IACvD,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAIrC"}
@@ -0,0 +1,52 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * shopping_lists module — Phase 5 (T200).
4
+ *
5
+ * Schema:
6
+ * - shopping_lists: scoped by (organization_id, customer_account_id) for
7
+ * owner queries. No FK constraint on customer_account_id /
8
+ * organization_id since those tables already exist; we keep the same
9
+ * "plain uuid columns" pattern the rest of the codebase uses.
10
+ * - shopping_list_items: cascade delete on the parent list, plain uuid
11
+ * FK to products so product archival doesn't drop list rows (the
12
+ * conversion service surfaces archived rows in a skip report).
13
+ */
14
+ export class Migration20260426T135443ShoppingListsInit extends Migration {
15
+ async up() {
16
+ this.addSql(`
17
+ create table "shopping_lists" (
18
+ "id" uuid not null,
19
+ "organization_id" uuid not null,
20
+ "customer_account_id" uuid not null,
21
+ "name" varchar(160) not null,
22
+ "created_at" timestamptz not null,
23
+ "updated_at" timestamptz not null,
24
+ constraint "shopping_lists_pkey" primary key ("id")
25
+ );
26
+ `);
27
+ this.addSql('create index "shopping_lists_organization_id_index" on "shopping_lists" ("organization_id");');
28
+ this.addSql('create index "shopping_lists_customer_account_id_index" on "shopping_lists" ("customer_account_id");');
29
+ this.addSql(`
30
+ create table "shopping_list_items" (
31
+ "id" uuid not null,
32
+ "shopping_list_id" uuid not null,
33
+ "product_id" uuid not null,
34
+ "variant_id" uuid null,
35
+ "quantity" int not null,
36
+ "note" text null,
37
+ "created_at" timestamptz not null,
38
+ "updated_at" timestamptz not null,
39
+ constraint "shopping_list_items_pkey" primary key ("id")
40
+ );
41
+ `);
42
+ this.addSql('create index "shopping_list_items_shopping_list_id_index" on "shopping_list_items" ("shopping_list_id");');
43
+ this.addSql('create index "shopping_list_items_product_id_index" on "shopping_list_items" ("product_id");');
44
+ this.addSql(`alter table "shopping_list_items" add constraint "shopping_list_items_list_fk"
45
+ foreign key ("shopping_list_id") references "shopping_lists" ("id") on delete cascade;`);
46
+ }
47
+ async down() {
48
+ this.addSql('drop table if exists "shopping_list_items" cascade;');
49
+ this.addSql('drop table if exists "shopping_lists" cascade;');
50
+ }
51
+ }
52
+ //# sourceMappingURL=20260426T135443_shopping_lists_init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260426T135443_shopping_lists_init.js","sourceRoot":"","sources":["../../src/migrations/20260426T135443_shopping_lists_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,yCAA0C,SAAQ,SAAS;IAC7D,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;KAUX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,8FAA8F,CAC/F,CAAC;QACF,IAAI,CAAC,MAAM,CACT,sGAAsG,CACvG,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;KAYX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,0GAA0G,CAC3G,CAAC;QACF,IAAI,CAAC,MAAM,CACT,8FAA8F,CAC/F,CAAC;QACF,IAAI,CAAC,MAAM,CACT;+FACyF,CAC1F,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,qDAAqD,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Default shopping list.
4
+ *
5
+ * Adds `shopping_lists.is_default`: at most one list per (organization,
6
+ * customer) is the customer's default — the target of the storefront
7
+ * "add to shopping list" affordances. Existing customers with at least one
8
+ * list have their earliest-created list promoted to default by the backfill;
9
+ * customers with no list get a "Default" list lazily on first access
10
+ * (ShoppingListService.ensureDefault).
11
+ */
12
+ export declare class Migration20260611T140418ShoppingListsDefault extends Migration {
13
+ up(): Promise<void>;
14
+ down(): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=20260611T140418_shopping_lists_default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260611T140418_shopping_lists_default.d.ts","sourceRoot":"","sources":["../../src/migrations/20260611T140418_shopping_lists_default.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;GASG;AACH,qBAAa,4CAA6C,SAAQ,SAAS;IAC1D,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
@@ -0,0 +1,31 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Default shopping list.
4
+ *
5
+ * Adds `shopping_lists.is_default`: at most one list per (organization,
6
+ * customer) is the customer's default — the target of the storefront
7
+ * "add to shopping list" affordances. Existing customers with at least one
8
+ * list have their earliest-created list promoted to default by the backfill;
9
+ * customers with no list get a "Default" list lazily on first access
10
+ * (ShoppingListService.ensureDefault).
11
+ */
12
+ export class Migration20260611T140418ShoppingListsDefault extends Migration {
13
+ async up() {
14
+ this.addSql(`alter table "shopping_lists" add column "is_default" boolean not null default false;`);
15
+ // Promote the earliest-created list per (organization, customer) to default.
16
+ this.addSql(`
17
+ update "shopping_lists" s set "is_default" = true
18
+ where s."id" = (
19
+ select s2."id" from "shopping_lists" s2
20
+ where s2."organization_id" = s."organization_id"
21
+ and s2."customer_account_id" = s."customer_account_id"
22
+ order by s2."created_at" asc, s2."id" asc
23
+ limit 1
24
+ );
25
+ `);
26
+ }
27
+ async down() {
28
+ this.addSql(`alter table "shopping_lists" drop column "is_default";`);
29
+ }
30
+ }
31
+ //# sourceMappingURL=20260611T140418_shopping_lists_default.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260611T140418_shopping_lists_default.js","sourceRoot":"","sources":["../../src/migrations/20260611T140418_shopping_lists_default.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,OAAO,4CAA6C,SAAQ,SAAS;IAChE,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,sFAAsF,CACvF,CAAC;QACF,6EAA6E;QAC7E,IAAI,CAAC,MAAM,CAAC;;;;;;;;;KASX,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,wDAAwD,CAAC,CAAC;IACxE,CAAC;CACF"}
@@ -0,0 +1,28 @@
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 (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260426T135443ShoppingListsInit } from './20260426T135443_shopping_lists_init.js';
25
+ import { Migration20260611T140418ShoppingListsDefault } from './20260611T140418_shopping_lists_default.js';
26
+ export declare const migrations: (typeof Migration20260426T135443ShoppingListsInit)[];
27
+ export { Migration20260426T135443ShoppingListsInit, Migration20260611T140418ShoppingListsDefault, };
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,EAAE,4CAA4C,EAAE,MAAM,6CAA6C,CAAC;AAE3G,eAAO,MAAM,UAAU,sDAGtB,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,4CAA4C,GAC7C,CAAC"}
@@ -0,0 +1,31 @@
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 (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260426T135443ShoppingListsInit } from './20260426T135443_shopping_lists_init.js';
25
+ import { Migration20260611T140418ShoppingListsDefault } from './20260611T140418_shopping_lists_default.js';
26
+ export const migrations = [
27
+ Migration20260426T135443ShoppingListsInit,
28
+ Migration20260611T140418ShoppingListsDefault,
29
+ ];
30
+ export { Migration20260426T135443ShoppingListsInit, Migration20260611T140418ShoppingListsDefault, };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AACrG,OAAO,EAAE,4CAA4C,EAAE,MAAM,6CAA6C,CAAC;AAE3G,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,yCAAyC;IACzC,4CAA4C;CAC7C,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,4CAA4C,GAC7C,CAAC"}
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: shopping_lists
3
+ description: Per-customer named bundles convertible to Cart or RFQ
4
+ ---
5
+
6
+ # `shopping_lists`
7
+
8
+ Per-customer named bundles of (product, variant?, quantity, note?) rows.
9
+ Buyers save recurring orders, then convert all or a selected subset of
10
+ items into a Cart or a draft Quote Request in a single click. Conversions
11
+ skip archived products and report them rather than failing the whole batch.
12
+
13
+ ## Public surface
14
+
15
+ All endpoints require an authenticated customer session and are scoped to
16
+ the `(customer, organization)` pair on the session.
17
+
18
+ | Verb + Path | Purpose |
19
+ | --- | --- |
20
+ | `GET /api/v1/shopping-lists` | List the buyer's lists with their items |
21
+ | `POST /api/v1/shopping-lists` | Create a new list |
22
+ | `GET /api/v1/shopping-lists/:id` | One list with its items |
23
+ | `PATCH /api/v1/shopping-lists/:id` | Rename |
24
+ | `DELETE /api/v1/shopping-lists/:id` | Delete (cascades to items) |
25
+ | `POST /api/v1/shopping-lists/:id/items` | Add an item |
26
+ | `PATCH /api/v1/shopping-lists/:id/items/:itemId` | Update quantity / note |
27
+ | `DELETE /api/v1/shopping-lists/:id/items/:itemId` | Remove |
28
+ | `POST /api/v1/shopping-lists/:id/convert-to-cart` | Add items to the active cart; archived rows are skipped + reported |
29
+ | `POST /api/v1/shopping-lists/:id/convert-to-rfq` | Add items to the draft RFQ; returns the `rfqId` for deep-linking |
30
+
31
+ The two converters accept an optional `itemIds: string[]` body. Empty or
32
+ missing array means "convert every item on the list".
33
+
34
+ ## Conversion semantics
35
+
36
+ `ShoppingListService` partitions the requested items by their referencing
37
+ product's status:
38
+
39
+ - `active` products go into the cart / RFQ via the existing
40
+ `CartService.addItem` / `RfqService.addItem` so cart aggregation and
41
+ RFQ draft version bumps stay in one place.
42
+ - `archived` products (or rows whose product no longer exists) are
43
+ reported in `skipped[]` with one of:
44
+ - `product_archived`
45
+ - `product_not_found`
46
+ - `variant_unavailable`
47
+
48
+ The HTTP response is always `200 OK` with `{ added, skipped }` so the UI
49
+ can show a friendly message rather than treating the partial result as
50
+ an error.
51
+
52
+ ## Entities
53
+
54
+ `ShoppingList` — `(organizationId, customerAccountId, name)`.
55
+
56
+ `ShoppingListItem` — cascade-deletes with its parent list. The FK to
57
+ `products` is intentionally a plain uuid column without a CASCADE so a
58
+ product archive doesn't drop historical rows; the conversion service
59
+ surfaces them in the skip report instead.
60
+
61
+ ## Extension points
62
+
63
+ - **Sharing within an organization** — the entity has no `isShared`
64
+ column today; adding org-wide sharing means a column flip plus a
65
+ one-line tweak to `#owned()` in `shopping-list-service.ts`.
66
+ - **Per-list deadlines** — extend `ShoppingList` with `expiresAt` for
67
+ procurement workflows that auto-archive stale lists.
68
+ - **Convert-to-quote-only-mode** — the converter already returns an RFQ
69
+ id; the storefront can deep-link the buyer into the draft to add a
70
+ requester note before submission.
package/i18n/en.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "errors.SHOPPING_LIST_CANNOT_DELETE_DEFAULT": "Your default shopping list is the one new items are saved to, so it cannot be deleted. Empty it if you no longer need what is on it, or make another list the default first — then this one can go.",
3
+ "errors.SHOPPING_LIST_CANNOT_DELETE_LAST": "This is your only shopping list, and an account always keeps at least one. Create another list first, and then this one can be deleted."
4
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "errors.SHOPPING_LIST_CANNOT_DELETE_DEFAULT": "Domyślna lista zakupowa to ta, na której zapisują się nowe produkty, dlatego nie można jej usunąć. Opróżnij ją, jeśli nie potrzebujesz już jej zawartości, albo najpierw ustaw jako domyślną inną listę — wtedy tę będzie można usunąć.",
3
+ "errors.SHOPPING_LIST_CANNOT_DELETE_LAST": "To Twoja jedyna lista zakupowa, a na koncie zawsze musi pozostać co najmniej jedna. Najpierw utwórz kolejną listę, a potem tę będzie można usunąć."
4
+ }