Skip to main content

Pricing Module Data Import — Implementation Summary

Date: 2026-02-27
Status: ✅ Complete


Overview​

Added bulk import support for the pricing module tables: price_list, price_list_scope, and price_list_item. This allows users to import pricing data from Excel or CSV files through the Data Import Center.


What Was Implemented​

Backend (NestJS)​

1. Import Handlers​

Three new handlers following the ImportTypeHandler interface:

PriceListImportHandler (apps/backend/src/data-import/application/handlers/price-list-import.handler.ts)

  • Validates: name (required), currency (required), kind, channel, priority, date range
  • Resolves: currency by code/name (must be linked to business)
  • Upserts by: businessId + name
  • Defaults: kind=both, channel=pos, priority=100, isActive=true

PriceListScopeImportHandler (apps/backend/src/data-import/application/handlers/price-list-scope-import.handler.ts)

  • Validates: priceListName (required), channel (required)
  • Resolves: price list by name, location by name (optional, null = global)
  • Upserts by: businessId + priceListId + locationId + channel
  • Handles unique default constraint: only one default scope per business + location + channel
  • Defaults: isDefault=false, isActive=true

PriceListItemImportHandler (apps/backend/src/data-import/application/handlers/price-list-item-import.handler.ts)

  • Validates: priceListName (required), itemType (required), itemIdentifier (required), unitPrice (required)
  • Resolves:
    • Price list by name
    • Product by SKU (itemType=product)
    • Service by name (itemType=service)
    • Modifier by name (itemType=modifier)
  • Converts decimal price to minor units (e.g., 19.99 USD → 1999 cents)
  • Upserts by: priceListId + itemType + productId/serviceId/modifierId + minQty + validFrom + validTo
  • Defaults: minQty=1, priority=0, isActive=true

2. Module Registration​

Updated apps/backend/src/data-import/data-import.module.ts:

  • Added handler imports
  • Added handlers to providers array
  • Registered handlers in ImportTypeRegistry:
    • price_list → PriceListImportHandler
    • price_list_scope → PriceListScopeImportHandler
    • price_list_item → PriceListItemImportHandler

3. Template Schemas​

Updated apps/backend/src/data-import/application/template-schema.service.ts:

  • Added price_list schema (9 columns)
  • Added price_list_scope schema (5 columns)
  • Added price_list_item schema (9 columns)

4. Unit Tests​

Three test suites with comprehensive coverage:

  • price-list-import.handler.spec.ts (validation, create, update, currency resolution)
  • price-list-scope-import.handler.spec.ts (validation, create, update, default constraint)
  • price-list-item-import.handler.spec.ts (validation, create, update, multi-type resolution, price conversion)

Frontend (React PWA)​

1. Import Type Schemas​

Updated apps/frontend-pwa/src/components/forms/data-import/import-type-schemas.ts:

  • Added labels: "Price Lists", "Price List Scopes", "Price List Items"
  • Added schemas with headers and required fields

2. Dashboard Cards​

Updated apps/frontend-pwa/src/components/forms/data-import/ImportCenterDashboardPage.tsx:

  • Added icons: DollarSign, MapPinned, Receipt
  • Added three cards to IMPORT_TYPES array

3. Menu Entries​

Updated config/firebase/remote-config/pwaMenu.json:

  • Added "Price Lists" menu item (order 13)
  • Added "Price List Scopes" menu item (order 14)
  • Added "Price List Items" menu item (order 15)

4. i18n Translations​

Updated apps/frontend-pwa/src/i18n/locales/en.json:

  • priceLists: "Price Lists"
  • priceListScopes: "Price List Scopes"
  • priceListItems: "Price List Items"

Updated apps/frontend-pwa/src/i18n/locales/es.json:

  • priceLists: "Listas de precios"
  • priceListScopes: "Alcances de lista de precios"
  • priceListItems: "Artículos de lista de precios"

Documentation​

1. User Guide​

Updated docs/data-import/user-guide.md:

  • Added pricing to bulk-load capabilities
  • Added three new rows to "Import Types and Required Fields" table
  • Updated references section

2. API Documentation​

Updated docs/data-import/data-import-postman-curls.md:

  • Added price_list, price_list_scope, price_list_item to importType enum
  • Added three new rows to "Import type schemas" table

Import Dependencies​

Price List​

Prerequisites:

  • Currency (must be linked to business)

Import order: Can be imported anytime after currencies are set up


Price List Scope​

Prerequisites:

  • Price List (must exist in same business)
  • Location (optional, for location-specific scopes)

Import order: After price lists


Price List Item​

Prerequisites:

  • Price List (must exist in same business)
  • Product (for itemType=product)
  • Service (for itemType=service)
  • Product Modifier (for itemType=modifier)

Import order: After price lists and products/services/modifiers


Column Reference​

price_list​

ColumnRequiredTypeNotes
name✓stringUnique per business
currency✓stringCurrency code (GTQ, USD, etc.)
kindenumretail, restaurant, both (default: both)
channelenumpos, online, dine_in, takeaway, delivery, kiosk, wholesale (default: pos)
priorityinteger0-1000 (default: 100)
validFromISO dateOptional validity start
validToISO dateOptional validity end (must be after validFrom)
isActivebooleanDefault: true
notestextOptional notes

price_list_scope​

ColumnRequiredTypeNotes
priceListName✓stringPrice list name (must exist)
locationNamestringLocation name (null = global scope)
channel✓enumpos, online, dine_in, takeaway, delivery, kiosk, wholesale
isDefaultbooleanOnly one default per business + location + channel (default: false)
isActivebooleanDefault: true

price_list_item​

