Pricebands allow multiple pricing schedules per product, so that a "list" price can be maintained, but also additional pricing such as "trade" or "staff" or "Customer123". Pricebands is one of very first pricing options that was added to the POS engine and is used by a wide cross section of retailers. Some parts of the design are not how it would be built today, but it is stable overall and maintaining stability is critical for this component.
Concepts
A price band has a definition and a name. Some price bands are simple mapping to database columns, others are calculated prices that only typically have a definition
General Operation
Pricebands are located by name. From the name, the POS fetches settings PriceBandnn. Description until it finds a matching entry. This provides the internal Priceband number. NN starts at 0 and no gaps can exist in settings. eg You cannot skip from PriceBand5.Description to PriceBand7.Description You cannot define more than 200 pricebands. The number NN in the setting name is not related to the pricebands themselves, it is only used to connect PriceBandNN.Control to PriceBandNN.Description
Price adjustment by pricebands is done in PosProduct::SetPriceBand2() for each product individually
- The function starts by being passed a Product/Saleline and a priceband/Sale to apply
- If the Price of the product has already been set (hard), price banding is skipped
- If the product is not an inventory item, price banding is skipped. You cannot apply price bands to modifiers for example.
- If the table 'pricebands' exists in the database, the priceband details are loaded from there. Using a database table is rare and not advised.
- Otherwise the setting PriceBandNN.Control value is decoded.
- Valid syntax options for defining pricebands are (not all are used):
- formula(N)
- column(N)
- condition(N)
- default(M)
- zero(N)
- allowed(N)
- nodiscount
Either formula() or column() must be present, others are optional
- If the nodiscount flag was present, the product record is marked "PriceBandNoDiscount" regardless of any other priceband outcome.
- The products.unitprice is loaded and saved as PrivateDiskPrice
- If a sale exists and it has a default priceband, it is checked to see if that priceband replaces the PrivateDiskPrice
- WorkingPrice is set to UserPrice (which is currently PrivateDiskPrice)
- If the priceband is formula based:
- If allowed(n) is used, the value of N is fetched. If the result of this fetch is zero, the priceband does not apply
- If notallowed(n) is used, the value of N is fetched. If the result of this fetch is NOT zero, the priceband does not apply
- If the priceband is permitted, the internal calculator is used to process the formula, and WorkPrice is set to the result
- If the priceband is column based:
- WorkPrice is set to the value of the column
- If WorkPrice is $0, and zero(N) is used then:
- If N is "costprice", the price is set to costprice for the product and the function exits.
- If N is "unitprice", the function exits as pricing is already unitprice
- Otherwise, this whole function is recursively called with N as the name of the priceband to apply.
Examples
PriceBand0.Control column(PriceBand2) zero(unitprice) PriceBand0.Description SOHU
This example creates a priceband "SOHU". The pricing is taken from products.Priceband2, unless that price is $0 in which case products.unitprice is used.
PriceBand1.Control formula(unitprice*0.90) allowed(BandA) PriceBand1.Description 10% Off
This example creates a priceband called "10% Off", which grants a 10% reduction in unitprice but ONLY if the products.BandA column is not zero.
Selecting Pricebands
- When a new sale is created a default priceband is loaded from
- if locations.priceband exists this value is used, even if empty
- If the sale location is the current location, the setting SaleDefaultPriceBand is used.
- If a sale is being recreated and the field sale.lastpriceband exists, this value is used as the priceband
- When a customer is selected to a sale, and the field customer.priceband exists, this is used as the default sale priceband.
- When a new item is added to the sale, if the table pricebandmap exists the POS attempts to match
the best priceband using an SQL query of
select ... from pricebandmap where pidstart <= PID and pidend >= PID and deptstart <= DEPID and deptend >= DEPID and customerstart <= CID and customerend >= CID and locationstart <= LOCID and locationend >= LOCID order by priority desc
- For each row the following is performed until the first match
- The DOW mask is checked
- The timestart and timeend are checked against the current time.