@open-mercato/core 0.4.5-develop-3ce83a8b24 → 0.4.5-develop-539cff4960
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/dist/generated/entities/catalog_product/index.js +16 -0
- package/dist/generated/entities/catalog_product/index.js.map +2 -2
- package/dist/generated/entities/catalog_product_unit_conversion/index.js +27 -0
- package/dist/generated/entities/catalog_product_unit_conversion/index.js.map +7 -0
- package/dist/generated/entities/sales_credit_memo_line/index.js +7 -1
- package/dist/generated/entities/sales_credit_memo_line/index.js.map +2 -2
- package/dist/generated/entities/sales_invoice_line/index.js +7 -1
- package/dist/generated/entities/sales_invoice_line/index.js.map +2 -2
- package/dist/generated/entities/sales_order_line/index.js +6 -0
- package/dist/generated/entities/sales_order_line/index.js.map +2 -2
- package/dist/generated/entities/sales_quote_line/index.js +6 -0
- package/dist/generated/entities/sales_quote_line/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/api/prices/route.js +123 -8
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/product-unit-conversions/route.js +194 -0
- package/dist/modules/catalog/api/product-unit-conversions/route.js.map +7 -0
- package/dist/modules/catalog/api/products/route.js +351 -201
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +1267 -497
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +733 -210
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/commands/index.js +1 -0
- package/dist/modules/catalog/commands/index.js.map +2 -2
- package/dist/modules/catalog/commands/productUnitConversions.js +503 -0
- package/dist/modules/catalog/commands/productUnitConversions.js.map +7 -0
- package/dist/modules/catalog/commands/products.js +355 -73
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/shared.js +18 -4
- package/dist/modules/catalog/commands/shared.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductUomSection.js +591 -0
- package/dist/modules/catalog/components/products/ProductUomSection.js.map +7 -0
- package/dist/modules/catalog/components/products/productForm.js +66 -5
- package/dist/modules/catalog/components/products/productForm.js.map +2 -2
- package/dist/modules/catalog/components/products/productFormUtils.js +68 -0
- package/dist/modules/catalog/components/products/productFormUtils.js.map +7 -0
- package/dist/modules/catalog/data/entities.js +86 -0
- package/dist/modules/catalog/data/entities.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +65 -3
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/events.js +3 -0
- package/dist/modules/catalog/events.js.map +2 -2
- package/dist/modules/catalog/lib/unitCodes.js +7 -0
- package/dist/modules/catalog/lib/unitCodes.js.map +7 -0
- package/dist/modules/catalog/lib/unitResolution.js +53 -0
- package/dist/modules/catalog/lib/unitResolution.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260218225422.js +19 -0
- package/dist/modules/catalog/migrations/Migration20260218225422.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260219084500.js +27 -0
- package/dist/modules/catalog/migrations/Migration20260219084500.js.map +7 -0
- package/dist/modules/catalog/search.js +69 -1
- package/dist/modules/catalog/search.js.map +2 -2
- package/dist/modules/catalog/seed/examples.js +91 -42
- package/dist/modules/catalog/seed/examples.js.map +2 -2
- package/dist/modules/dashboards/seed/analytics.js +3 -0
- package/dist/modules/dashboards/seed/analytics.js.map +2 -2
- package/dist/modules/sales/api/order-lines/route.js +98 -15
- package/dist/modules/sales/api/order-lines/route.js.map +2 -2
- package/dist/modules/sales/api/quote-lines/route.js +101 -14
- package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js +87 -12
- package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +1424 -260
- package/dist/modules/sales/commands/documents.js.map +3 -3
- package/dist/modules/sales/commands/shared.js +6 -2
- package/dist/modules/sales/commands/shared.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +216 -86
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +913 -241
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +3 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js +15 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/data/entities.js +59 -3
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/data/validators.js +35 -0
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/sales/frontend/quote/[token]/page.js +15 -1
- package/dist/modules/sales/frontend/quote/[token]/page.js.map +2 -2
- package/dist/modules/sales/migrations/Migration20260218225423.js +31 -0
- package/dist/modules/sales/migrations/Migration20260218225423.js.map +7 -0
- package/dist/modules/sales/migrations/Migration20260219084501.js +71 -0
- package/dist/modules/sales/migrations/Migration20260219084501.js.map +7 -0
- package/dist/modules/sales/search.js +28 -0
- package/dist/modules/sales/search.js.map +2 -2
- package/dist/modules/sales/seed/examples.js +14 -1
- package/dist/modules/sales/seed/examples.js.map +2 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js +1 -1
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +28 -15
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/translations.js +9 -0
- package/dist/modules/staff/translations.js.map +7 -0
- package/dist/modules/translations/components/TranslationDrawerAction.js +97 -0
- package/dist/modules/translations/components/TranslationDrawerAction.js.map +7 -0
- package/dist/modules/translations/lib/extract-record-id.js +31 -2
- package/dist/modules/translations/lib/extract-record-id.js.map +2 -2
- package/dist/modules/translations/lib/resolve-field-list.js +3 -0
- package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
- package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js +105 -36
- package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map +2 -2
- package/dist/modules/translations/widgets/injection-table.js +18 -29
- package/dist/modules/translations/widgets/injection-table.js.map +2 -2
- package/generated/entities/catalog_product/index.ts +8 -0
- package/generated/entities/catalog_product_unit_conversion/index.ts +12 -0
- package/generated/entities/sales_credit_memo_line/index.ts +3 -0
- package/generated/entities/sales_invoice_line/index.ts +3 -0
- package/generated/entities/sales_order_line/index.ts +3 -0
- package/generated/entities/sales_quote_line/index.ts +3 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +2 -2
- package/src/modules/auth/i18n/de.json +1 -1
- package/src/modules/auth/i18n/en.json +1 -1
- package/src/modules/auth/i18n/es.json +1 -1
- package/src/modules/auth/i18n/pl.json +1 -1
- package/src/modules/catalog/api/prices/route.ts +213 -81
- package/src/modules/catalog/api/product-unit-conversions/route.ts +195 -0
- package/src/modules/catalog/api/products/route.ts +638 -402
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2085 -1072
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +1288 -593
- package/src/modules/catalog/commands/index.ts +1 -0
- package/src/modules/catalog/commands/productUnitConversions.ts +626 -0
- package/src/modules/catalog/commands/products.ts +1151 -693
- package/src/modules/catalog/commands/shared.ts +19 -5
- package/src/modules/catalog/components/products/ProductUomSection.tsx +745 -0
- package/src/modules/catalog/components/products/productForm.ts +369 -256
- package/src/modules/catalog/components/products/productFormUtils.ts +82 -0
- package/src/modules/catalog/data/entities.ts +82 -1
- package/src/modules/catalog/data/validators.ts +118 -34
- package/src/modules/catalog/events.ts +3 -0
- package/src/modules/catalog/i18n/de.json +56 -0
- package/src/modules/catalog/i18n/en.json +56 -0
- package/src/modules/catalog/i18n/es.json +56 -0
- package/src/modules/catalog/i18n/pl.json +56 -0
- package/src/modules/catalog/lib/unitCodes.ts +1 -0
- package/src/modules/catalog/lib/unitResolution.ts +62 -0
- package/src/modules/catalog/migrations/.snapshot-open-mercato.json +245 -0
- package/src/modules/catalog/migrations/Migration20260218225422.ts +21 -0
- package/src/modules/catalog/migrations/Migration20260219084500.ts +26 -0
- package/src/modules/catalog/search.ts +73 -1
- package/src/modules/catalog/seed/examples.ts +552 -479
- package/src/modules/dashboards/i18n/de.json +1 -1
- package/src/modules/dashboards/i18n/en.json +1 -1
- package/src/modules/dashboards/i18n/es.json +1 -1
- package/src/modules/dashboards/i18n/pl.json +1 -1
- package/src/modules/dashboards/seed/analytics.ts +3 -0
- package/src/modules/sales/api/order-lines/route.ts +158 -68
- package/src/modules/sales/api/quote-lines/route.ts +161 -67
- package/src/modules/sales/api/quotes/public/[token]/route.ts +122 -36
- package/src/modules/sales/commands/documents.ts +4250 -2424
- package/src/modules/sales/commands/shared.ts +7 -2
- package/src/modules/sales/components/documents/ItemsSection.tsx +580 -310
- package/src/modules/sales/components/documents/LineItemDialog.tsx +1988 -833
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +17 -3
- package/src/modules/sales/components/documents/lineItemTypes.ts +6 -0
- package/src/modules/sales/data/entities.ts +53 -0
- package/src/modules/sales/data/validators.ts +36 -0
- package/src/modules/sales/frontend/quote/[token]/page.tsx +25 -1
- package/src/modules/sales/i18n/de.json +23 -3
- package/src/modules/sales/i18n/en.json +23 -3
- package/src/modules/sales/i18n/es.json +23 -3
- package/src/modules/sales/i18n/pl.json +23 -3
- package/src/modules/sales/lib/types.ts +30 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +172 -0
- package/src/modules/sales/migrations/Migration20260218225423.ts +37 -0
- package/src/modules/sales/migrations/Migration20260219084501.ts +73 -0
- package/src/modules/sales/search.ts +28 -0
- package/src/modules/sales/seed/examples.ts +20 -1
- package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +1 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +8 -0
- package/src/modules/staff/translations.ts +5 -0
- package/src/modules/translations/components/TranslationDrawerAction.tsx +107 -0
- package/src/modules/translations/lib/extract-record-id.ts +47 -3
- package/src/modules/translations/lib/resolve-field-list.ts +4 -0
- package/src/modules/translations/widgets/injection/translation-manager/widget.client.tsx +108 -36
- package/src/modules/translations/widgets/injection-table.ts +19 -33
- package/src/modules/workflows/i18n/de.json +4 -4
- package/src/modules/workflows/i18n/en.json +4 -4
- package/src/modules/workflows/i18n/es.json +4 -4
- package/src/modules/workflows/i18n/pl.json +4 -4
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"catalog.audit.prices.create": "Utwórz cenę",
|
|
17
17
|
"catalog.audit.prices.delete": "Usuń cenę",
|
|
18
18
|
"catalog.audit.prices.update": "Zaktualizuj cenę",
|
|
19
|
+
"catalog.audit.productUnitConversions.create": "Utwórz konwersję jednostki produktu",
|
|
20
|
+
"catalog.audit.productUnitConversions.delete": "Usuń konwersję jednostki produktu",
|
|
21
|
+
"catalog.audit.productUnitConversions.update": "Zaktualizuj konwersję jednostki produktu",
|
|
19
22
|
"catalog.audit.products.create": "Utwórz produkt",
|
|
20
23
|
"catalog.audit.products.delete": "Usuń produkt",
|
|
21
24
|
"catalog.audit.products.update": "Zaktualizuj produkt",
|
|
@@ -394,6 +397,7 @@
|
|
|
394
397
|
"catalog.products.edit.weight.value": "Waga",
|
|
395
398
|
"catalog.products.errors.handleExists": "Handle jest już używany.",
|
|
396
399
|
"catalog.products.errors.skuExists": "SKU jest już używany.",
|
|
400
|
+
"catalog.products.errors.taxClassNotFound": "Nie znaleziono klasy podatkowej.",
|
|
397
401
|
"catalog.products.filters.active": "Aktywne",
|
|
398
402
|
"catalog.products.filters.categories": "Kategorie",
|
|
399
403
|
"catalog.products.filters.categoriesLoadError": "Nie udało się załadować kategorii",
|
|
@@ -454,12 +458,64 @@
|
|
|
454
458
|
"catalog.products.types.grouped": "Grupowy",
|
|
455
459
|
"catalog.products.types.simple": "Prosty",
|
|
456
460
|
"catalog.products.types.virtual": "Wirtualny",
|
|
461
|
+
"catalog.products.unitPrice.baseQuantity": "Ilość bazowa dla jednostki referencyjnej",
|
|
462
|
+
"catalog.products.unitPrice.enable": "Włącz prezentację ceny jednostkowej UE",
|
|
463
|
+
"catalog.products.unitPrice.errors.baseQuantity": "Ilość bazowa jest wymagana po włączeniu ceny jednostkowej.",
|
|
464
|
+
"catalog.products.unitPrice.errors.referenceUnit": "Jednostka referencyjna jest wymagana po włączeniu ceny jednostkowej.",
|
|
465
|
+
"catalog.products.unitPrice.hint": "Pokaż wyliczoną cenę za wybraną jednostkę referencyjną. W większości przypadków ustaw ilość na 1.",
|
|
466
|
+
"catalog.products.unitPrice.hintWithPreview": "Pokaż wyliczoną cenę za {{quantity}} {{unit}}. Dla większości produktów ustaw 1 (np. 1 kg, 1 l, 1 m²).",
|
|
467
|
+
"catalog.products.unitPrice.options.kg": "1 kg",
|
|
468
|
+
"catalog.products.unitPrice.options.l": "1 l",
|
|
469
|
+
"catalog.products.unitPrice.options.m2": "1 m²",
|
|
470
|
+
"catalog.products.unitPrice.options.m3": "1 m³",
|
|
471
|
+
"catalog.products.unitPrice.options.pc": "1 szt.",
|
|
472
|
+
"catalog.products.unitPrice.referenceUnit": "Jednostka referencyjna",
|
|
473
|
+
"catalog.products.unitPrice.selectReferenceUnit": "Wybierz jednostkę referencyjną",
|
|
474
|
+
"catalog.products.uom.active": "Aktywna",
|
|
475
|
+
"catalog.products.uom.addConversion": "Dodaj konwersję",
|
|
476
|
+
"catalog.products.uom.baseUnit": "Jednostka bazowa",
|
|
477
|
+
"catalog.products.uom.conversionOrderHint": "Użyj strzałek, aby zmienić priorytet konwersji.",
|
|
478
|
+
"catalog.products.uom.conversionPreview": "1 {{fromUnit}} = {{factor}} {{baseUnit}}",
|
|
479
|
+
"catalog.products.uom.conversionUnit": "Jednostka sprzedaży",
|
|
480
|
+
"catalog.products.uom.conversions": "Konwersje produktu",
|
|
481
|
+
"catalog.products.uom.defaultSalesQuantity": "Domyślna ilość sprzedaży",
|
|
482
|
+
"catalog.products.uom.defaultSalesQuantityHint": "Używane do wstępnego wypełniania ilości w pozycjach ofert/zamówień. Wartość jest interpretowana w domyślnej jednostce sprzedaży.",
|
|
483
|
+
"catalog.products.uom.defaultSalesQuantityLabel": "Domyślna ilość linii (w jednostce sprzedaży)",
|
|
484
|
+
"catalog.products.uom.defaultSalesQuantityPreview": "Domyślna pozycja: {{quantity}} {{salesUnit}}.",
|
|
485
|
+
"catalog.products.uom.defaultSalesQuantityPreviewWithNormalization": "Domyślna pozycja: {{quantity}} {{salesUnit}} (= {{normalized}} {{baseUnit}}).",
|
|
486
|
+
"catalog.products.uom.defaultSalesUnit": "Domyślna jednostka sprzedaży",
|
|
487
|
+
"catalog.products.uom.description": "Ustaw jednostkę bazową i sprzedażową oraz konwersje opakowań.",
|
|
488
|
+
"catalog.products.uom.emptyConversions": "Brak skonfigurowanych konwersji.",
|
|
489
|
+
"catalog.products.uom.errors.baseRequired": "Jednostka bazowa jest wymagana, gdy ustawiono domyślną jednostkę sprzedaży.",
|
|
490
|
+
"catalog.products.uom.errors.baseRequiredForConversions": "Jednostka bazowa jest wymagana, gdy skonfigurowano konwersje.",
|
|
491
|
+
"catalog.products.uom.errors.defaultSalesConversionRequired": "Aktywna konwersja dla domyślnej jednostki sprzedaży jest wymagana, gdy różni się od jednostki bazowej.",
|
|
492
|
+
"catalog.products.uom.errors.duplicateConversion": "Duplikat jednostki konwersji nie jest dozwolony.",
|
|
493
|
+
"catalog.products.uom.errors.loadUnits": "Nie udało się załadować jednostek. Spróbuj odświeżyć stronę.",
|
|
494
|
+
"catalog.products.uom.errors.sync": "Nie udało się zsynchronizować konwersji produktu.",
|
|
495
|
+
"catalog.products.uom.loadingUnits": "Ładowanie jednostek...",
|
|
496
|
+
"catalog.products.uom.moveDown": "Przenieś konwersję niżej",
|
|
497
|
+
"catalog.products.uom.moveUp": "Przenieś konwersję wyżej",
|
|
498
|
+
"catalog.products.uom.removeConversion": "Usuń konwersję",
|
|
499
|
+
"catalog.products.uom.roundingMode": "Tryb zaokrąglania",
|
|
500
|
+
"catalog.products.uom.roundingModeDown": "W dół",
|
|
501
|
+
"catalog.products.uom.roundingModeHalfUp": "Połowa w górę",
|
|
502
|
+
"catalog.products.uom.roundingModeUp": "W górę",
|
|
503
|
+
"catalog.products.uom.roundingScale": "Skala zaokrąglania",
|
|
504
|
+
"catalog.products.uom.selectUnit": "Wybierz jednostkę",
|
|
505
|
+
"catalog.products.uom.sortOrder": "Kolejność",
|
|
506
|
+
"catalog.products.uom.title": "Jednostki miary",
|
|
507
|
+
"catalog.products.uom.toBaseFactor": "Współczynnik do bazowej",
|
|
508
|
+
"catalog.products.validation.baseUnitRequired": "Jednostka bazowa jest wymagana, gdy ustawiona jest domyślna jednostka sprzedaży.",
|
|
509
|
+
"catalog.products.validation.handleFormat": "Identyfikator może zawierać tylko małe litery, cyfry, myślniki lub podkreślenia.",
|
|
510
|
+
"catalog.products.validation.referenceUnitRequired": "Jednostka referencyjna jest wymagana, gdy wyświetlanie ceny jednostkowej jest włączone.",
|
|
511
|
+
"catalog.products.validation.titleRequired": "Tytuł jest wymagany.",
|
|
457
512
|
"catalog.search.badge.category": "Kategoria",
|
|
458
513
|
"catalog.search.badge.offer": "Oferta kanału",
|
|
459
514
|
"catalog.search.badge.optionSchema": "Schemat opcji",
|
|
460
515
|
"catalog.search.badge.priceKind": "Rodzaj ceny",
|
|
461
516
|
"catalog.search.badge.product": "Produkt",
|
|
462
517
|
"catalog.search.badge.tag": "Tag",
|
|
518
|
+
"catalog.search.badge.unitConversion": "Przelicznik jednostek",
|
|
463
519
|
"catalog.search.badge.variant": "Wariant",
|
|
464
520
|
"catalog.search.priceKind.excludingTax": "Bez VAT",
|
|
465
521
|
"catalog.search.priceKind.includingTax": "Z VAT",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { canonicalizeUnitCode, toUnitLookupKey, REFERENCE_UNIT_CODES, type ReferenceUnitCode } from "@open-mercato/shared/lib/units/unitCodes";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { EntityManager } from "@mikro-orm/postgresql";
|
|
2
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
3
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
4
|
+
import {
|
|
5
|
+
Dictionary,
|
|
6
|
+
DictionaryEntry,
|
|
7
|
+
} from "@open-mercato/core/modules/dictionaries/data/entities";
|
|
8
|
+
import { canonicalizeUnitCode } from "./unitCodes";
|
|
9
|
+
|
|
10
|
+
export const UOM_DICTIONARY_KEYS = ["unit", "units", "measurement_units"] as const;
|
|
11
|
+
|
|
12
|
+
export async function resolveUnitDictionary(
|
|
13
|
+
em: EntityManager,
|
|
14
|
+
organizationId: string,
|
|
15
|
+
tenantId: string,
|
|
16
|
+
) {
|
|
17
|
+
return findOneWithDecryption(
|
|
18
|
+
em,
|
|
19
|
+
Dictionary,
|
|
20
|
+
{
|
|
21
|
+
organizationId,
|
|
22
|
+
tenantId,
|
|
23
|
+
key: { $in: UOM_DICTIONARY_KEYS },
|
|
24
|
+
deletedAt: null,
|
|
25
|
+
isActive: true,
|
|
26
|
+
},
|
|
27
|
+
{ orderBy: { createdAt: "asc" } },
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function resolveCanonicalUnitCode(
|
|
32
|
+
em: EntityManager,
|
|
33
|
+
params: {
|
|
34
|
+
organizationId: string;
|
|
35
|
+
tenantId: string;
|
|
36
|
+
unitCode: string;
|
|
37
|
+
},
|
|
38
|
+
): Promise<string> {
|
|
39
|
+
const dictionary = await resolveUnitDictionary(
|
|
40
|
+
em,
|
|
41
|
+
params.organizationId,
|
|
42
|
+
params.tenantId,
|
|
43
|
+
);
|
|
44
|
+
if (!dictionary) {
|
|
45
|
+
return canonicalizeUnitCode(params.unitCode) ?? params.unitCode;
|
|
46
|
+
}
|
|
47
|
+
const unitCode = canonicalizeUnitCode(params.unitCode);
|
|
48
|
+
if (!unitCode) {
|
|
49
|
+
throw new CrudHttpError(400, { error: "uom.unit_not_found" });
|
|
50
|
+
}
|
|
51
|
+
const entry = await findOneWithDecryption(em, DictionaryEntry, {
|
|
52
|
+
dictionary,
|
|
53
|
+
organizationId: dictionary.organizationId,
|
|
54
|
+
tenantId: dictionary.tenantId,
|
|
55
|
+
$or: [{ normalizedValue: unitCode }, { value: unitCode }],
|
|
56
|
+
});
|
|
57
|
+
if (!entry) {
|
|
58
|
+
throw new CrudHttpError(400, { error: "uom.unit_not_found" });
|
|
59
|
+
}
|
|
60
|
+
const canonical = typeof entry.value === "string" ? entry.value.trim() : "";
|
|
61
|
+
return canonical.length ? canonical : unitCode;
|
|
62
|
+
}
|
|
@@ -450,6 +450,77 @@
|
|
|
450
450
|
"nullable": true,
|
|
451
451
|
"mappedType": "text"
|
|
452
452
|
},
|
|
453
|
+
"default_sales_unit": {
|
|
454
|
+
"name": "default_sales_unit",
|
|
455
|
+
"type": "text",
|
|
456
|
+
"unsigned": false,
|
|
457
|
+
"autoincrement": false,
|
|
458
|
+
"primary": false,
|
|
459
|
+
"nullable": true,
|
|
460
|
+
"mappedType": "text"
|
|
461
|
+
},
|
|
462
|
+
"default_sales_unit_quantity": {
|
|
463
|
+
"name": "default_sales_unit_quantity",
|
|
464
|
+
"type": "numeric(18,6)",
|
|
465
|
+
"unsigned": false,
|
|
466
|
+
"autoincrement": false,
|
|
467
|
+
"primary": false,
|
|
468
|
+
"nullable": false,
|
|
469
|
+
"precision": 18,
|
|
470
|
+
"scale": 6,
|
|
471
|
+
"default": "'1'",
|
|
472
|
+
"mappedType": "decimal"
|
|
473
|
+
},
|
|
474
|
+
"uom_rounding_scale": {
|
|
475
|
+
"name": "uom_rounding_scale",
|
|
476
|
+
"type": "smallint",
|
|
477
|
+
"unsigned": false,
|
|
478
|
+
"autoincrement": false,
|
|
479
|
+
"primary": false,
|
|
480
|
+
"nullable": false,
|
|
481
|
+
"default": "4",
|
|
482
|
+
"mappedType": "smallint"
|
|
483
|
+
},
|
|
484
|
+
"uom_rounding_mode": {
|
|
485
|
+
"name": "uom_rounding_mode",
|
|
486
|
+
"type": "text",
|
|
487
|
+
"unsigned": false,
|
|
488
|
+
"autoincrement": false,
|
|
489
|
+
"primary": false,
|
|
490
|
+
"nullable": false,
|
|
491
|
+
"default": "'half_up'",
|
|
492
|
+
"mappedType": "text"
|
|
493
|
+
},
|
|
494
|
+
"unit_price_enabled": {
|
|
495
|
+
"name": "unit_price_enabled",
|
|
496
|
+
"type": "boolean",
|
|
497
|
+
"unsigned": false,
|
|
498
|
+
"autoincrement": false,
|
|
499
|
+
"primary": false,
|
|
500
|
+
"nullable": false,
|
|
501
|
+
"default": "false",
|
|
502
|
+
"mappedType": "boolean"
|
|
503
|
+
},
|
|
504
|
+
"unit_price_reference_unit": {
|
|
505
|
+
"name": "unit_price_reference_unit",
|
|
506
|
+
"type": "text",
|
|
507
|
+
"unsigned": false,
|
|
508
|
+
"autoincrement": false,
|
|
509
|
+
"primary": false,
|
|
510
|
+
"nullable": true,
|
|
511
|
+
"mappedType": "text"
|
|
512
|
+
},
|
|
513
|
+
"unit_price_base_quantity": {
|
|
514
|
+
"name": "unit_price_base_quantity",
|
|
515
|
+
"type": "numeric(18,6)",
|
|
516
|
+
"unsigned": false,
|
|
517
|
+
"autoincrement": false,
|
|
518
|
+
"primary": false,
|
|
519
|
+
"nullable": true,
|
|
520
|
+
"precision": 18,
|
|
521
|
+
"scale": 6,
|
|
522
|
+
"mappedType": "decimal"
|
|
523
|
+
},
|
|
453
524
|
"default_media_id": {
|
|
454
525
|
"name": "default_media_id",
|
|
455
526
|
"type": "uuid",
|
|
@@ -1439,6 +1510,180 @@
|
|
|
1439
1510
|
},
|
|
1440
1511
|
"nativeEnums": {}
|
|
1441
1512
|
},
|
|
1513
|
+
{
|
|
1514
|
+
"columns": {
|
|
1515
|
+
"id": {
|
|
1516
|
+
"name": "id",
|
|
1517
|
+
"type": "uuid",
|
|
1518
|
+
"unsigned": false,
|
|
1519
|
+
"autoincrement": false,
|
|
1520
|
+
"primary": false,
|
|
1521
|
+
"nullable": false,
|
|
1522
|
+
"default": "gen_random_uuid()",
|
|
1523
|
+
"mappedType": "uuid"
|
|
1524
|
+
},
|
|
1525
|
+
"product_id": {
|
|
1526
|
+
"name": "product_id",
|
|
1527
|
+
"type": "uuid",
|
|
1528
|
+
"unsigned": false,
|
|
1529
|
+
"autoincrement": false,
|
|
1530
|
+
"primary": false,
|
|
1531
|
+
"nullable": false,
|
|
1532
|
+
"mappedType": "uuid"
|
|
1533
|
+
},
|
|
1534
|
+
"organization_id": {
|
|
1535
|
+
"name": "organization_id",
|
|
1536
|
+
"type": "uuid",
|
|
1537
|
+
"unsigned": false,
|
|
1538
|
+
"autoincrement": false,
|
|
1539
|
+
"primary": false,
|
|
1540
|
+
"nullable": false,
|
|
1541
|
+
"mappedType": "uuid"
|
|
1542
|
+
},
|
|
1543
|
+
"tenant_id": {
|
|
1544
|
+
"name": "tenant_id",
|
|
1545
|
+
"type": "uuid",
|
|
1546
|
+
"unsigned": false,
|
|
1547
|
+
"autoincrement": false,
|
|
1548
|
+
"primary": false,
|
|
1549
|
+
"nullable": false,
|
|
1550
|
+
"mappedType": "uuid"
|
|
1551
|
+
},
|
|
1552
|
+
"unit_code": {
|
|
1553
|
+
"name": "unit_code",
|
|
1554
|
+
"type": "text",
|
|
1555
|
+
"unsigned": false,
|
|
1556
|
+
"autoincrement": false,
|
|
1557
|
+
"primary": false,
|
|
1558
|
+
"nullable": false,
|
|
1559
|
+
"mappedType": "text"
|
|
1560
|
+
},
|
|
1561
|
+
"to_base_factor": {
|
|
1562
|
+
"name": "to_base_factor",
|
|
1563
|
+
"type": "numeric(24,12)",
|
|
1564
|
+
"unsigned": false,
|
|
1565
|
+
"autoincrement": false,
|
|
1566
|
+
"primary": false,
|
|
1567
|
+
"nullable": false,
|
|
1568
|
+
"precision": 24,
|
|
1569
|
+
"scale": 12,
|
|
1570
|
+
"mappedType": "decimal"
|
|
1571
|
+
},
|
|
1572
|
+
"sort_order": {
|
|
1573
|
+
"name": "sort_order",
|
|
1574
|
+
"type": "int",
|
|
1575
|
+
"unsigned": false,
|
|
1576
|
+
"autoincrement": false,
|
|
1577
|
+
"primary": false,
|
|
1578
|
+
"nullable": false,
|
|
1579
|
+
"default": "0",
|
|
1580
|
+
"mappedType": "integer"
|
|
1581
|
+
},
|
|
1582
|
+
"is_active": {
|
|
1583
|
+
"name": "is_active",
|
|
1584
|
+
"type": "boolean",
|
|
1585
|
+
"unsigned": false,
|
|
1586
|
+
"autoincrement": false,
|
|
1587
|
+
"primary": false,
|
|
1588
|
+
"nullable": false,
|
|
1589
|
+
"default": "true",
|
|
1590
|
+
"mappedType": "boolean"
|
|
1591
|
+
},
|
|
1592
|
+
"metadata": {
|
|
1593
|
+
"name": "metadata",
|
|
1594
|
+
"type": "jsonb",
|
|
1595
|
+
"unsigned": false,
|
|
1596
|
+
"autoincrement": false,
|
|
1597
|
+
"primary": false,
|
|
1598
|
+
"nullable": true,
|
|
1599
|
+
"mappedType": "json"
|
|
1600
|
+
},
|
|
1601
|
+
"created_at": {
|
|
1602
|
+
"name": "created_at",
|
|
1603
|
+
"type": "timestamptz",
|
|
1604
|
+
"unsigned": false,
|
|
1605
|
+
"autoincrement": false,
|
|
1606
|
+
"primary": false,
|
|
1607
|
+
"nullable": false,
|
|
1608
|
+
"length": 6,
|
|
1609
|
+
"mappedType": "datetime"
|
|
1610
|
+
},
|
|
1611
|
+
"updated_at": {
|
|
1612
|
+
"name": "updated_at",
|
|
1613
|
+
"type": "timestamptz",
|
|
1614
|
+
"unsigned": false,
|
|
1615
|
+
"autoincrement": false,
|
|
1616
|
+
"primary": false,
|
|
1617
|
+
"nullable": false,
|
|
1618
|
+
"length": 6,
|
|
1619
|
+
"mappedType": "datetime"
|
|
1620
|
+
},
|
|
1621
|
+
"deleted_at": {
|
|
1622
|
+
"name": "deleted_at",
|
|
1623
|
+
"type": "timestamptz",
|
|
1624
|
+
"unsigned": false,
|
|
1625
|
+
"autoincrement": false,
|
|
1626
|
+
"primary": false,
|
|
1627
|
+
"nullable": true,
|
|
1628
|
+
"length": 6,
|
|
1629
|
+
"mappedType": "datetime"
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
"name": "catalog_product_unit_conversions",
|
|
1633
|
+
"schema": "public",
|
|
1634
|
+
"indexes": [
|
|
1635
|
+
{
|
|
1636
|
+
"keyName": "catalog_product_unit_conversions_scope_idx",
|
|
1637
|
+
"columnNames": [
|
|
1638
|
+
"organization_id",
|
|
1639
|
+
"tenant_id",
|
|
1640
|
+
"product_id"
|
|
1641
|
+
],
|
|
1642
|
+
"composite": true,
|
|
1643
|
+
"constraint": false,
|
|
1644
|
+
"primary": false,
|
|
1645
|
+
"unique": false
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
"keyName": "catalog_product_unit_conversions_unique",
|
|
1649
|
+
"columnNames": [
|
|
1650
|
+
"product_id",
|
|
1651
|
+
"unit_code"
|
|
1652
|
+
],
|
|
1653
|
+
"composite": true,
|
|
1654
|
+
"constraint": true,
|
|
1655
|
+
"primary": false,
|
|
1656
|
+
"unique": true
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
"keyName": "catalog_product_unit_conversions_pkey",
|
|
1660
|
+
"columnNames": [
|
|
1661
|
+
"id"
|
|
1662
|
+
],
|
|
1663
|
+
"composite": false,
|
|
1664
|
+
"constraint": true,
|
|
1665
|
+
"primary": true,
|
|
1666
|
+
"unique": true
|
|
1667
|
+
}
|
|
1668
|
+
],
|
|
1669
|
+
"checks": [],
|
|
1670
|
+
"foreignKeys": {
|
|
1671
|
+
"catalog_product_unit_conversions_product_id_foreign": {
|
|
1672
|
+
"constraintName": "catalog_product_unit_conversions_product_id_foreign",
|
|
1673
|
+
"columnNames": [
|
|
1674
|
+
"product_id"
|
|
1675
|
+
],
|
|
1676
|
+
"localTableName": "public.catalog_product_unit_conversions",
|
|
1677
|
+
"referencedColumnNames": [
|
|
1678
|
+
"id"
|
|
1679
|
+
],
|
|
1680
|
+
"referencedTableName": "public.catalog_products",
|
|
1681
|
+
"deleteRule": "cascade",
|
|
1682
|
+
"updateRule": "cascade"
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
"nativeEnums": {}
|
|
1686
|
+
},
|
|
1442
1687
|
{
|
|
1443
1688
|
"columns": {
|
|
1444
1689
|
"id": {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260218225422 extends Migration {
|
|
4
|
+
|
|
5
|
+
override async up(): Promise<void> {
|
|
6
|
+
this.addSql(`create table "catalog_product_unit_conversions" ("id" uuid not null default gen_random_uuid(), "product_id" uuid not null, "organization_id" uuid not null, "tenant_id" uuid not null, "unit_code" text not null, "to_base_factor" numeric(24,12) not null, "sort_order" int not null default 0, "is_active" boolean not null default true, "metadata" jsonb null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "catalog_product_unit_conversions_pkey" primary key ("id"));`);
|
|
7
|
+
this.addSql(`create index "catalog_product_unit_conversions_scope_idx" on "catalog_product_unit_conversions" ("organization_id", "tenant_id", "product_id");`);
|
|
8
|
+
this.addSql(`alter table "catalog_product_unit_conversions" add constraint "catalog_product_unit_conversions_unique" unique ("product_id", "unit_code");`);
|
|
9
|
+
|
|
10
|
+
this.addSql(`alter table "catalog_product_unit_conversions" add constraint "catalog_product_unit_conversions_product_id_foreign" foreign key ("product_id") references "catalog_products" ("id") on update cascade on delete cascade;`);
|
|
11
|
+
|
|
12
|
+
this.addSql(`alter table "catalog_products" add column "default_sales_unit" text null, add column "default_sales_unit_quantity" numeric(18,6) not null default '1', add column "uom_rounding_scale" smallint not null default 4, add column "uom_rounding_mode" text not null default 'half_up', add column "unit_price_enabled" boolean not null default false, add column "unit_price_reference_unit" text null, add column "unit_price_base_quantity" numeric(18,6) null;`);
|
|
13
|
+
this.addSql(`update "catalog_products" set "default_sales_unit" = "default_unit" where "default_sales_unit" is null and "default_unit" is not null;`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override async down(): Promise<void> {
|
|
17
|
+
this.addSql(`drop table if exists "catalog_product_unit_conversions" cascade;`);
|
|
18
|
+
this.addSql(`alter table "catalog_products" drop column "default_sales_unit", drop column "default_sales_unit_quantity", drop column "uom_rounding_scale", drop column "uom_rounding_mode", drop column "unit_price_enabled", drop column "unit_price_reference_unit", drop column "unit_price_base_quantity";`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
|
|
3
|
+
// Data migration: normalize legacy 'qty' unit codes to 'pc'
|
|
4
|
+
export class Migration20260219084500 extends Migration {
|
|
5
|
+
override async up(): Promise<void> {
|
|
6
|
+
this.addSql(
|
|
7
|
+
`update "catalog_products" set "default_unit" = 'pc' where lower(btrim(coalesce("default_unit", ''))) = 'qty';`,
|
|
8
|
+
);
|
|
9
|
+
this.addSql(
|
|
10
|
+
`update "catalog_products" set "default_sales_unit" = 'pc' where lower(btrim(coalesce("default_sales_unit", ''))) = 'qty';`,
|
|
11
|
+
);
|
|
12
|
+
this.addSql(
|
|
13
|
+
`update "catalog_products" set "unit_price_reference_unit" = 'pc' where lower(btrim(coalesce("unit_price_reference_unit", ''))) = 'qty';`,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
this.addSql(
|
|
17
|
+
`delete from "catalog_product_unit_conversions" as legacy where lower(btrim(coalesce(legacy."unit_code", ''))) = 'qty' and exists (select 1 from "catalog_product_unit_conversions" as canonical where canonical."product_id" = legacy."product_id" and canonical."id" <> legacy."id" and lower(btrim(coalesce(canonical."unit_code", ''))) = 'pc');`,
|
|
18
|
+
);
|
|
19
|
+
this.addSql(
|
|
20
|
+
`update "catalog_product_unit_conversions" set "unit_code" = 'pc' where lower(btrim(coalesce("unit_code", ''))) = 'qty';`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Irreversible: cannot reconstruct original 'qty' values
|
|
25
|
+
override async down(): Promise<void> {}
|
|
26
|
+
}
|
|
@@ -242,6 +242,20 @@ function buildPriceKindPresenter(
|
|
|
242
242
|
return { title, subtitle, icon: 'dollar-sign', badge: label }
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
function buildUnitConversionPresenter(
|
|
246
|
+
translate: TranslateFn,
|
|
247
|
+
record: Record<string, unknown>,
|
|
248
|
+
): SearchResultPresenter {
|
|
249
|
+
const label = translate('catalog.search.badge.unitConversion', 'Unit Conversion')
|
|
250
|
+
const unitCode = readRecordText(record, 'unit_code', 'unitCode')
|
|
251
|
+
const factor = readRecordText(record, 'to_base_factor', 'toBaseFactor')
|
|
252
|
+
const title = unitCode ? `${unitCode} (×${factor ?? '?'})` : label
|
|
253
|
+
const isActive = record.is_active ?? record.isActive
|
|
254
|
+
const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null
|
|
255
|
+
const subtitle = formatSubtitle(unitCode, statusText)
|
|
256
|
+
return { title, subtitle, icon: 'ruler', badge: label }
|
|
257
|
+
}
|
|
258
|
+
|
|
245
259
|
function buildOptionSchemaPresenter(
|
|
246
260
|
translate: TranslateFn,
|
|
247
261
|
record: Record<string, unknown>,
|
|
@@ -277,6 +291,26 @@ export const searchConfig: SearchModuleConfig = {
|
|
|
277
291
|
appendLine(lines, 'SKU', record.sku)
|
|
278
292
|
appendLine(lines, 'Handle', record.handle)
|
|
279
293
|
appendLine(lines, 'Product type', record.product_type ?? record.productType)
|
|
294
|
+
appendLine(lines, 'Base unit', record.default_unit ?? record.defaultUnit)
|
|
295
|
+
appendLine(lines, 'Default sales unit', record.default_sales_unit ?? record.defaultSalesUnit)
|
|
296
|
+
appendLine(
|
|
297
|
+
lines,
|
|
298
|
+
'Default sales quantity',
|
|
299
|
+
record.default_sales_unit_quantity ?? record.defaultSalesUnitQuantity,
|
|
300
|
+
)
|
|
301
|
+
appendLine(lines, 'UoM rounding mode', record.uom_rounding_mode ?? record.uomRoundingMode)
|
|
302
|
+
appendLine(lines, 'UoM rounding scale', record.uom_rounding_scale ?? record.uomRoundingScale)
|
|
303
|
+
appendLine(lines, 'Unit price enabled', record.unit_price_enabled ?? record.unitPriceEnabled)
|
|
304
|
+
appendLine(
|
|
305
|
+
lines,
|
|
306
|
+
'Unit price reference unit',
|
|
307
|
+
record.unit_price_reference_unit ?? record.unitPriceReferenceUnit,
|
|
308
|
+
)
|
|
309
|
+
appendLine(
|
|
310
|
+
lines,
|
|
311
|
+
'Unit price base quantity',
|
|
312
|
+
record.unit_price_base_quantity ?? record.unitPriceBaseQuantity,
|
|
313
|
+
)
|
|
280
314
|
return buildIndexSource(ctx, buildProductPresenter(translate, record), lines)
|
|
281
315
|
},
|
|
282
316
|
formatResult: async (ctx) => {
|
|
@@ -285,7 +319,17 @@ export const searchConfig: SearchModuleConfig = {
|
|
|
285
319
|
},
|
|
286
320
|
resolveUrl: async (ctx) => buildProductUrl(readRecordText(ctx.record, 'id')),
|
|
287
321
|
fieldPolicy: {
|
|
288
|
-
searchable: [
|
|
322
|
+
searchable: [
|
|
323
|
+
'title',
|
|
324
|
+
'subtitle',
|
|
325
|
+
'description',
|
|
326
|
+
'sku',
|
|
327
|
+
'handle',
|
|
328
|
+
'product_type',
|
|
329
|
+
'default_unit',
|
|
330
|
+
'default_sales_unit',
|
|
331
|
+
'unit_price_reference_unit',
|
|
332
|
+
],
|
|
289
333
|
excluded: ['metadata', 'dimensions', 'tax_rate_id'],
|
|
290
334
|
},
|
|
291
335
|
},
|
|
@@ -411,6 +455,34 @@ export const searchConfig: SearchModuleConfig = {
|
|
|
411
455
|
searchable: ['title', 'code'],
|
|
412
456
|
},
|
|
413
457
|
},
|
|
458
|
+
{
|
|
459
|
+
entityId: 'catalog:catalog_product_unit_conversion',
|
|
460
|
+
enabled: true,
|
|
461
|
+
priority: 3,
|
|
462
|
+
buildSource: async (ctx) => {
|
|
463
|
+
const { t: translate } = await resolveTranslations()
|
|
464
|
+
const record = ctx.record
|
|
465
|
+
const lines: string[] = []
|
|
466
|
+
appendLine(lines, 'Unit code', record.unit_code ?? record.unitCode)
|
|
467
|
+
appendLine(lines, 'To base factor', record.to_base_factor ?? record.toBaseFactor)
|
|
468
|
+
appendLine(lines, 'Sort order', record.sort_order ?? record.sortOrder)
|
|
469
|
+
const isActive = record.is_active ?? record.isActive
|
|
470
|
+
appendLine(lines, 'Active', isActive)
|
|
471
|
+
return buildIndexSource(ctx, buildUnitConversionPresenter(translate, record), lines)
|
|
472
|
+
},
|
|
473
|
+
formatResult: async (ctx) => {
|
|
474
|
+
const { t: translate } = await resolveTranslations()
|
|
475
|
+
return buildUnitConversionPresenter(translate, ctx.record)
|
|
476
|
+
},
|
|
477
|
+
resolveUrl: async (ctx) => {
|
|
478
|
+
const productId = resolveProductId(ctx.record)
|
|
479
|
+
return buildProductUrl(productId)
|
|
480
|
+
},
|
|
481
|
+
fieldPolicy: {
|
|
482
|
+
searchable: ['unit_code'],
|
|
483
|
+
excluded: ['metadata'],
|
|
484
|
+
},
|
|
485
|
+
},
|
|
414
486
|
{
|
|
415
487
|
entityId: 'catalog:catalog_option_schema_template',
|
|
416
488
|
enabled: true,
|