ColumnRequiredTypeNotes
priceListName✓stringPrice list name (must exist)
itemType✓enumproduct, service, modifier
itemIdentifier✓stringSKU (product), name (service/modifier)
unitPrice✓decimalPrice in currency units (converted to minor units)
minQtydecimalMinimum quantity for tier pricing (default: 1)
priorityintegerPriority for overlapping rules (default: 0)
validFromISO dateOptional validity start
validToISO dateOptional validity end (must be after validFrom)
isActivebooleanDefault: true

Example Import Files​

price_list.xlsx​

namecurrencykindchannelpriorityvalidFromvalidToisActivenotes
Weekend SpecialUSDrestaurantdine_in2002026-01-012026-12-31TRUEWeekend pricing
Retail StandardGTQretailpos100TRUEStandard retail prices

price_list_scope.xlsx​

priceListNamelocationNamechannelisDefaultisActive
Weekend SpecialStore 1dine_inTRUETRUE
Weekend SpecialonlineFALSETRUE
Retail StandardposTRUETRUE

price_list_item.xlsx​

priceListNameitemTypeitemIdentifierunitPriceminQtypriorityvalidFromvalidToisActive
Weekend SpecialproductSKU12319.9910TRUE
Weekend SpecialproductSKU12317.99100TRUE
Weekend SpecialserviceHaircut25.0010TRUE
Retail StandardmodifierExtra Cheese2.5010TRUE

Testing Checklist​

  • Backend handlers created
  • Handlers registered in module
  • Template schemas added
  • Frontend schemas added
  • Dashboard cards added
  • Menu entries added
  • i18n translations added
  • Documentation updated
  • Unit tests written
  • Linting passed

Manual Testing (TODO)​

  • Download template for each import type
  • Upload valid file → verify success
  • Upload file with missing required field → verify error
  • Upload file with invalid enum → verify error
  • Upload file with invalid currency → verify error
  • Upload file with invalid price list reference → verify error
  • Upload file with invalid product/service/modifier → verify error
  • Re-upload same file → verify upsert (no duplicates)
  • Verify price conversion to minor units
  • Verify default scope constraint (only one default per business + location + channel)
  • Verify frontend wizard shows all target fields
  • Verify import history shows pricing imports

API Endpoints​

All pricing import types use the standard Data Import API:

  • GET /data-import/templates/price_list - Download template
  • GET /data-import/templates/price_list_scope - Download template
  • GET /data-import/templates/price_list_item - Download template
  • POST /data-import/upload - Upload file (importType: price_list | price_list_scope | price_list_item)
  • GET /data-import/jobs - List jobs
  • GET /data-import/jobs/:id - Get job status
  • GET /data-import/jobs/:id/errors - Get error rows

Notes​

  1. Money Conversion: The price_list_item handler converts decimal prices to minor units based on the currency's minorUnit field (e.g., 19.99 USD with minorUnit=2 → 1999 cents). Both unit_price_minor (integer, authoritative) and unit_price (decimal, display) are stored.

  2. Unique Default Constraint: The price_list_scope handler enforces that only one scope can be marked as default per business + location + channel. When setting a new default, it automatically unsets the existing default.

  3. Upsert Semantics: All handlers use upsert logic (update if exists, create if not) to make re-imports safe and idempotent.

  4. Import Order: Users should import in this order:

    1. Price Lists (requires currencies)
    2. Price List Scopes (requires price lists, optionally locations)
    3. Price List Items (requires price lists and products/services/modifiers)
  5. Global vs Location Scopes: When locationName is empty/null in price_list_scope, the scope applies globally (all locations). Otherwise, it applies only to the specified location.


Files Changed​

Backend​

  • apps/backend/src/data-import/application/handlers/price-list-import.handler.ts (new)
  • apps/backend/src/data-import/application/handlers/price-list-scope-import.handler.ts (new)
  • apps/backend/src/data-import/application/handlers/price-list-item-import.handler.ts (new)
  • apps/backend/src/data-import/application/handlers/__tests__/price-list-import.handler.spec.ts (new)
  • apps/backend/src/data-import/application/handlers/__tests__/price-list-scope-import.handler.spec.ts (new)
  • apps/backend/src/data-import/application/handlers/__tests__/price-list-item-import.handler.spec.ts (new)
  • apps/backend/src/data-import/data-import.module.ts (modified)
  • apps/backend/src/data-import/application/template-schema.service.ts (modified)

Frontend​

  • apps/frontend-pwa/src/components/forms/data-import/import-type-schemas.ts (modified)
  • apps/frontend-pwa/src/components/forms/data-import/ImportCenterDashboardPage.tsx (modified)
  • apps/frontend-pwa/src/i18n/locales/en.json (modified)
  • apps/frontend-pwa/src/i18n/locales/es.json (modified)
  • config/firebase/remote-config/pwaMenu.json (modified)

Documentation​

  • docs/data-import/user-guide.md (modified)
  • docs/data-import/data-import-postman-curls.md (modified)
  • docs/data-import/pricing-import-implementation.md (new)

Next Steps​

  1. Manual Testing: Follow the testing checklist above
  2. Database Migration: Ensure the pricing tables migration (2026-02-27t04:29:09.000z-pricing-tables.mjs) is applied to all environments
  3. Deployment: Deploy backend and frontend changes together
  4. User Training: Update user documentation with pricing import examples
  5. Monitoring: Watch for import errors in Sentry after deployment

Support​

For issues or questions:

  • Backend: Check handler logs in apps/backend/src/data-import/
  • Frontend: Check Import Center at /imports
  • API: Test with Postman using curls in docs/data-import/data-import-postman-curls.md