Appearance
New Product Prices (ERP Price Import)
Flow ID: AD-43 | Module(s): eshop | Complexity: Medium Last Updated: 2026-04-04
Business Context
The "New Product Prices" feature manages incoming price updates from external ERP systems. When an ERP pushes new retail prices (with VAT) and wholesale prices for products, they are staged in a new_product_prices holding table. Administrators then review these pending prices and can selectively import them one at a time or all at once.
The import process recalculates the net price (without VAT) from the provided price-with-VAT using the product's VAT rate, then updates the main shop_product table. This ensures price accuracy through the ERP-to-eshop pipeline while giving administrators control over when prices go live.
API Reference
REST Endpoints
No REST API. Price imports are managed through the legacy admin panel.
Legacy Admin Routes
| Route | Controller | Method | HTTP | Description |
|---|---|---|---|---|
eshop/new_product_prices | Adv_new_product_prices | index | GET | List pending price updates |
eshop/new_product_prices/updatePrice/{id} | Adv_new_product_prices | updatePrice | GET | Import a single price update |
eshop/new_product_prices/updateAllPrices | Adv_new_product_prices | updateAllPrices | GET | Import all pending price updates |
eshop/new_product_prices/delete/{id} | Adv_new_product_prices | delete | GET | Delete a single pending record |
eshop/new_product_prices/deleteAll | Adv_new_product_prices | deleteAll | GET | Delete all pending records |
Code Flow
Listing Pending Prices
- Admin navigates to
eshop/new_product_prices. index()callsnew_product_prices_model::getNewProductPriceList()which queriesnew_product_pricesordered byinsert_date DESC, excluding imported records (is_imported IS NULL OR is_imported = 0).- For each record, loads the product's MUI names and barcodes for display.
- Renders the
admin/products/new_product_prices_listview with records, MUI data, and barcodes.
Importing a Single Price
- Admin clicks "Import" on a specific record. GET to
updatePrice/{id}. - Calls
new_product_prices_model::updateNewProductPriceFromErp($id, $successMsg, $errorMsg). - The import process: a. Fetches the staged record from
new_product_pricesby ID. b. Fetches the correspondingshop_productmaster record. c. Loads VAT rates fromvatstable. d. Calculates net price:price = price_with_vat - (price_with_vat * vat_rate / (100 + vat_rate)), rounded to 2 decimals. e. Builds update data:price(net) andwholesale_price. Zero values are excluded. f. Duplicate check: If the calculated values already match the current product prices, skips the DB update (workaround foraffected_rows()returning 0 on no-change updates). g. Callsupdate_master_record()to updateshop_product. h. Marks the staged record as imported:is_imported = true,update_date = now(). - Calls
afterUpdatePrice($id)hook (empty by default, overridable by client repos). - Sets success/error flash messages and redirects back to the list.
Importing All Prices
- Admin clicks "Import All". GET to
updateAllPrices. - Fetches all non-imported records from
new_product_prices. - Passes all IDs to
updateNewProductPriceFromErp()which processes each sequentially. - Calls
afterUpdateAllPrices($ids)hook. - Summary messages: "X records updated successfully" / "Y records failed" with per-product error details.
Deleting Records
delete($id): Deletes a single staged record vianew_product_prices_model::deleteRecord($id). CallsafterDelete($id)hook.deleteAll(): Deletes all non-imported records directly via DB query. CallsafterDeleteAll()hook.
Domain Layer
No modern domain layer. The model extends Product_model (not Adv_base_model) to reuse product query methods like getMasterRecordsWhereIn() and update_master_record().
Architecture
| Component | Path | Purpose |
|---|---|---|
Adv_new_product_prices | ecommercen/eshop/controllers/Adv_new_product_prices.php | Admin controller (177 lines) |
Adv_new_product_prices_model | ecommercen/eshop/models/Adv_new_product_prices_model.php | Model extending Product_model (161 lines) |
New_product_prices_model | application/modules/eshop/models/New_product_prices_model.php | App-level wrapper |
vats_model | ecommercen/eshop/models/ | VAT rates for price calculation |
product_model | ecommercen/eshop/models/ | Parent model for product updates |
| Routes | application/config/routes.php:409-412 | Route definitions |
Data Model
new_product_prices
| Column | Type | Description |
|---|---|---|
id | int (PK) | Auto-increment primary key |
product_id | int (FK) | Target product ID |
price_with_vat | decimal | New retail price including VAT (from ERP) |
wholesale_price | decimal | New wholesale price (from ERP) |
is_imported | tinyint/bool | Whether the price has been applied (null/0 = pending, 1 = imported) |
insert_date | datetime | When the ERP pushed the record |
update_date | datetime | When the price was imported to shop_product |
Affected Table: shop_product
| Column | Updated By | Description |
|---|---|---|
price | Calculated | Net price (without VAT), derived from price_with_vat |
wholesale_price | Direct copy | Wholesale price from ERP |
Configuration
Required roles: AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS.
No registry keys. The feature depends on external ERP integration to populate the new_product_prices table.
Client Extension Points
- Override controller: Extend in
application/modules/eshop/controllers/to customize the admin interface. - Hook methods: Four protected hook methods can be overridden in client controllers:
afterUpdatePrice($id)-- after single price importafterUpdateAllPrices($ids)-- after bulk price importafterDelete($id)-- after single record deletionafterDeleteAll()-- after bulk deletion
- Override model: Extend
New_product_prices_modelto customize the price calculation formula or add additional fields to the update. - ERP integration: The
new_product_pricestable is populated by external processes (ERP sync jobs, API imports). Client repos typically have custom ERP integrations that write to this table.
Business Rules
- VAT recalculation: Net price is derived from
price_with_vatusing the formula:price = price_with_vat - (price_with_vat * vat / (100 + vat)). - Zero exclusion: If the calculated net price or wholesale price is 0, that field is excluded from the update.
- Idempotent import: If the calculated values already match the current
shop_productvalues, the record is still marked as imported without issuing an UPDATE query. - Staging table pattern: Prices are staged in
new_product_pricesbefore going live, giving admins review and approval control. - Per-record tracking: Each staged record tracks its import status and timestamp independently.
- Model inheritance: The model extends
Product_modelrather thanAdv_base_model, giving it direct access to product table helpers.
Related Flows
- AD-02 Product Management Admin -- Product admin page shows current prices
- SY-04 Price Tracking -- Price history tracking may record changes from imports
- SY-17 Bulk Product Import -- Alternative product data import mechanism