Sales Discount Rules
Guide for creating sales price, item discount, and invoice discount rules with formulas evaluated by ExpressionCalc.
Menu Route
General Rules
/form/salesdiscrules
When to Use This Form
Use this form to create master rules for sales discounts. The rule does not create a transaction by itself; it is read by sales transactions when the selected customer or product has the related rule.
Item Sales Price
Change line-item selling price based on product, customer, salesperson, unit, quantity, or temporary subtotal.
Item Discount
Apply percent or value discount per unit in Sales, Sales Order, POS, Table Order, and Shop.
Invoice Discount
Apply header-level percent or value discount based on sales gross amount and summaries by brand, supplier, factory, or product group.
This rule is only for sales pricing and discounts. Payment discounts, product rewards, and point rewards use separate rule forms.
System Overview
- Users create the rule in
/form/salesdiscrules. - Product-category rules are attached to Product master data. Customer master data must also contain the rule list so item price or item discount calculation is called from transactions.
- Transaction-category rules are attached to Customer master data and are used to calculate invoice-level discounts.
- When a transaction changes, the frontend calls
POST /salespriceformulafor item detail andPOST /salesinvoiceformulafor header discount. - The backend reads formulas from
salesdiscrules, passes variables intoExpressionCalc, and returns the result to the transaction form.
If several active rules of the same kind are evaluated, the last evaluated value can overwrite the previous value. Use one final rule per need, or combine tiered logic in one formula.
Before Creating a Rule
Requirement
The
discount_management package is active so rule fields are visible in Customer and Product master data.Requirement
Product, Customer, Salesperson, and product units are correct because those fields become formula variables.
Requirement
Sales price, default discount, and quantity-based prices in Units are prepared when they will be used in formulas.
Requirement
The user writing the formula understands that the formula result must be numeric: a price, percent discount, or value discount.
Workflow
- Open General / Rules / Sales Discount Rules.
- Click Add to create a new rule.
- Fill in Code, Description, and Display Name.
- Choose Category: Product for line-item rules, or Transaction for invoice-level rules.
- Choose Kind: Sales Price, Percent Disc, or Value Disc.
- Use the Variables field to copy a valid variable name.
- Use the Functions field when the formula needs helpers such as
roundorstackeddiscount. - Write the formula in Formula. It must return one value.
- Save the rule.
- Attach the rule to Product or Customer according to its category, then test it in Sales, Sales Order, POS, Table Order, or Shop.
Form Fields
| Field | Required | Explanation |
|---|---|---|
Code (id) | Yes | Numeric rule code. This code is stored in the salesdiscrules JSON field in Customer or Product. |
Description (description) | Yes | Long rule name. Use a name that explains the condition and result, for example "Wholesale discount 12 pcs". |
Display Name (displayname) | Yes | Short name with a maximum of 20 characters for compact display. |
Category (category) | Yes | Product calculates item values. Transaction calculates invoice discount. |
Kind (kind) | Yes | Sales Price returns a new selling price. Percent Disc returns a percent discount. Value Disc returns a value discount. |
Formula (formula) | Yes | Calculation expression evaluated by ExpressionCalc. The editor looks like JavaScript, but backend evaluation uses the PHP expression parser. |
Variables (variables) | No | Autocomplete list from vw_salesdiscrules. Selecting a variable copies it to the clipboard. |
Functions (functions) | No | Formula helper list. Selecting a function copies a template to the clipboard. |
| Audit fields | No | usercreate, useredit, and updatetimestamp are filled automatically. |
Attaching the Rule to Master Data
Attach Product-category rules to Product when the rule should react to a specific item. The same rule should also be allowed for the Customer so the transaction can evaluate it.
Attach Product-category rules for item price or item discount, and Transaction-category rules for invoice discount. Customer rules control which formulas are available during sales entry.
How the Formula Works
- The transaction prepares variables from the selected product, customer, salesperson, unit, quantity, price, discount, tax, and transaction summary.
- The backend loads the rule formula and evaluates it through
ExpressionCalc. - For Sales Price, the result becomes the item selling price.
- For Percent Disc, the result becomes item or invoice percent discount.
- For Value Disc, the result becomes item or invoice value discount.
- If the formula is invalid, the transaction should be tested again after the formula is corrected.
Formula result must be numeric.
Use Product category for item price or item discount.
Use Transaction category for invoice discount.
Avoid writing fixed dates or fixed customer IDs unless the promotion is intentionally limited.
Available Variables
| Variable Group | Examples | Notes |
|---|---|---|
| Product and unit | productid, productgroup, brand, unit | Used for product-specific price or discount rules. |
| Customer and salesperson | customerid, customergroup, salesman | Used for customer-level or salesperson-level promotions. |
| Quantity and price | qty, salesprice, subtotal | Used for tiered quantity discounts or price changes. |
| Invoice summary | grossamount, brandgross, suppliergross | Used for transaction-level invoice discount formulas. |
Use the variable selector in the form to copy valid variable names. Variable names are case-sensitive.
Functions and Formula Syntax
| Function | Example | Use |
|---|---|---|
round | round(grossamount / 1000) * 1000 | Round a calculated value. |
floor | floor(qty / 10) | Return a whole number, commonly used for buy X get Y rules. |
ceil | ceil(qty / 12) | Round up when a minimum package should count as one. |
min / max | min(10, percentdisc) | Limit a result to a safe range. |
stackeddiscount | stackeddiscount(10, 5) | Calculate stacked discount logic when it is enabled in the expression engine. |
Write one expression that returns one value. Avoid multiple statements. Test the formula with realistic transaction data before using it in daily operations.
Formula Examples
| Need | Example Formula | Result |
|---|---|---|
| 10% item discount when quantity reaches 12 | qty >= 12 ? 10 : 0 | Percent Disc returns 10 or 0. |
| Special price for high quantity | qty >= 24 ? 95000 : salesprice | Sales Price returns a replacement selling price. |
| Invoice discount by gross amount | grossamount >= 1000000 ? 5 : 0 | Transaction Percent Disc returns 5%. |
| Rounded value discount | round(grossamount * 0.02) | Value Disc returns 2% of gross amount rounded. |
Testing Checklist
- Test with a customer that has the rule attached.
- Test with a product that has the rule attached when the category is Product.
- Test below and above the promotion threshold.
- Check Sales, Sales Order, POS, Table Order, or Shop depending on where the rule will be used.
- Check whether multiple active rules overwrite each other.
Common Issues
- Rule does not run: check whether the rule is attached to Customer and, for Product rules, to Product.
- Wrong discount: check Category and Kind first.
- Formula returns empty or error: check variable names, syntax, and whether the variable exists in the selected transaction context.
- Invoice discount does not change: make sure the rule category is Transaction and it is attached to Customer.