@endora-commerce/mod-price-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.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/admin/components/ApplicationRuleBuilder.d.ts +24 -0
- package/dist/admin/components/ApplicationRuleBuilder.d.ts.map +1 -0
- package/dist/admin/components/ApplicationRuleBuilder.js +249 -0
- package/dist/admin/components/ApplicationRuleBuilder.js.map +1 -0
- package/dist/admin/components/BracketGrid.d.ts +29 -0
- package/dist/admin/components/BracketGrid.d.ts.map +1 -0
- package/dist/admin/components/BracketGrid.js +252 -0
- package/dist/admin/components/BracketGrid.js.map +1 -0
- package/dist/admin/components/DisplayModeOverrideRow.d.ts +46 -0
- package/dist/admin/components/DisplayModeOverrideRow.d.ts.map +1 -0
- package/dist/admin/components/DisplayModeOverrideRow.js +109 -0
- package/dist/admin/components/DisplayModeOverrideRow.js.map +1 -0
- package/dist/admin/components/LinkedPriceListsPanel.d.ts +55 -0
- package/dist/admin/components/LinkedPriceListsPanel.d.ts.map +1 -0
- package/dist/admin/components/LinkedPriceListsPanel.js +131 -0
- package/dist/admin/components/LinkedPriceListsPanel.js.map +1 -0
- package/dist/admin/index.d.ts +34 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +119 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/DisplayModeOverridesPage.d.ts +25 -0
- package/dist/admin/pages/DisplayModeOverridesPage.d.ts.map +1 -0
- package/dist/admin/pages/DisplayModeOverridesPage.js +266 -0
- package/dist/admin/pages/DisplayModeOverridesPage.js.map +1 -0
- package/dist/admin/pages/PriceListDetailPage.d.ts +23 -0
- package/dist/admin/pages/PriceListDetailPage.d.ts.map +1 -0
- package/dist/admin/pages/PriceListDetailPage.js +421 -0
- package/dist/admin/pages/PriceListDetailPage.js.map +1 -0
- package/dist/admin/pages/PriceListsPage.d.ts +17 -0
- package/dist/admin/pages/PriceListsPage.d.ts.map +1 -0
- package/dist/admin/pages/PriceListsPage.js +198 -0
- package/dist/admin/pages/PriceListsPage.js.map +1 -0
- package/dist/admin/zones/CategoryDisplayMode.d.ts +27 -0
- package/dist/admin/zones/CategoryDisplayMode.d.ts.map +1 -0
- package/dist/admin/zones/CategoryDisplayMode.js +11 -0
- package/dist/admin/zones/CategoryDisplayMode.js.map +1 -0
- package/dist/admin/zones/OrganizationDisplayMode.d.ts +36 -0
- package/dist/admin/zones/OrganizationDisplayMode.d.ts.map +1 -0
- package/dist/admin/zones/OrganizationDisplayMode.js +12 -0
- package/dist/admin/zones/OrganizationDisplayMode.js.map +1 -0
- package/dist/admin/zones/ProductLinkedPriceLists.d.ts +16 -0
- package/dist/admin/zones/ProductLinkedPriceLists.d.ts.map +1 -0
- package/dist/admin/zones/ProductLinkedPriceLists.js +7 -0
- package/dist/admin/zones/ProductLinkedPriceLists.js.map +1 -0
- package/dist/backend/entities/price-display-mode-override.entity.d.ts +23 -0
- package/dist/backend/entities/price-display-mode-override.entity.d.ts.map +1 -0
- package/dist/backend/entities/price-display-mode-override.entity.js +58 -0
- package/dist/backend/entities/price-display-mode-override.entity.js.map +1 -0
- package/dist/backend/entities/price-list-price-bracket.entity.d.ts +26 -0
- package/dist/backend/entities/price-list-price-bracket.entity.d.ts.map +1 -0
- package/dist/backend/entities/price-list-price-bracket.entity.js +73 -0
- package/dist/backend/entities/price-list-price-bracket.entity.js.map +1 -0
- package/dist/backend/entities/price-list-product.entity.d.ts +15 -0
- package/dist/backend/entities/price-list-product.entity.d.ts.map +1 -0
- package/dist/backend/entities/price-list-product.entity.js +43 -0
- package/dist/backend/entities/price-list-product.entity.js.map +1 -0
- package/dist/backend/entities/price-list.entity.d.ts +34 -0
- package/dist/backend/entities/price-list.entity.d.ts.map +1 -0
- package/dist/backend/entities/price-list.entity.js +115 -0
- package/dist/backend/entities/price-list.entity.js.map +1 -0
- package/dist/backend/index.d.ts +125 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +179 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +68 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +88 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.d.ts +40 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +447 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +47 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +131 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/application-rule-evaluator.d.ts +25 -0
- package/dist/backend/services/application-rule-evaluator.d.ts.map +1 -0
- package/dist/backend/services/application-rule-evaluator.js +2 -0
- package/dist/backend/services/application-rule-evaluator.js.map +1 -0
- package/dist/backend/services/audit-references.d.ts +11 -0
- package/dist/backend/services/audit-references.d.ts.map +1 -0
- package/dist/backend/services/audit-references.js +23 -0
- package/dist/backend/services/audit-references.js.map +1 -0
- package/dist/backend/services/default-price-list-migration.d.ts +70 -0
- package/dist/backend/services/default-price-list-migration.d.ts.map +1 -0
- package/dist/backend/services/default-price-list-migration.js +177 -0
- package/dist/backend/services/default-price-list-migration.js.map +1 -0
- package/dist/backend/services/display-mode-resolver.d.ts +78 -0
- package/dist/backend/services/display-mode-resolver.d.ts.map +1 -0
- package/dist/backend/services/display-mode-resolver.js +71 -0
- package/dist/backend/services/display-mode-resolver.js.map +1 -0
- package/dist/backend/services/listing-price-chain.d.ts +26 -0
- package/dist/backend/services/listing-price-chain.d.ts.map +1 -0
- package/dist/backend/services/listing-price-chain.js +55 -0
- package/dist/backend/services/listing-price-chain.js.map +1 -0
- package/dist/backend/services/price-bracket-resolver.d.ts +24 -0
- package/dist/backend/services/price-bracket-resolver.d.ts.map +1 -0
- package/dist/backend/services/price-bracket-resolver.js +16 -0
- package/dist/backend/services/price-bracket-resolver.js.map +1 -0
- package/dist/backend/services/price-list-candidate-vector.d.ts +116 -0
- package/dist/backend/services/price-list-candidate-vector.d.ts.map +1 -0
- package/dist/backend/services/price-list-candidate-vector.js +133 -0
- package/dist/backend/services/price-list-candidate-vector.js.map +1 -0
- package/dist/backend/services/price-list-currency-reference.d.ts +12 -0
- package/dist/backend/services/price-list-currency-reference.d.ts.map +1 -0
- package/dist/backend/services/price-list-currency-reference.js +24 -0
- package/dist/backend/services/price-list-currency-reference.js.map +1 -0
- package/dist/backend/services/price-list-read-port.d.ts +29 -0
- package/dist/backend/services/price-list-read-port.d.ts.map +1 -0
- package/dist/backend/services/price-list-read-port.js +64 -0
- package/dist/backend/services/price-list-read-port.js.map +1 -0
- package/dist/backend/services/price-list-resolver.d.ts +26 -0
- package/dist/backend/services/price-list-resolver.d.ts.map +1 -0
- package/dist/backend/services/price-list-resolver.js +37 -0
- package/dist/backend/services/price-list-resolver.js.map +1 -0
- package/dist/backend/services/price-list-service.d.ts +431 -0
- package/dist/backend/services/price-list-service.d.ts.map +1 -0
- package/dist/backend/services/price-list-service.js +1238 -0
- package/dist/backend/services/price-list-service.js.map +1 -0
- package/dist/backend/services/price-list-status-worker.d.ts +28 -0
- package/dist/backend/services/price-list-status-worker.d.ts.map +1 -0
- package/dist/backend/services/price-list-status-worker.js +38 -0
- package/dist/backend/services/price-list-status-worker.js.map +1 -0
- package/dist/backend/services/pricing-cache.d.ts +53 -0
- package/dist/backend/services/pricing-cache.d.ts.map +1 -0
- package/dist/backend/services/pricing-cache.js +88 -0
- package/dist/backend/services/pricing-cache.js.map +1 -0
- package/dist/backend/services/pricing-service.d.ts +304 -0
- package/dist/backend/services/pricing-service.d.ts.map +1 -0
- package/dist/backend/services/pricing-service.interface.d.ts +151 -0
- package/dist/backend/services/pricing-service.interface.d.ts.map +1 -0
- package/dist/backend/services/pricing-service.interface.js +10 -0
- package/dist/backend/services/pricing-service.interface.js.map +1 -0
- package/dist/backend/services/pricing-service.js +803 -0
- package/dist/backend/services/pricing-service.js.map +1 -0
- package/dist/backend/services/unit-price-ordering.d.ts +121 -0
- package/dist/backend/services/unit-price-ordering.d.ts.map +1 -0
- package/dist/backend/services/unit-price-ordering.js +164 -0
- package/dist/backend/services/unit-price-ordering.js.map +1 -0
- package/dist/manifest.d.ts +218 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +228 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260426T075235_price_lists_pricing_init.d.ts +24 -0
- package/dist/migrations/20260426T075235_price_lists_pricing_init.d.ts.map +1 -0
- package/dist/migrations/20260426T075235_price_lists_pricing_init.js +89 -0
- package/dist/migrations/20260426T075235_price_lists_pricing_init.js.map +1 -0
- package/dist/migrations/20260504T125655_price_lists_engine.d.ts +6 -0
- package/dist/migrations/20260504T125655_price_lists_engine.d.ts.map +1 -0
- package/dist/migrations/20260504T125655_price_lists_engine.js +319 -0
- package/dist/migrations/20260504T125655_price_lists_engine.js.map +1 -0
- package/dist/migrations/20260817T055457_price_lists_single_system_price_list.d.ts +15 -0
- package/dist/migrations/20260817T055457_price_lists_single_system_price_list.d.ts.map +1 -0
- package/dist/migrations/20260817T055457_price_lists_single_system_price_list.js +58 -0
- package/dist/migrations/20260817T055457_price_lists_single_system_price_list.js.map +1 -0
- package/dist/migrations/20260821T135907_price_lists_unit_price_amount_index.d.ts +40 -0
- package/dist/migrations/20260821T135907_price_lists_unit_price_amount_index.d.ts.map +1 -0
- package/dist/migrations/20260821T135907_price_lists_unit_price_amount_index.js +46 -0
- package/dist/migrations/20260821T135907_price_lists_unit_price_amount_index.js.map +1 -0
- package/dist/migrations/index.d.ts +30 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +35 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/price_lists.md +278 -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,319 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Price Lists engine — feature 011 schema reshape (additive rollout).
|
|
4
|
+
*
|
|
5
|
+
* What this migration does, in order (data-model.md §7):
|
|
6
|
+
* 1. Acquire an advisory lock to coalesce concurrent migrators.
|
|
7
|
+
* 2. Add the engine columns to `price_lists`: type, status, starts_at,
|
|
8
|
+
* ends_at, modified_at, application_rule (JSONB), is_system. The legacy
|
|
9
|
+
* columns (code, currency, priority, is_default) remain in place — they
|
|
10
|
+
* will be dropped by a follow-up migration once every reader migrates to
|
|
11
|
+
* the engine resolver. Widen `name` from varchar(160) to varchar(200).
|
|
12
|
+
* 3. Create `price_list_products` (assignment join).
|
|
13
|
+
* 4. Create `price_list_price_brackets` (multi-currency multi-bracket per
|
|
14
|
+
* pair; service-layer overlap check, not a DB constraint — research §R3).
|
|
15
|
+
* 5. Create `price_display_mode_overrides` (Org/Cat/Product overrides).
|
|
16
|
+
* 6. Seed the `Default` price list with deterministic UUID
|
|
17
|
+
* `00000000-0000-4000-8000-00000000d51b`. Idempotent: ON CONFLICT
|
|
18
|
+
* (id) DO UPDATE keeps both the legacy AND engine columns coherent.
|
|
19
|
+
* 7. Migrate every Product's `attributeValues.defaultPrice` (with
|
|
20
|
+
* `attributeValues.price` as fallback) into one bracket row per
|
|
21
|
+
* currency exposed by any sales channel (research §R7). The legacy
|
|
22
|
+
* JSONB key is NOT stripped; existing readers continue to work.
|
|
23
|
+
*
|
|
24
|
+
* The migration is idempotent: re-running adds no rows, no columns are
|
|
25
|
+
* re-altered (PostgreSQL's "if not exists" guards every DDL), and the seed
|
|
26
|
+
* row's deterministic UUID prevents duplication.
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_PRICE_LIST_ID = '00000000-0000-4000-8000-00000000d51b';
|
|
29
|
+
const ADVISORY_LOCK_KEY = 11; // feature number, namespaced into pg_advisory_xact_lock.
|
|
30
|
+
export class Migration20260504T125655PriceListsEngine extends Migration {
|
|
31
|
+
async up() {
|
|
32
|
+
// ============================================================
|
|
33
|
+
// 1. Advisory lock — coalesce concurrent migrators
|
|
34
|
+
// ============================================================
|
|
35
|
+
this.addSql(`select pg_advisory_xact_lock(${ADVISORY_LOCK_KEY});`);
|
|
36
|
+
// ============================================================
|
|
37
|
+
// 2. price_lists — additive engine columns
|
|
38
|
+
// ============================================================
|
|
39
|
+
this.addSql(`alter table "price_lists" alter column "name" type varchar(200);`);
|
|
40
|
+
this.addSql(`
|
|
41
|
+
alter table "price_lists"
|
|
42
|
+
add column if not exists "type" varchar(8) not null default 'base',
|
|
43
|
+
add column if not exists "status" varchar(16) not null default 'draft',
|
|
44
|
+
add column if not exists "starts_at" timestamptz null,
|
|
45
|
+
add column if not exists "ends_at" timestamptz null,
|
|
46
|
+
add column if not exists "modified_at" timestamptz not null default now(),
|
|
47
|
+
add column if not exists "application_rule" jsonb not null default '{"kind":"all"}'::jsonb,
|
|
48
|
+
add column if not exists "is_system" boolean not null default false;
|
|
49
|
+
`);
|
|
50
|
+
this.addSql(`
|
|
51
|
+
alter table "price_lists"
|
|
52
|
+
drop constraint if exists "price_lists_type_check";
|
|
53
|
+
`);
|
|
54
|
+
this.addSql(`
|
|
55
|
+
alter table "price_lists"
|
|
56
|
+
add constraint "price_lists_type_check" check ("type" in ('base','sale'));
|
|
57
|
+
`);
|
|
58
|
+
this.addSql(`
|
|
59
|
+
alter table "price_lists"
|
|
60
|
+
drop constraint if exists "price_lists_status_check";
|
|
61
|
+
`);
|
|
62
|
+
this.addSql(`
|
|
63
|
+
alter table "price_lists"
|
|
64
|
+
add constraint "price_lists_status_check"
|
|
65
|
+
check ("status" in ('draft','active','scheduled','expired'));
|
|
66
|
+
`);
|
|
67
|
+
this.addSql(`
|
|
68
|
+
alter table "price_lists"
|
|
69
|
+
drop constraint if exists "price_lists_dates_check";
|
|
70
|
+
`);
|
|
71
|
+
this.addSql(`
|
|
72
|
+
alter table "price_lists"
|
|
73
|
+
add constraint "price_lists_dates_check"
|
|
74
|
+
check ("ends_at" is null or "starts_at" is null or "ends_at" > "starts_at");
|
|
75
|
+
`);
|
|
76
|
+
this.addSql(`create index if not exists "price_lists_status_idx" on "price_lists" ("status");`);
|
|
77
|
+
this.addSql(`create index if not exists "price_lists_type_idx" on "price_lists" ("type");`);
|
|
78
|
+
this.addSql(`create index if not exists "price_lists_modified_at_idx" on "price_lists" ("modified_at" desc);`);
|
|
79
|
+
this.addSql(`
|
|
80
|
+
create index if not exists "price_lists_status_starts_at_idx"
|
|
81
|
+
on "price_lists" ("starts_at")
|
|
82
|
+
where "status" = 'scheduled';
|
|
83
|
+
`);
|
|
84
|
+
this.addSql(`
|
|
85
|
+
create index if not exists "price_lists_status_ends_at_idx"
|
|
86
|
+
on "price_lists" ("ends_at")
|
|
87
|
+
where "status" = 'active' and "ends_at" is not null;
|
|
88
|
+
`);
|
|
89
|
+
// ============================================================
|
|
90
|
+
// 3. price_list_products — assignment join
|
|
91
|
+
// ============================================================
|
|
92
|
+
this.addSql(`
|
|
93
|
+
create table if not exists "price_list_products" (
|
|
94
|
+
"price_list_id" uuid not null,
|
|
95
|
+
"product_id" uuid not null,
|
|
96
|
+
"created_at" timestamptz not null default now(),
|
|
97
|
+
constraint "price_list_products_pkey" primary key ("price_list_id", "product_id"),
|
|
98
|
+
constraint "price_list_products_list_fk"
|
|
99
|
+
foreign key ("price_list_id") references "price_lists" ("id") on delete cascade,
|
|
100
|
+
constraint "price_list_products_product_fk"
|
|
101
|
+
foreign key ("product_id") references "products" ("id") on delete cascade
|
|
102
|
+
);
|
|
103
|
+
`);
|
|
104
|
+
this.addSql(`
|
|
105
|
+
create index if not exists "price_list_products_product_idx"
|
|
106
|
+
on "price_list_products" ("product_id");
|
|
107
|
+
`);
|
|
108
|
+
// ============================================================
|
|
109
|
+
// 4. price_list_price_brackets — multi-currency multi-bracket prices
|
|
110
|
+
// ============================================================
|
|
111
|
+
this.addSql(`
|
|
112
|
+
create table if not exists "price_list_price_brackets" (
|
|
113
|
+
"price_list_id" uuid not null,
|
|
114
|
+
"product_id" uuid not null,
|
|
115
|
+
"currency_code" varchar(3) not null,
|
|
116
|
+
"min_quantity" integer not null,
|
|
117
|
+
"max_quantity" integer null,
|
|
118
|
+
"amount" numeric(14, 4) not null,
|
|
119
|
+
"created_at" timestamptz not null default now(),
|
|
120
|
+
"updated_at" timestamptz not null default now(),
|
|
121
|
+
constraint "price_list_price_brackets_pkey"
|
|
122
|
+
primary key ("price_list_id", "product_id", "currency_code", "min_quantity"),
|
|
123
|
+
constraint "price_list_price_brackets_min_qty_check" check ("min_quantity" >= 1),
|
|
124
|
+
constraint "price_list_price_brackets_max_qty_check"
|
|
125
|
+
check ("max_quantity" is null or "max_quantity" >= "min_quantity"),
|
|
126
|
+
constraint "price_list_price_brackets_amount_check" check ("amount" >= 0),
|
|
127
|
+
constraint "price_list_price_brackets_assignment_fk"
|
|
128
|
+
foreign key ("price_list_id", "product_id")
|
|
129
|
+
references "price_list_products" ("price_list_id", "product_id")
|
|
130
|
+
on delete cascade
|
|
131
|
+
);
|
|
132
|
+
`);
|
|
133
|
+
// ============================================================
|
|
134
|
+
// 5. price_display_mode_overrides — Org/Cat/Product overrides
|
|
135
|
+
// ============================================================
|
|
136
|
+
this.addSql(`
|
|
137
|
+
create table if not exists "price_display_mode_overrides" (
|
|
138
|
+
"scope" varchar(16) not null,
|
|
139
|
+
"target_id" uuid not null,
|
|
140
|
+
"mode" varchar(16) not null,
|
|
141
|
+
"created_at" timestamptz not null default now(),
|
|
142
|
+
"updated_at" timestamptz not null default now(),
|
|
143
|
+
constraint "price_display_mode_overrides_pkey" primary key ("scope", "target_id"),
|
|
144
|
+
constraint "price_display_mode_overrides_scope_check"
|
|
145
|
+
check ("scope" in ('organization','category','product')),
|
|
146
|
+
constraint "price_display_mode_overrides_mode_check"
|
|
147
|
+
check ("mode" in ('gross_only','net_only','both','none'))
|
|
148
|
+
);
|
|
149
|
+
`);
|
|
150
|
+
// ============================================================
|
|
151
|
+
// 6. Seed Default price list (idempotent via deterministic UUID).
|
|
152
|
+
// If a different price_list already carries is_default=true (e.g. a
|
|
153
|
+
// pre-feature-011 dev seed produced one with a random UUID like
|
|
154
|
+
// `default_pln`), re-point its children onto the deterministic row and
|
|
155
|
+
// drop the legacy row. Without this, the partial unique index
|
|
156
|
+
// `uniq_price_lists_one_default` (created by migration 014) blocks the
|
|
157
|
+
// seed insert.
|
|
158
|
+
// ============================================================
|
|
159
|
+
this.addSql(`
|
|
160
|
+
do $$
|
|
161
|
+
declare
|
|
162
|
+
v_target_id uuid := '${DEFAULT_PRICE_LIST_ID}'::uuid;
|
|
163
|
+
v_legacy_id uuid;
|
|
164
|
+
begin
|
|
165
|
+
select "id" into v_legacy_id
|
|
166
|
+
from "price_lists"
|
|
167
|
+
where "is_default" = true and "id" <> v_target_id
|
|
168
|
+
limit 1;
|
|
169
|
+
|
|
170
|
+
if v_legacy_id is not null then
|
|
171
|
+
-- Release the partial unique index before inserting the deterministic row.
|
|
172
|
+
update "price_lists" set "is_default" = false where "id" = v_legacy_id;
|
|
173
|
+
end if;
|
|
174
|
+
|
|
175
|
+
insert into "price_lists" (
|
|
176
|
+
"id", "code", "name", "currency",
|
|
177
|
+
"is_default", "priority",
|
|
178
|
+
"type", "status", "modified_at",
|
|
179
|
+
"application_rule", "is_system",
|
|
180
|
+
"created_at", "updated_at"
|
|
181
|
+
)
|
|
182
|
+
values (
|
|
183
|
+
v_target_id, 'default', 'Default', 'PLN',
|
|
184
|
+
true, 0,
|
|
185
|
+
'base', 'active', now(),
|
|
186
|
+
'{"kind":"all"}'::jsonb, true,
|
|
187
|
+
now(), now()
|
|
188
|
+
)
|
|
189
|
+
on conflict ("id") do update set
|
|
190
|
+
"name" = excluded."name",
|
|
191
|
+
"type" = excluded."type",
|
|
192
|
+
"status" = excluded."status",
|
|
193
|
+
"is_default" = true,
|
|
194
|
+
"is_system" = excluded."is_system",
|
|
195
|
+
"application_rule" = excluded."application_rule";
|
|
196
|
+
|
|
197
|
+
if v_legacy_id is not null then
|
|
198
|
+
-- Re-point legacy-feature-014 children onto the deterministic row,
|
|
199
|
+
-- then delete the legacy row.
|
|
200
|
+
update "price_list_items"
|
|
201
|
+
set "price_list_id" = v_target_id
|
|
202
|
+
where "price_list_id" = v_legacy_id;
|
|
203
|
+
update "price_list_assignments"
|
|
204
|
+
set "price_list_id" = v_target_id
|
|
205
|
+
where "price_list_id" = v_legacy_id;
|
|
206
|
+
delete from "price_lists" where "id" = v_legacy_id;
|
|
207
|
+
end if;
|
|
208
|
+
end
|
|
209
|
+
$$;
|
|
210
|
+
`);
|
|
211
|
+
// ============================================================
|
|
212
|
+
// 7. Migrate legacy attributeValues.defaultPrice → bracket rows
|
|
213
|
+
// (one row per currency exposed by any sales channel; identity copy
|
|
214
|
+
// per Q3 / FR-002. Legacy keys are NOT stripped — see expand→migrate
|
|
215
|
+
// →contract note in Phase 2 of tasks.md.)
|
|
216
|
+
// ============================================================
|
|
217
|
+
this.addSql(`
|
|
218
|
+
with currencies as (
|
|
219
|
+
select distinct upper(c::text) as code
|
|
220
|
+
from sales_channels sc, lateral jsonb_array_elements_text(sc.currencies) as c
|
|
221
|
+
where length(c) = 3
|
|
222
|
+
),
|
|
223
|
+
legacy_priced as (
|
|
224
|
+
select
|
|
225
|
+
p."id" as product_id,
|
|
226
|
+
coalesce(
|
|
227
|
+
(p."attribute_values"->>'defaultPrice')::numeric,
|
|
228
|
+
(p."attribute_values"->>'price')::numeric
|
|
229
|
+
) as legacy_price
|
|
230
|
+
from "products" p
|
|
231
|
+
where (p."attribute_values" ? 'defaultPrice') or (p."attribute_values" ? 'price')
|
|
232
|
+
),
|
|
233
|
+
assignment_upserts as (
|
|
234
|
+
insert into "price_list_products" ("price_list_id", "product_id", "created_at")
|
|
235
|
+
select '${DEFAULT_PRICE_LIST_ID}'::uuid, lp.product_id, now()
|
|
236
|
+
from legacy_priced lp
|
|
237
|
+
on conflict ("price_list_id", "product_id") do nothing
|
|
238
|
+
returning 1
|
|
239
|
+
)
|
|
240
|
+
insert into "price_list_price_brackets" (
|
|
241
|
+
"price_list_id", "product_id", "currency_code",
|
|
242
|
+
"min_quantity", "max_quantity", "amount",
|
|
243
|
+
"created_at", "updated_at"
|
|
244
|
+
)
|
|
245
|
+
select
|
|
246
|
+
'${DEFAULT_PRICE_LIST_ID}'::uuid,
|
|
247
|
+
lp.product_id,
|
|
248
|
+
c.code,
|
|
249
|
+
1,
|
|
250
|
+
null,
|
|
251
|
+
lp.legacy_price,
|
|
252
|
+
now(),
|
|
253
|
+
now()
|
|
254
|
+
from legacy_priced lp
|
|
255
|
+
cross join currencies c
|
|
256
|
+
where lp.legacy_price is not null and lp.legacy_price >= 0
|
|
257
|
+
on conflict ("price_list_id", "product_id", "currency_code", "min_quantity") do nothing;
|
|
258
|
+
`);
|
|
259
|
+
// Fallback: deployments that have not yet seeded sales-channel currencies
|
|
260
|
+
// get a single PLN row per product so the seed at least covers the
|
|
261
|
+
// platform's default currency. Idempotent.
|
|
262
|
+
this.addSql(`
|
|
263
|
+
with legacy_priced as (
|
|
264
|
+
select
|
|
265
|
+
p."id" as product_id,
|
|
266
|
+
coalesce(
|
|
267
|
+
(p."attribute_values"->>'defaultPrice')::numeric,
|
|
268
|
+
(p."attribute_values"->>'price')::numeric
|
|
269
|
+
) as legacy_price
|
|
270
|
+
from "products" p
|
|
271
|
+
where (p."attribute_values" ? 'defaultPrice') or (p."attribute_values" ? 'price')
|
|
272
|
+
),
|
|
273
|
+
ensure_assignment as (
|
|
274
|
+
insert into "price_list_products" ("price_list_id", "product_id", "created_at")
|
|
275
|
+
select '${DEFAULT_PRICE_LIST_ID}'::uuid, product_id, now()
|
|
276
|
+
from legacy_priced
|
|
277
|
+
on conflict ("price_list_id", "product_id") do nothing
|
|
278
|
+
returning 1
|
|
279
|
+
)
|
|
280
|
+
insert into "price_list_price_brackets" (
|
|
281
|
+
"price_list_id", "product_id", "currency_code",
|
|
282
|
+
"min_quantity", "max_quantity", "amount",
|
|
283
|
+
"created_at", "updated_at"
|
|
284
|
+
)
|
|
285
|
+
select
|
|
286
|
+
'${DEFAULT_PRICE_LIST_ID}'::uuid, product_id, 'PLN', 1, null,
|
|
287
|
+
legacy_price, now(), now()
|
|
288
|
+
from legacy_priced
|
|
289
|
+
where legacy_price is not null and legacy_price >= 0
|
|
290
|
+
on conflict ("price_list_id", "product_id", "currency_code", "min_quantity") do nothing;
|
|
291
|
+
`);
|
|
292
|
+
}
|
|
293
|
+
async down() {
|
|
294
|
+
this.addSql(`drop table if exists "price_list_price_brackets" cascade;`);
|
|
295
|
+
this.addSql(`drop table if exists "price_list_products" cascade;`);
|
|
296
|
+
this.addSql(`drop table if exists "price_display_mode_overrides" cascade;`);
|
|
297
|
+
this.addSql(`drop index if exists "price_lists_status_idx";`);
|
|
298
|
+
this.addSql(`drop index if exists "price_lists_type_idx";`);
|
|
299
|
+
this.addSql(`drop index if exists "price_lists_modified_at_idx";`);
|
|
300
|
+
this.addSql(`drop index if exists "price_lists_status_starts_at_idx";`);
|
|
301
|
+
this.addSql(`drop index if exists "price_lists_status_ends_at_idx";`);
|
|
302
|
+
this.addSql(`alter table "price_lists" drop constraint if exists "price_lists_dates_check";`);
|
|
303
|
+
this.addSql(`alter table "price_lists" drop constraint if exists "price_lists_status_check";`);
|
|
304
|
+
this.addSql(`alter table "price_lists" drop constraint if exists "price_lists_type_check";`);
|
|
305
|
+
this.addSql(`
|
|
306
|
+
alter table "price_lists"
|
|
307
|
+
drop column if exists "is_system",
|
|
308
|
+
drop column if exists "application_rule",
|
|
309
|
+
drop column if exists "modified_at",
|
|
310
|
+
drop column if exists "ends_at",
|
|
311
|
+
drop column if exists "starts_at",
|
|
312
|
+
drop column if exists "status",
|
|
313
|
+
drop column if exists "type";
|
|
314
|
+
`);
|
|
315
|
+
// Note: the seeded Default row, if it survived, stays — drop logic
|
|
316
|
+
// is left to a future contract migration once 014's tables are dropped.
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=20260504T125655_price_lists_engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260504T125655_price_lists_engine.js","sourceRoot":"","sources":["../../src/migrations/20260504T125655_price_lists_engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/** The partial unique index this migration installs. */
|
|
3
|
+
export declare const SYSTEM_PRICE_LIST_INDEX = "price_lists_is_system_unique";
|
|
4
|
+
/**
|
|
5
|
+
* `up()` as data, so the demotion can be exercised against a database that has
|
|
6
|
+
* actually drifted — which a freshly migrated test database never has.
|
|
7
|
+
* `test/integration/price_lists/system-price-list-singleton.test.ts` replays
|
|
8
|
+
* these statements after re-creating the drift.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SINGLE_SYSTEM_PRICE_LIST_SQL: readonly string[];
|
|
11
|
+
export declare class Migration20260817T055457PriceListsSingleSystemPriceList extends Migration {
|
|
12
|
+
up(): Promise<void>;
|
|
13
|
+
down(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=20260817T055457_price_lists_single_system_price_list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260817T055457_price_lists_single_system_price_list.d.ts","sourceRoot":"","sources":["../../src/migrations/20260817T055457_price_lists_single_system_price_list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AA4BlD,wDAAwD;AACxD,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAczD,CAAC;AAEF,qBAAa,uDAAwD,SAAQ,SAAS;IACrE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAInB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Exactly one system price list, enforced by the database (issue #50).
|
|
4
|
+
*
|
|
5
|
+
* `is_system` marks the seeded `Default` row — the terminal fallback of the
|
|
6
|
+
* resolution chain (feature 011, FR-026/FR-027). Since the issue-#132 ruling
|
|
7
|
+
* made catalogue listings ask `price_lists` for the price they show, that row
|
|
8
|
+
* is load-bearing: the chain is *default price list → prices attached directly
|
|
9
|
+
* to the product → no price displayed*. Nothing enforced the singleton, so a
|
|
10
|
+
* second `is_system` row was merely a convention away, and a test fixture
|
|
11
|
+
* created one.
|
|
12
|
+
*
|
|
13
|
+
* `price_lists` is `@RuleScoped()` — it carries **no** tenant column, because
|
|
14
|
+
* organisation targeting lives inside `application_rule` rather than beside it.
|
|
15
|
+
* So the singleton is platform-wide and the index has no tenant key to add:
|
|
16
|
+
* one `is_system` row in the table, full stop. A per-organisation index here
|
|
17
|
+
* would be a column that does not exist.
|
|
18
|
+
*
|
|
19
|
+
* The demotion below runs first so the index can be created on a database that
|
|
20
|
+
* already drifted. It keeps the canonically seeded `Default` row when present
|
|
21
|
+
* and otherwise the oldest system row, and clears the flag on the rest. That
|
|
22
|
+
* drops no list, no assignment and no bracket — only a marker that was never
|
|
23
|
+
* valid on more than one row.
|
|
24
|
+
*/
|
|
25
|
+
const DEFAULT_PRICE_LIST_ID = '00000000-0000-4000-8000-00000000d51b';
|
|
26
|
+
/** The partial unique index this migration installs. */
|
|
27
|
+
export const SYSTEM_PRICE_LIST_INDEX = 'price_lists_is_system_unique';
|
|
28
|
+
/**
|
|
29
|
+
* `up()` as data, so the demotion can be exercised against a database that has
|
|
30
|
+
* actually drifted — which a freshly migrated test database never has.
|
|
31
|
+
* `test/integration/price_lists/system-price-list-singleton.test.ts` replays
|
|
32
|
+
* these statements after re-creating the drift.
|
|
33
|
+
*/
|
|
34
|
+
export const SINGLE_SYSTEM_PRICE_LIST_SQL = [
|
|
35
|
+
`update "price_lists"
|
|
36
|
+
set "is_system" = false
|
|
37
|
+
where "is_system" = true
|
|
38
|
+
and "id" <> (
|
|
39
|
+
select "id"
|
|
40
|
+
from "price_lists"
|
|
41
|
+
where "is_system" = true
|
|
42
|
+
order by ("id" = '${DEFAULT_PRICE_LIST_ID}') desc, "created_at" asc, "id" asc
|
|
43
|
+
limit 1
|
|
44
|
+
);`,
|
|
45
|
+
`create unique index if not exists "${SYSTEM_PRICE_LIST_INDEX}"
|
|
46
|
+
on "price_lists" ("is_system")
|
|
47
|
+
where "is_system" = true;`,
|
|
48
|
+
];
|
|
49
|
+
export class Migration20260817T055457PriceListsSingleSystemPriceList extends Migration {
|
|
50
|
+
async up() {
|
|
51
|
+
for (const statement of SINGLE_SYSTEM_PRICE_LIST_SQL)
|
|
52
|
+
this.addSql(statement);
|
|
53
|
+
}
|
|
54
|
+
async down() {
|
|
55
|
+
this.addSql(`drop index if exists "${SYSTEM_PRICE_LIST_INDEX}";`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=20260817T055457_price_lists_single_system_price_list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260817T055457_price_lists_single_system_price_list.js","sourceRoot":"","sources":["../../src/migrations/20260817T055457_price_lists_single_system_price_list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,qBAAqB,GAAG,sCAAsC,CAAC;AAErE,wDAAwD;AACxD,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAsB;IAC7D;;;;;;;6BAO2B,qBAAqB;;SAEzC;IACP,sCAAsC,uBAAuB;;+BAEhC;CAC9B,CAAC;AAEF,MAAM,OAAO,uDAAwD,SAAQ,SAAS;IAC3E,KAAK,CAAC,EAAE;QACf,KAAK,MAAM,SAAS,IAAI,4BAA4B;YAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/E,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,yBAAyB,uBAAuB,IAAI,CAAC,CAAC;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* The one storage cost of feature 086: a partial covering index over the
|
|
4
|
+
* quantity-1 brackets, in amount order.
|
|
5
|
+
*
|
|
6
|
+
* `price_list_price_brackets` at `min_quantity = 1` **is** the per-(price list,
|
|
7
|
+
* product) unit price, exactly and by check constraint. The table carries
|
|
8
|
+
* `check (min_quantity >= 1)` and `check (max_quantity is null or
|
|
9
|
+
* max_quantity >= min_quantity)`, and `resolvePriceBracket(rows, currency, 1)`
|
|
10
|
+
* accepts a row when `min_quantity <= 1` and `max_quantity is null or >= 1`.
|
|
11
|
+
* The first reduces to `min_quantity = 1`; the second always holds. So the
|
|
12
|
+
* partial predicate below is not a heuristic narrowing — it selects every
|
|
13
|
+
* listing price and nothing else, which is why a partial index can be built on
|
|
14
|
+
* it at all.
|
|
15
|
+
*
|
|
16
|
+
* **Column order, and why it is this one.** `price_list_id` and `currency_code`
|
|
17
|
+
* are equality predicates that select one candidate stream; `amount` is the
|
|
18
|
+
* ordering; `product_id` is the tie-break that makes the merge's order total
|
|
19
|
+
* and makes the scan index-only (measured: `Heap Fetches: 0`). Reversing the
|
|
20
|
+
* last two would cost a heap fetch per row and lose the tie-break's position in
|
|
21
|
+
* the sort.
|
|
22
|
+
*
|
|
23
|
+
* **Descending needs no second index.** PostgreSQL scans a btree backwards, so
|
|
24
|
+
* `order by amount desc, product_id desc` uses this one.
|
|
25
|
+
*
|
|
26
|
+
* Measured on the design's synthetic corpora: 7 432 kB over 112 000
|
|
27
|
+
* assignments, 132 MB over 1 597 000. Without it the ordering is a sort of the
|
|
28
|
+
* whole candidate relation — 206 ms with a 4 176 kB external merge to disk, per
|
|
29
|
+
* page view, per viewer.
|
|
30
|
+
*
|
|
31
|
+
* No foreign key, so no manifest `dependencies` entry changes and
|
|
32
|
+
* `fk-dependency-drift` is unaffected.
|
|
33
|
+
*/
|
|
34
|
+
/** The index this migration installs — named once, read by the perf bench. */
|
|
35
|
+
export declare const UNIT_PRICE_AMOUNT_INDEX = "price_list_price_brackets_unit_amount_idx";
|
|
36
|
+
export declare class Migration20260821T135907PriceListsUnitPriceAmountIndex extends Migration {
|
|
37
|
+
up(): Promise<void>;
|
|
38
|
+
down(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=20260821T135907_price_lists_unit_price_amount_index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260821T135907_price_lists_unit_price_amount_index.d.ts","sourceRoot":"","sources":["../../src/migrations/20260821T135907_price_lists_unit_price_amount_index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,8CAA8C,CAAC;AAEnF,qBAAa,sDAAuD,SAAQ,SAAS;IACpE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAQnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* The one storage cost of feature 086: a partial covering index over the
|
|
4
|
+
* quantity-1 brackets, in amount order.
|
|
5
|
+
*
|
|
6
|
+
* `price_list_price_brackets` at `min_quantity = 1` **is** the per-(price list,
|
|
7
|
+
* product) unit price, exactly and by check constraint. The table carries
|
|
8
|
+
* `check (min_quantity >= 1)` and `check (max_quantity is null or
|
|
9
|
+
* max_quantity >= min_quantity)`, and `resolvePriceBracket(rows, currency, 1)`
|
|
10
|
+
* accepts a row when `min_quantity <= 1` and `max_quantity is null or >= 1`.
|
|
11
|
+
* The first reduces to `min_quantity = 1`; the second always holds. So the
|
|
12
|
+
* partial predicate below is not a heuristic narrowing — it selects every
|
|
13
|
+
* listing price and nothing else, which is why a partial index can be built on
|
|
14
|
+
* it at all.
|
|
15
|
+
*
|
|
16
|
+
* **Column order, and why it is this one.** `price_list_id` and `currency_code`
|
|
17
|
+
* are equality predicates that select one candidate stream; `amount` is the
|
|
18
|
+
* ordering; `product_id` is the tie-break that makes the merge's order total
|
|
19
|
+
* and makes the scan index-only (measured: `Heap Fetches: 0`). Reversing the
|
|
20
|
+
* last two would cost a heap fetch per row and lose the tie-break's position in
|
|
21
|
+
* the sort.
|
|
22
|
+
*
|
|
23
|
+
* **Descending needs no second index.** PostgreSQL scans a btree backwards, so
|
|
24
|
+
* `order by amount desc, product_id desc` uses this one.
|
|
25
|
+
*
|
|
26
|
+
* Measured on the design's synthetic corpora: 7 432 kB over 112 000
|
|
27
|
+
* assignments, 132 MB over 1 597 000. Without it the ordering is a sort of the
|
|
28
|
+
* whole candidate relation — 206 ms with a 4 176 kB external merge to disk, per
|
|
29
|
+
* page view, per viewer.
|
|
30
|
+
*
|
|
31
|
+
* No foreign key, so no manifest `dependencies` entry changes and
|
|
32
|
+
* `fk-dependency-drift` is unaffected.
|
|
33
|
+
*/
|
|
34
|
+
/** The index this migration installs — named once, read by the perf bench. */
|
|
35
|
+
export const UNIT_PRICE_AMOUNT_INDEX = 'price_list_price_brackets_unit_amount_idx';
|
|
36
|
+
export class Migration20260821T135907PriceListsUnitPriceAmountIndex extends Migration {
|
|
37
|
+
async up() {
|
|
38
|
+
this.addSql(`create index if not exists "${UNIT_PRICE_AMOUNT_INDEX}"
|
|
39
|
+
on "price_list_price_brackets" ("price_list_id", "currency_code", "amount", "product_id")
|
|
40
|
+
where "min_quantity" = 1;`);
|
|
41
|
+
}
|
|
42
|
+
async down() {
|
|
43
|
+
this.addSql(`drop index if exists "${UNIT_PRICE_AMOUNT_INDEX}";`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=20260821T135907_price_lists_unit_price_amount_index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260821T135907_price_lists_unit_price_amount_index.js","sourceRoot":"","sources":["../../src/migrations/20260821T135907_price_lists_unit_price_amount_index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,2CAA2C,CAAC;AAEnF,MAAM,OAAO,sDAAuD,SAAQ,SAAS;IAC1E,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,+BAA+B,uBAAuB;;kCAE1B,CAC7B,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,yBAAyB,uBAAuB,IAAI,CAAC,CAAC;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 orders **this module's own** migrations
|
|
10
|
+
* and nothing else (feature 081). Where the block sits relative to every other
|
|
11
|
+
* module's is decided by the manifest `dependencies` graph.
|
|
12
|
+
*
|
|
13
|
+
* The **named** exports stay, and the asymmetry with `./backend` — which
|
|
14
|
+
* publishes an array and no entity class by name (D-168) — is deliberate.
|
|
15
|
+
* `db/migrations-registry.generated.ts` imports each class by name from this
|
|
16
|
+
* specifier, and a migration class name is contract in a way an entity class
|
|
17
|
+
* 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
|
|
22
|
+
* is a query against a table nobody created.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260426T075235PriceListsPricingInit } from './20260426T075235_price_lists_pricing_init.js';
|
|
25
|
+
import { Migration20260504T125655PriceListsEngine } from './20260504T125655_price_lists_engine.js';
|
|
26
|
+
import { Migration20260817T055457PriceListsSingleSystemPriceList } from './20260817T055457_price_lists_single_system_price_list.js';
|
|
27
|
+
import { Migration20260821T135907PriceListsUnitPriceAmountIndex } from './20260821T135907_price_lists_unit_price_amount_index.js';
|
|
28
|
+
export declare const migrations: (typeof Migration20260426T075235PriceListsPricingInit)[];
|
|
29
|
+
export { Migration20260426T075235PriceListsPricingInit, Migration20260504T125655PriceListsEngine, Migration20260817T055457PriceListsSingleSystemPriceList, Migration20260821T135907PriceListsUnitPriceAmountIndex, };
|
|
30
|
+
//# 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,6CAA6C,EAAE,MAAM,+CAA+C,CAAC;AAC9G,OAAO,EAAE,wCAAwC,EAAE,MAAM,yCAAyC,CAAC;AACnG,OAAO,EAAE,uDAAuD,EAAE,MAAM,2DAA2D,CAAC;AACpI,OAAO,EAAE,sDAAsD,EAAE,MAAM,0DAA0D,CAAC;AAElI,eAAO,MAAM,UAAU,0DAKtB,CAAC;AAEF,OAAO,EACL,6CAA6C,EAC7C,wCAAwC,EACxC,uDAAuD,EACvD,sDAAsD,GACvD,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
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 orders **this module's own** migrations
|
|
10
|
+
* and nothing else (feature 081). Where the block sits relative to every other
|
|
11
|
+
* module's is decided by the manifest `dependencies` graph.
|
|
12
|
+
*
|
|
13
|
+
* The **named** exports stay, and the asymmetry with `./backend` — which
|
|
14
|
+
* publishes an array and no entity class by name (D-168) — is deliberate.
|
|
15
|
+
* `db/migrations-registry.generated.ts` imports each class by name from this
|
|
16
|
+
* specifier, and a migration class name is contract in a way an entity class
|
|
17
|
+
* 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
|
|
22
|
+
* is a query against a table nobody created.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260426T075235PriceListsPricingInit } from './20260426T075235_price_lists_pricing_init.js';
|
|
25
|
+
import { Migration20260504T125655PriceListsEngine } from './20260504T125655_price_lists_engine.js';
|
|
26
|
+
import { Migration20260817T055457PriceListsSingleSystemPriceList } from './20260817T055457_price_lists_single_system_price_list.js';
|
|
27
|
+
import { Migration20260821T135907PriceListsUnitPriceAmountIndex } from './20260821T135907_price_lists_unit_price_amount_index.js';
|
|
28
|
+
export const migrations = [
|
|
29
|
+
Migration20260426T075235PriceListsPricingInit,
|
|
30
|
+
Migration20260504T125655PriceListsEngine,
|
|
31
|
+
Migration20260817T055457PriceListsSingleSystemPriceList,
|
|
32
|
+
Migration20260821T135907PriceListsUnitPriceAmountIndex,
|
|
33
|
+
];
|
|
34
|
+
export { Migration20260426T075235PriceListsPricingInit, Migration20260504T125655PriceListsEngine, Migration20260817T055457PriceListsSingleSystemPriceList, Migration20260821T135907PriceListsUnitPriceAmountIndex, };
|
|
35
|
+
//# 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,6CAA6C,EAAE,MAAM,+CAA+C,CAAC;AAC9G,OAAO,EAAE,wCAAwC,EAAE,MAAM,yCAAyC,CAAC;AACnG,OAAO,EAAE,uDAAuD,EAAE,MAAM,2DAA2D,CAAC;AACpI,OAAO,EAAE,sDAAsD,EAAE,MAAM,0DAA0D,CAAC;AAElI,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,6CAA6C;IAC7C,wCAAwC;IACxC,uDAAuD;IACvD,sDAAsD;CACvD,CAAC;AAEF,OAAO,EACL,6CAA6C,EAC7C,wCAAwC,EACxC,uDAAuD,EACvD,sDAAsD,GACvD,CAAC"}
|