@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,47 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { PriceListService } from './services/price-list-service.js';
|
|
4
|
+
import type { PricingServiceContract } from './services/pricing-service.interface.js';
|
|
5
|
+
import { type CatalogProductReadPort, type OrganizationDetailsPort } from '@endora-commerce/contracts';
|
|
6
|
+
export interface StorefrontPricingRoutesDeps {
|
|
7
|
+
priceListService: PriceListService;
|
|
8
|
+
pricingService: PricingServiceContract;
|
|
9
|
+
emFactory: () => EntityManager;
|
|
10
|
+
/**
|
|
11
|
+
* Feature 075 Phase C — the product these two routes price, over `catalog`'s
|
|
12
|
+
* read port instead of its `Product` entity. It fails closed when `catalog`
|
|
13
|
+
* is off, which is the answer a storefront price probe should get: quoting a
|
|
14
|
+
* price for a product the platform will not serve is worse than refusing.
|
|
15
|
+
*/
|
|
16
|
+
catalogProductRead: CatalogProductReadPort;
|
|
17
|
+
/**
|
|
18
|
+
* The buying organisation behind a signed-in request (issue #265). The same
|
|
19
|
+
* port the admin resolved-price probe in `routes.ts` reads, for the same
|
|
20
|
+
* reason: a price is resolved against an Organization, and the request only
|
|
21
|
+
* carries its id.
|
|
22
|
+
*/
|
|
23
|
+
organizationDetails: OrganizationDetailsPort;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Storefront pricing endpoints (T025/T026 / contracts/pricing-resolution).
|
|
27
|
+
*
|
|
28
|
+
* GET /api/v1/storefront/products/:id/resolved-price
|
|
29
|
+
* GET /api/v1/storefront/pricing/display-mode/:productId
|
|
30
|
+
*
|
|
31
|
+
* Mounted from `plugin.ts` under the platform's existing
|
|
32
|
+
* `/api/v1/storefront/` prefix. Both routes are reachable without a credential
|
|
33
|
+
* and both **read** one when it is there, through the single
|
|
34
|
+
* {@link pricingViewerOf} above.
|
|
35
|
+
*
|
|
36
|
+
* The header above used to say auth was "intentionally absent", and the handler
|
|
37
|
+
* below passed `organization: null` to the engine behind a comment promising
|
|
38
|
+
* the organisation would be derived "when wired" (issue #265). It never was —
|
|
39
|
+
* so the one endpoint on the platform whose name promises a per-customer answer
|
|
40
|
+
* quoted every caller the channel price, from feature 011 until now. The
|
|
41
|
+
* ruling MR !796 settled for the catalogue applies here unchanged: an anonymous
|
|
42
|
+
* visitor sees the channel price, a signed-in buyer sees their own — and since
|
|
43
|
+
* issue #271 the display mode beside it is resolved for the same viewer, so the
|
|
44
|
+
* two cannot answer "net or gross for this buyer?" differently.
|
|
45
|
+
*/
|
|
46
|
+
export declare function registerStorefrontPricingRoutes(app: FastifyInstance, deps: StorefrontPricingRoutesDeps): Promise<void>;
|
|
47
|
+
//# sourceMappingURL=routes.storefront.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.storefront.d.ts","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAG7B,MAAM,4BAA4B,CAAC;AAIpC,MAAM,WAAW,2BAA2B;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,cAAc,EAAE,sBAAsB,CAAC;IACvC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B;;;;;OAKG;IACH,kBAAkB,EAAE,sBAAsB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,EAAE,uBAAuB,CAAC;CAC9C;AA2CD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,+BAA+B,CACnD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,IAAI,CAAC,CA+Gf"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { isProductVisibleTo, } from '@endora-commerce/contracts';
|
|
2
|
+
import { getResolvedChannel } from '@endora-commerce/platform/kernel';
|
|
3
|
+
import { markPersonalisedPricing, productAudienceOf } from '@endora-commerce/platform/http';
|
|
4
|
+
async function pricingViewerOf(request, organizationDetails) {
|
|
5
|
+
const audience = productAudienceOf(request);
|
|
6
|
+
// `null` covers three callers whose answers happen to agree: the anonymous
|
|
7
|
+
// visitor, the authenticated caller with no Organization (feature 026's
|
|
8
|
+
// guest-style account, an unbound API key), and the buyer whose Organization
|
|
9
|
+
// row is gone.
|
|
10
|
+
const organization = audience.organizationId === null
|
|
11
|
+
? null
|
|
12
|
+
: await organizationDetails.findById(audience.organizationId);
|
|
13
|
+
return { audience, organization, customerKind: organization ? 'signed_in' : 'guest' };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Storefront pricing endpoints (T025/T026 / contracts/pricing-resolution).
|
|
17
|
+
*
|
|
18
|
+
* GET /api/v1/storefront/products/:id/resolved-price
|
|
19
|
+
* GET /api/v1/storefront/pricing/display-mode/:productId
|
|
20
|
+
*
|
|
21
|
+
* Mounted from `plugin.ts` under the platform's existing
|
|
22
|
+
* `/api/v1/storefront/` prefix. Both routes are reachable without a credential
|
|
23
|
+
* and both **read** one when it is there, through the single
|
|
24
|
+
* {@link pricingViewerOf} above.
|
|
25
|
+
*
|
|
26
|
+
* The header above used to say auth was "intentionally absent", and the handler
|
|
27
|
+
* below passed `organization: null` to the engine behind a comment promising
|
|
28
|
+
* the organisation would be derived "when wired" (issue #265). It never was —
|
|
29
|
+
* so the one endpoint on the platform whose name promises a per-customer answer
|
|
30
|
+
* quoted every caller the channel price, from feature 011 until now. The
|
|
31
|
+
* ruling MR !796 settled for the catalogue applies here unchanged: an anonymous
|
|
32
|
+
* visitor sees the channel price, a signed-in buyer sees their own — and since
|
|
33
|
+
* issue #271 the display mode beside it is resolved for the same viewer, so the
|
|
34
|
+
* two cannot answer "net or gross for this buyer?" differently.
|
|
35
|
+
*/
|
|
36
|
+
export async function registerStorefrontPricingRoutes(app, deps) {
|
|
37
|
+
const { priceListService, pricingService, catalogProductRead, organizationDetails } = deps;
|
|
38
|
+
app.get('/api/v1/storefront/pricing/display-mode/:productId', async (request, reply) => {
|
|
39
|
+
const viewer = await pricingViewerOf(request, organizationDetails);
|
|
40
|
+
// Before the read, as the resolved price below does: this answer is
|
|
41
|
+
// derived for the caller once the caller names an Organization, so it must
|
|
42
|
+
// not enter a cache another caller can read. The anonymous answer stays
|
|
43
|
+
// unstamped and is still the representation the storefront's shared
|
|
44
|
+
// window and a crawler hold (Principle VII).
|
|
45
|
+
markPersonalisedPricing(reply, viewer.audience);
|
|
46
|
+
const product = await catalogProductRead.findById(request.params.productId);
|
|
47
|
+
// Issue #227 — this route took a bare product id and answered about it.
|
|
48
|
+
// Whether a price is even displayed for a product an operator restricted
|
|
49
|
+
// to one distributor is that operator's answer, so the row has to pass the
|
|
50
|
+
// audience test before this route says anything about it at all.
|
|
51
|
+
if (!product || !isProductVisibleTo(product, viewer.audience)) {
|
|
52
|
+
reply.status(404);
|
|
53
|
+
return { error: { code: 'NOT_FOUND', message: 'Product not found.' } };
|
|
54
|
+
}
|
|
55
|
+
// Feature 053 / FR-002: read the channel resolved once by the canonical
|
|
56
|
+
// middleware (`getResolvedChannel()`) — no header re-parse, no re-query.
|
|
57
|
+
const channel = getResolvedChannel(request);
|
|
58
|
+
// Issue #271 — the viewer's, matching the price. This route used to pass
|
|
59
|
+
// `organizationId: null, customerKind: 'guest'` unconditionally, so the
|
|
60
|
+
// cart that reads it got the `unauthenticated_display_mode` key while the
|
|
61
|
+
// product page's resolved price carried the signed-in one: the same buyer,
|
|
62
|
+
// the same product, two conventions. The chain the resolver walks is
|
|
63
|
+
// unchanged — Product → Category → Organization → Settings, the
|
|
64
|
+
// organisation's say being the `price_display_mode_overrides` row it
|
|
65
|
+
// already consults — and only the viewer handed to it moved.
|
|
66
|
+
const mode = await priceListService.resolveDisplayMode({
|
|
67
|
+
productId: product.id,
|
|
68
|
+
organizationId: viewer.organization?.id ?? null,
|
|
69
|
+
salesChannelId: channel.id,
|
|
70
|
+
customerKind: viewer.customerKind,
|
|
71
|
+
});
|
|
72
|
+
return { data: { displayMode: mode } };
|
|
73
|
+
});
|
|
74
|
+
app.get('/api/v1/storefront/products/:id/resolved-price', async (request, reply) => {
|
|
75
|
+
const viewer = await pricingViewerOf(request, organizationDetails);
|
|
76
|
+
// Before the read, as `catalog`'s public routes do: this response's price
|
|
77
|
+
// is resolved for the caller, and so is the 404 below, so a response that
|
|
78
|
+
// named an organisation must not enter a shared cache whatever it turned
|
|
79
|
+
// out to be. Anonymous responses are left unstamped and unchanged — still
|
|
80
|
+
// the representation a crawler indexes.
|
|
81
|
+
markPersonalisedPricing(reply, viewer.audience);
|
|
82
|
+
const product = await catalogProductRead.findById(request.params.id);
|
|
83
|
+
// Issue #227 — see the display-mode route above. A resolved price is the
|
|
84
|
+
// most direct disclosure this module has.
|
|
85
|
+
if (!product || !isProductVisibleTo(product, viewer.audience)) {
|
|
86
|
+
reply.status(404);
|
|
87
|
+
return { error: { code: 'NOT_FOUND', message: 'Product not found.' } };
|
|
88
|
+
}
|
|
89
|
+
const channel = getResolvedChannel(request);
|
|
90
|
+
const quantity = Math.max(1, Number(request.query.quantity ?? '1') || 1);
|
|
91
|
+
const currency = request.query.currency?.toUpperCase();
|
|
92
|
+
const variantId = request.query.variantId ?? null;
|
|
93
|
+
// The engine reads `customerGroupId` off the record `pricingViewerOf`
|
|
94
|
+
// loaded, so the organisation's group travels with it; a customer's *own*
|
|
95
|
+
// group override (feature 040 R6) is not resolved here, exactly as it is
|
|
96
|
+
// not on the catalogue listing this page's cards come from.
|
|
97
|
+
const out = await pricingService.resolveEngine({
|
|
98
|
+
product,
|
|
99
|
+
variantId,
|
|
100
|
+
context: {
|
|
101
|
+
quantity,
|
|
102
|
+
organization: viewer.organization,
|
|
103
|
+
salesChannel: channel,
|
|
104
|
+
...(currency ? { currencyCode: currency } : {}),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
data: {
|
|
109
|
+
resolvedPrice: {
|
|
110
|
+
baseListId: out.base.listId,
|
|
111
|
+
basePrice: out.base.bracket
|
|
112
|
+
? { amount: out.base.bracket.amount, currency: out.currencyCode }
|
|
113
|
+
: null,
|
|
114
|
+
saleListId: out.sale?.listId ?? null,
|
|
115
|
+
salePrice: out.sale
|
|
116
|
+
? { amount: out.sale.bracket.amount, currency: out.currencyCode }
|
|
117
|
+
: null,
|
|
118
|
+
displayMode: out.displayMode,
|
|
119
|
+
currencyCode: out.currencyCode,
|
|
120
|
+
quantityBracket: out.base.bracket
|
|
121
|
+
? {
|
|
122
|
+
minQuantity: out.base.bracket.minQuantity,
|
|
123
|
+
maxQuantity: out.base.bracket.maxQuantity ?? null,
|
|
124
|
+
}
|
|
125
|
+
: null,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=routes.storefront.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.storefront.js","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure rule evaluator (feature 011 / FR-022, FR-023, FR-026, FR-029).
|
|
3
|
+
*
|
|
4
|
+
* The implementation moved to `@endora-commerce/contracts` in feature 075's Phase P
|
|
5
|
+
* (FR-013): it is a pure function over an `ApplicationRule` — a shape that
|
|
6
|
+
* package already declares — and a context of plain ids, so switching
|
|
7
|
+
* `price_lists` off cannot change whether a rule matches and a gated port
|
|
8
|
+
* answering 503 would be a bug rather than a degrade.
|
|
9
|
+
*
|
|
10
|
+
* There is a second reason it had to be one implementation rather than a port:
|
|
11
|
+
* `organizations` evaluates the same rules to work out an organisation's
|
|
12
|
+
* effective price lists, and it must reach the *same* answer the resolver
|
|
13
|
+
* does. Two copies that drifted would show an operator one set of lists on the
|
|
14
|
+
* organisation screen and price from another at checkout.
|
|
15
|
+
*
|
|
16
|
+
* The two type names widen when they leave the module (`ResolutionContext` and
|
|
17
|
+
* `RuleEvaluation` are too generic for a package every module imports), so
|
|
18
|
+
* they are aliased back here. Both this shim and the aliases go with the last
|
|
19
|
+
* consumer Phase C rewires.
|
|
20
|
+
*/
|
|
21
|
+
import type { PriceListResolutionContext, PriceListRuleEvaluation } from '@endora-commerce/contracts';
|
|
22
|
+
export { evaluateApplicationRule } from '@endora-commerce/contracts';
|
|
23
|
+
export type ResolutionContext = PriceListResolutionContext;
|
|
24
|
+
export type RuleEvaluation = PriceListRuleEvaluation;
|
|
25
|
+
//# sourceMappingURL=application-rule-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-rule-evaluator.d.ts","sourceRoot":"","sources":["../../../src/backend/services/application-rule-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EACV,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,MAAM,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-rule-evaluator.js","sourceRoot":"","sources":["../../../src/backend/services/application-rule-evaluator.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuditReferenceRegistryPort } from '@endora-commerce/contracts';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
/**
|
|
4
|
+
* What a `price_list` audit row is called, and where the admin app shows it
|
|
5
|
+
* (feature 075, D-87 drain).
|
|
6
|
+
*
|
|
7
|
+
* `audit_logs` used to run `select id, name, code from price_lists` and spell
|
|
8
|
+
* `/price-lists/{id}` into its own switch statement.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerPriceListAuditReferences(registry: AuditReferenceRegistryPort, emFactory: () => EntityManager): void;
|
|
11
|
+
//# sourceMappingURL=audit-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-references.d.ts","sourceRoot":"","sources":["../../../src/backend/services/audit-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,0BAA0B,EACpC,SAAS,EAAE,MAAM,aAAa,GAC7B,IAAI,CAiBN"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PriceList } from '../entities/price-list.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a `price_list` audit row is called, and where the admin app shows it
|
|
4
|
+
* (feature 075, D-87 drain).
|
|
5
|
+
*
|
|
6
|
+
* `audit_logs` used to run `select id, name, code from price_lists` and spell
|
|
7
|
+
* `/price-lists/{id}` into its own switch statement.
|
|
8
|
+
*/
|
|
9
|
+
export function registerPriceListAuditReferences(registry, emFactory) {
|
|
10
|
+
registry.register({
|
|
11
|
+
ownerModuleId: 'price_lists',
|
|
12
|
+
referenceType: 'price_list',
|
|
13
|
+
resolve: async (ids) => {
|
|
14
|
+
const rows = await emFactory().find(PriceList, { id: { $in: [...ids] } }, { fields: ['id', 'name', 'code'] });
|
|
15
|
+
return rows.map((row) => ({
|
|
16
|
+
id: row.id,
|
|
17
|
+
label: row.name.length > 0 ? row.name : row.code,
|
|
18
|
+
url: `/price-lists/${row.id}`,
|
|
19
|
+
}));
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=audit-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-references.js","sourceRoot":"","sources":["../../../src/backend/services/audit-references.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,UAAU,gCAAgC,CAC9C,QAAoC,EACpC,SAA8B;IAE9B,QAAQ,CAAC,QAAQ,CAAC;QAChB,aAAa,EAAE,aAAa;QAC5B,aAAa,EAAE,YAAY;QAC3B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrB,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CACjC,SAAS,EACT,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,EAAE,EACzB,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CACnC,CAAC;YACF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACxB,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI;gBAChD,GAAG,EAAE,gBAAgB,GAAG,CAAC,EAAE,EAAE;aAC9B,CAAC,CAAC,CAAC;QACN,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CatalogProductReadPort } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* Default price list migration helper (feature 011 / US1, FR-002, FR-003,
|
|
5
|
+
* research §R7).
|
|
6
|
+
*
|
|
7
|
+
* Surfaced as a callable service so:
|
|
8
|
+
* - migration `031` invokes it inline at upgrade time (preserved here),
|
|
9
|
+
* - an admin "repair" command can re-run it idempotently,
|
|
10
|
+
* - integration tests can re-seed `Default` after a truncate.
|
|
11
|
+
*
|
|
12
|
+
* Returns a structured report: how many products got fresh assignments, how
|
|
13
|
+
* many bracket rows were written, and which products received an identity
|
|
14
|
+
* copy across more than one currency (so administrators can revisit and set
|
|
15
|
+
* proper per-currency values per Q3).
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_PRICE_LIST_ID = "00000000-0000-4000-8000-00000000d51b";
|
|
18
|
+
export interface MigrationReport {
|
|
19
|
+
/** Products newly assigned to the Default list during this run. */
|
|
20
|
+
productsAssigned: number;
|
|
21
|
+
/** Bracket rows newly inserted during this run. */
|
|
22
|
+
bracketRowsInserted: number;
|
|
23
|
+
/** Products that ended up with > 1 currency bracket (multi-currency identity copy). */
|
|
24
|
+
productsWithMultiCurrencyIdentityCopy: Array<{
|
|
25
|
+
productId: string;
|
|
26
|
+
currencies: string[];
|
|
27
|
+
legacyAmount: string;
|
|
28
|
+
}>;
|
|
29
|
+
/** Products skipped because the legacy field was unparseable. */
|
|
30
|
+
productsSkipped: Array<{
|
|
31
|
+
productId: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export declare class DefaultPriceListMigrator {
|
|
36
|
+
private readonly emFactory;
|
|
37
|
+
constructor(emFactory: () => EntityManager);
|
|
38
|
+
/**
|
|
39
|
+
* Idempotent upsert of the seeded `Default` price list. Re-running is a
|
|
40
|
+
* no-op when the row already exists. Used by both migration 031 and any
|
|
41
|
+
* test fixture that truncates `price_lists` and needs to restore Default.
|
|
42
|
+
*/
|
|
43
|
+
seedDefault(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Idempotent re-run of the legacy `attributeValues.defaultPrice` migration.
|
|
46
|
+
* Re-runs are no-ops because every UPSERT keys on natural primary keys.
|
|
47
|
+
* Does NOT strip the legacy `attributeValues.defaultPrice` / `price` keys
|
|
48
|
+
* (per the expand→migrate→contract rollout — see Phase 2 strategy note in
|
|
49
|
+
* tasks.md).
|
|
50
|
+
*
|
|
51
|
+
* Auto-seeds the Default row first so this method works against a fresh
|
|
52
|
+
* test fixture that just truncated `price_lists`.
|
|
53
|
+
*
|
|
54
|
+
* Feature 075 Phase C — the product list arrives over `catalog`'s published
|
|
55
|
+
* read port instead of `em.find(Product, {})`.
|
|
56
|
+
*
|
|
57
|
+
* The port is a **parameter of this method**, not of the constructor, for two
|
|
58
|
+
* reasons. `seedDefault` needs nothing of `catalog`, and eighteen of this
|
|
59
|
+
* class's twenty callers only ever call that — a constructor argument would
|
|
60
|
+
* make all of them declare a dependency two of them have. And the read has to
|
|
61
|
+
* happen on the **caller's** unit of work: a backfill run inside an open
|
|
62
|
+
* transaction, as the dev seed and the migration tests do, must see the
|
|
63
|
+
* products that transaction has just written, which a port bound to another
|
|
64
|
+
* fork cannot. Passing it per call is what lets the caller say which
|
|
65
|
+
* `EntityManager` the read runs on, and the previous `em.find(Product, …)`
|
|
66
|
+
* only got that right by accident of sharing this class's own factory.
|
|
67
|
+
*/
|
|
68
|
+
run(catalogProductRead: CatalogProductReadPort): Promise<MigrationReport>;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=default-price-list-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-price-list-migration.d.ts","sourceRoot":"","sources":["../../../src/backend/services/default-price-list-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAMzE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,yCAAyC,CAAC;AAE5E,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,qCAAqC,EAAE,KAAK,CAAC;QAC3C,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,iEAAiE;IACjE,eAAe,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAE3D;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAgClC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,GAAG,CAAC,kBAAkB,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;CAiGhF"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { SalesChannel } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { PriceList } from '../entities/price-list.entity.js';
|
|
3
|
+
import { PriceListProduct } from '../entities/price-list-product.entity.js';
|
|
4
|
+
import { PriceListPriceBracket } from '../entities/price-list-price-bracket.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default price list migration helper (feature 011 / US1, FR-002, FR-003,
|
|
7
|
+
* research §R7).
|
|
8
|
+
*
|
|
9
|
+
* Surfaced as a callable service so:
|
|
10
|
+
* - migration `031` invokes it inline at upgrade time (preserved here),
|
|
11
|
+
* - an admin "repair" command can re-run it idempotently,
|
|
12
|
+
* - integration tests can re-seed `Default` after a truncate.
|
|
13
|
+
*
|
|
14
|
+
* Returns a structured report: how many products got fresh assignments, how
|
|
15
|
+
* many bracket rows were written, and which products received an identity
|
|
16
|
+
* copy across more than one currency (so administrators can revisit and set
|
|
17
|
+
* proper per-currency values per Q3).
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_PRICE_LIST_ID = '00000000-0000-4000-8000-00000000d51b';
|
|
20
|
+
export class DefaultPriceListMigrator {
|
|
21
|
+
emFactory;
|
|
22
|
+
constructor(emFactory) {
|
|
23
|
+
this.emFactory = emFactory;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Idempotent upsert of the seeded `Default` price list. Re-running is a
|
|
27
|
+
* no-op when the row already exists. Used by both migration 031 and any
|
|
28
|
+
* test fixture that truncates `price_lists` and needs to restore Default.
|
|
29
|
+
*/
|
|
30
|
+
async seedDefault() {
|
|
31
|
+
// command-coverage-ignore: one-time system bootstrap that seeds the Default
|
|
32
|
+
// price list at install/migration time — not an admin action.
|
|
33
|
+
const em = this.emFactory();
|
|
34
|
+
const existing = await em.findOne(PriceList, { id: DEFAULT_PRICE_LIST_ID });
|
|
35
|
+
if (existing) {
|
|
36
|
+
// Keep engine columns coherent if they were left unset on an older row.
|
|
37
|
+
existing.name = 'Default';
|
|
38
|
+
existing.type = 'base';
|
|
39
|
+
existing.status = 'active';
|
|
40
|
+
existing.applicationRule = { kind: 'all' };
|
|
41
|
+
existing.isSystem = true;
|
|
42
|
+
existing.modifiedAt = new Date();
|
|
43
|
+
await em.flush();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const row = em.create(PriceList, {
|
|
47
|
+
id: DEFAULT_PRICE_LIST_ID,
|
|
48
|
+
code: 'default',
|
|
49
|
+
name: 'Default',
|
|
50
|
+
currency: 'PLN',
|
|
51
|
+
isDefault: true,
|
|
52
|
+
priority: 0,
|
|
53
|
+
type: 'base',
|
|
54
|
+
status: 'active',
|
|
55
|
+
applicationRule: { kind: 'all' },
|
|
56
|
+
isSystem: true,
|
|
57
|
+
modifiedAt: new Date(),
|
|
58
|
+
});
|
|
59
|
+
await em.persistAndFlush(row);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Idempotent re-run of the legacy `attributeValues.defaultPrice` migration.
|
|
63
|
+
* Re-runs are no-ops because every UPSERT keys on natural primary keys.
|
|
64
|
+
* Does NOT strip the legacy `attributeValues.defaultPrice` / `price` keys
|
|
65
|
+
* (per the expand→migrate→contract rollout — see Phase 2 strategy note in
|
|
66
|
+
* tasks.md).
|
|
67
|
+
*
|
|
68
|
+
* Auto-seeds the Default row first so this method works against a fresh
|
|
69
|
+
* test fixture that just truncated `price_lists`.
|
|
70
|
+
*
|
|
71
|
+
* Feature 075 Phase C — the product list arrives over `catalog`'s published
|
|
72
|
+
* read port instead of `em.find(Product, {})`.
|
|
73
|
+
*
|
|
74
|
+
* The port is a **parameter of this method**, not of the constructor, for two
|
|
75
|
+
* reasons. `seedDefault` needs nothing of `catalog`, and eighteen of this
|
|
76
|
+
* class's twenty callers only ever call that — a constructor argument would
|
|
77
|
+
* make all of them declare a dependency two of them have. And the read has to
|
|
78
|
+
* happen on the **caller's** unit of work: a backfill run inside an open
|
|
79
|
+
* transaction, as the dev seed and the migration tests do, must see the
|
|
80
|
+
* products that transaction has just written, which a port bound to another
|
|
81
|
+
* fork cannot. Passing it per call is what lets the caller say which
|
|
82
|
+
* `EntityManager` the read runs on, and the previous `em.find(Product, …)`
|
|
83
|
+
* only got that right by accident of sharing this class's own factory.
|
|
84
|
+
*/
|
|
85
|
+
async run(catalogProductRead) {
|
|
86
|
+
// command-coverage-ignore: one-time data migration (backfills the Default
|
|
87
|
+
// price list + assignments) run at install/upgrade — not an admin action.
|
|
88
|
+
await this.seedDefault();
|
|
89
|
+
const em = this.emFactory();
|
|
90
|
+
// Discover the currencies exposed by any sales channel + always include PLN
|
|
91
|
+
// as the platform's default currency (matches migration 031's fallback).
|
|
92
|
+
const channels = await em.find(SalesChannel, {});
|
|
93
|
+
const currencies = new Set(['PLN']);
|
|
94
|
+
for (const ch of channels) {
|
|
95
|
+
for (const c of ch.currencies ?? []) {
|
|
96
|
+
if (typeof c === 'string' && c.length === 3) {
|
|
97
|
+
currencies.add(c.toUpperCase());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Discover legacy-priced products through `catalog`'s read port.
|
|
102
|
+
const products = await catalogProductRead.listAll();
|
|
103
|
+
const skipped = [];
|
|
104
|
+
const candidates = [];
|
|
105
|
+
for (const p of products) {
|
|
106
|
+
const av = p.attributeValues;
|
|
107
|
+
if (!av)
|
|
108
|
+
continue;
|
|
109
|
+
const raw = av['defaultPrice'] ?? av['price'];
|
|
110
|
+
if (raw == null)
|
|
111
|
+
continue;
|
|
112
|
+
const amount = Number(raw);
|
|
113
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
114
|
+
skipped.push({ productId: p.id, reason: 'legacy_price_invalid' });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
candidates.push({ productId: p.id, legacyAmount: amount.toFixed(4) });
|
|
118
|
+
}
|
|
119
|
+
let productsAssigned = 0;
|
|
120
|
+
let bracketRowsInserted = 0;
|
|
121
|
+
const multiCurrency = [];
|
|
122
|
+
const currencyList = [...currencies].sort();
|
|
123
|
+
// Pass 1: assignments — flush before inserting brackets so the FK from
|
|
124
|
+
// bracket → assignment is satisfied at insert time.
|
|
125
|
+
for (const c of candidates) {
|
|
126
|
+
const existingAssignment = await em.findOne(PriceListProduct, {
|
|
127
|
+
priceListId: DEFAULT_PRICE_LIST_ID,
|
|
128
|
+
productId: c.productId,
|
|
129
|
+
});
|
|
130
|
+
if (!existingAssignment) {
|
|
131
|
+
em.create(PriceListProduct, {
|
|
132
|
+
priceListId: DEFAULT_PRICE_LIST_ID,
|
|
133
|
+
productId: c.productId,
|
|
134
|
+
});
|
|
135
|
+
productsAssigned += 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
await em.flush();
|
|
139
|
+
// Pass 2: brackets per currency.
|
|
140
|
+
for (const c of candidates) {
|
|
141
|
+
for (const cur of currencyList) {
|
|
142
|
+
const existingBracket = await em.findOne(PriceListPriceBracket, {
|
|
143
|
+
priceListId: DEFAULT_PRICE_LIST_ID,
|
|
144
|
+
productId: c.productId,
|
|
145
|
+
currencyCode: cur,
|
|
146
|
+
minQuantity: 1,
|
|
147
|
+
});
|
|
148
|
+
if (!existingBracket) {
|
|
149
|
+
em.create(PriceListPriceBracket, {
|
|
150
|
+
priceListId: DEFAULT_PRICE_LIST_ID,
|
|
151
|
+
productId: c.productId,
|
|
152
|
+
currencyCode: cur,
|
|
153
|
+
minQuantity: 1,
|
|
154
|
+
maxQuantity: null,
|
|
155
|
+
amount: c.legacyAmount,
|
|
156
|
+
});
|
|
157
|
+
bracketRowsInserted += 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (currencyList.length > 1) {
|
|
161
|
+
multiCurrency.push({
|
|
162
|
+
productId: c.productId,
|
|
163
|
+
currencies: currencyList,
|
|
164
|
+
legacyAmount: c.legacyAmount,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
await em.flush();
|
|
169
|
+
return {
|
|
170
|
+
productsAssigned,
|
|
171
|
+
bracketRowsInserted,
|
|
172
|
+
productsWithMultiCurrencyIdentityCopy: multiCurrency,
|
|
173
|
+
productsSkipped: skipped,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=default-price-list-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-price-list-migration.js","sourceRoot":"","sources":["../../../src/backend/services/default-price-list-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { DisplayMode } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Pure display-mode resolver (feature 011 / FR-039).
|
|
4
|
+
*
|
|
5
|
+
* Walks the chain Product → Category → Organization → Settings (default for
|
|
6
|
+
* signed-in, unauthenticated for guests) and returns the first non-`inherit`
|
|
7
|
+
* value. Guests carry `organizationId === null`, in which case the
|
|
8
|
+
* Organization-level lookup is skipped.
|
|
9
|
+
*
|
|
10
|
+
* The category-level lookup picks the most specific category override the
|
|
11
|
+
* product belongs to — defined as the deepest category in the tree, with
|
|
12
|
+
* a deterministic tie-break on `(sort_order asc, id asc)`. The category
|
|
13
|
+
* depth + tie-break logic lives at the SQL layer; this pure resolver just
|
|
14
|
+
* receives the already-picked override row (or null).
|
|
15
|
+
*/
|
|
16
|
+
export type CustomerKind = 'guest' | 'signed_in';
|
|
17
|
+
export type ResolvedFrom = 'product' | 'category' | 'organization' | 'settings.default' | 'settings.unauthenticated';
|
|
18
|
+
export interface DisplayModeOverrideLookup {
|
|
19
|
+
/** Product-level override row or null. */
|
|
20
|
+
productOverride: DisplayMode | null;
|
|
21
|
+
/** Most-specific category-level override the product belongs to, or null. */
|
|
22
|
+
categoryOverride: DisplayMode | null;
|
|
23
|
+
/** Organization-level override or null (also null for guests). */
|
|
24
|
+
organizationOverride: DisplayMode | null;
|
|
25
|
+
}
|
|
26
|
+
export interface SettingsDefaults {
|
|
27
|
+
defaultDisplayMode: DisplayMode;
|
|
28
|
+
unauthenticatedDisplayMode: DisplayMode;
|
|
29
|
+
}
|
|
30
|
+
export interface ResolvedDisplayMode {
|
|
31
|
+
mode: DisplayMode;
|
|
32
|
+
resolvedFrom: ResolvedFrom;
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveDisplayMode(customer: CustomerKind, overrides: DisplayModeOverrideLookup, settings: SettingsDefaults): ResolvedDisplayMode;
|
|
35
|
+
/**
|
|
36
|
+
* One category-scope override, with the three fields the chain ranks it by.
|
|
37
|
+
*
|
|
38
|
+
* `resolveDisplayMode` above receives the already-picked override; the ranking
|
|
39
|
+
* that picks it used to live inline in `PriceListService`, which is why the
|
|
40
|
+
* batched path could have ranked differently from the per-product one. It is
|
|
41
|
+
* here now, so both hand their candidates to the same comparator.
|
|
42
|
+
*/
|
|
43
|
+
export interface DisplayModeCategoryCandidate {
|
|
44
|
+
mode: DisplayMode;
|
|
45
|
+
/** Ancestors above the category itself — deeper is more specific. */
|
|
46
|
+
depth: number;
|
|
47
|
+
sortOrder: number;
|
|
48
|
+
categoryId: string;
|
|
49
|
+
}
|
|
50
|
+
/** Most specific first: deepest, then `(sort_order asc, id asc)` as tie-break. */
|
|
51
|
+
export declare function pickCategoryOverride(candidates: readonly DisplayModeCategoryCandidate[]): DisplayModeCategoryCandidate | null;
|
|
52
|
+
export type DisplayModeDecision = {
|
|
53
|
+
source: 'product' | 'category' | 'organization';
|
|
54
|
+
mode: DisplayMode;
|
|
55
|
+
} | {
|
|
56
|
+
source: 'settings';
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The chain's decision with the settings tier left **unread**.
|
|
60
|
+
*
|
|
61
|
+
* The eager `resolveDisplayMode` above wants both settings values in hand,
|
|
62
|
+
* which is one query per product on a listing page and two more on a cart line
|
|
63
|
+
* that an override already answered. This form names the settings step instead
|
|
64
|
+
* of taking its value, so the per-product path reads the pair only when it is
|
|
65
|
+
* reached and the batched path reads it once for the whole page.
|
|
66
|
+
*
|
|
67
|
+
* `organizationOverride` is the caller's answer to "is this a signed-in
|
|
68
|
+
* customer with an organization" — a guest passes `null`, exactly as the
|
|
69
|
+
* eager resolver's `customer === 'signed_in'` guard requires.
|
|
70
|
+
*/
|
|
71
|
+
export declare function decideDisplayMode(inputs: {
|
|
72
|
+
productOverride: DisplayMode | null;
|
|
73
|
+
categoryCandidates: readonly DisplayModeCategoryCandidate[];
|
|
74
|
+
organizationOverride: DisplayMode | null;
|
|
75
|
+
}): DisplayModeDecision;
|
|
76
|
+
/** Which `pricing.*` settings key answers for this customer kind. */
|
|
77
|
+
export declare function settingsDisplayModeKey(customer: CustomerKind): 'default_display_mode' | 'unauthenticated_display_mode';
|
|
78
|
+
//# sourceMappingURL=display-mode-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-mode-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/display-mode-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,WAAW,CAAC;AAEjD,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,UAAU,GACV,cAAc,GACd,kBAAkB,GAClB,0BAA0B,CAAC;AAE/B,MAAM,WAAW,yBAAyB;IACxC,0CAA0C;IAC1C,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,6EAA6E;IAC7E,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,kEAAkE;IAClE,oBAAoB,EAAE,WAAW,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,kBAAkB,EAAE,WAAW,CAAC;IAChC,0BAA0B,EAAE,WAAW,CAAC;CACzC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,yBAAyB,EACpC,QAAQ,EAAE,gBAAgB,GACzB,mBAAmB,CAiBrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,WAAW,CAAC;IAClB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,SAAS,4BAA4B,EAAE,GAClD,4BAA4B,GAAG,IAAI,CAkBrC;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,cAAc,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GACtE;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAE3B;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,kBAAkB,EAAE,SAAS,4BAA4B,EAAE,CAAC;IAC5D,oBAAoB,EAAE,WAAW,GAAG,IAAI,CAAC;CAC1C,GAAG,mBAAmB,CAUtB;AAED,qEAAqE;AACrE,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,GACrB,sBAAsB,GAAG,8BAA8B,CAEzD"}
|