Sales Discount Rules

Guide to creating selling price, item discount, and sales invoice discount rules with formulas executed by ExpressionCalc.

Menu Route

Settings Business Rules

/form/salesdiscrules

When to Use This Form

This form is used to create the sales discount rule master data. Rules do not create transactions directly; rules are read by sales transactions when the selected product or customer has a related rule.

Item Selling Price
Changes the selling price of an item line based on product, customer, salesperson, unit, quantity, or temporary subtotal.
Item Discount
Gives a percent or amount discount per unit in the details of Sales, Sales Order, POS, Order via QR Code, and Shop.
Invoice Discount
Gives a percent or amount discount at transaction header level based on the sales gross and the summary by brand, supplier, factory, or product group.
These rules are for sales only. Payment discounts, product rewards, and point rewards use other rule forms.

System Overview

  1. Users create rules in /form/salesdiscrules.
  2. Product category rules are attached to the Product master data. The Customer master data must also have a rule list so the item price/discount calculation is called from the transaction screen.
  3. Transaction category rules are attached to the Customer master data to calculate invoice discounts.
  4. When the transaction changes, the frontend calls POST /salespriceformula for item details and POST /salesinvoiceformula for the header discount.
  5. The backend takes the formula from the table salesdiscrules, puts the variables into ExpressionCalc, then the result is sent back to the transaction form.
If several rules of the same type are active, the last value evaluated overwrites the previous value. Create one final rule for each need, or combine tiered logic in one formula.

Prerequisites Before Creating Rules

Package discount_management is active so the rule field appears in the Customer and Product master data.
Product, Customer, Salesperson, and product units are correct because those fields are the source of formula variables.
Selling price, default discount, and quantity-based price in the Units master data have been filled in if they will be used in formulas.
The user writing the formula understands that the formula result must be a number for the price or discount.

Entry Flow

  1. Open the menu Settings / Business Rules / Sales Discount Rules.
  2. Click Add for a new rule.
  3. Fill in the Code, Description, and Display Name.
  4. Select the Category: Product for item line rules, or Transaction for invoice discount rules.
  5. Select the Type: Sales Price, Percent Disc, or Value Disc.
  6. Use the available Variables field to copy valid variable names to the clipboard.
  7. Use the available Functions field if you need functions such as round or stackeddiscount.
  8. Write the formula in the Formula editor. The formula must return one value, not several statements.
  9. Save the rule.
  10. Attach the rule to the Product or Customer according to its category, then test it in a Sales, Sales Order, POS, Order via QR Code, or Shop transaction.

Form Fields

Field Required Explanation
Code (id) Yes Numeric rule code. This code is stored in the JSON field salesdiscrules on the Customer or Product.
Description (description) Yes Long rule name. Use a name that explains the rule's condition and result, for example "Wholesale discount 12 pcs".
Display Name (displayname) Yes Short name of at most 20 characters. Used for compact display.
Category (category) Yes Product for item calculations. Transaction for invoice discount calculations.
Type (kind) Yes Sales Price produces a new selling price. Percent Disc produces a percent discount. Value Disc produces an amount discount.
Formula (formula) Yes Calculation expression evaluated by ExpressionCalc. The editor uses JavaScript mode, but the backend evaluator uses the PHPExpression parser.
Variables (variables) No Variable autocomplete from vw_salesdiscrules. When selected, the variable value is copied to the clipboard.
Functions (functions) No List of formula functions. When selected, the function template is copied to the clipboard.
Audit (usercreate, useredit, updatetimestamp) No Automatic audit fields.
This form has no detail table. All of a rule's effect comes from the rule header fields and its formula.

Attaching Rules to Master Data

In the Product master data, the Sales Disc field takes its options from salesdiscrules with category=0. Product category rules are used by SalesPriceFormula.

On the transaction screen, the item formula is called if the selected product has a rule and the selected customer also has a sales discount rule list. The current backend implementation evaluates the rule list from the Product for item calculations.

In the Customer master data, the Sales Disc field can store sales discount rules. Transaction category rules are used by SalesInvoiceFormula for invoice discounts.

If the Customer has no rule, the system does not call the invoice discount formula and uses the manual discount input in the transaction.

The rule field also exists in Customer Group and Product Group. When customer or product data uses a particular group, the group can become the default rule source according to the master data process in the application.

How Formulas Work in the Backend

Product Category

Endpoint POST /salespriceformula receives customer, salesman, product, unit, detail, and salestotal. The backend takes rules from product.salesdiscrules. The rule result is sent as salesprice, percentdisc, or valuedisc.

Type Effect in the transaction Notes
Sales Price Overrides the item selling price. A result of 0 means the price becomes 0 in the new Shop/POS. In legacy forms, an empty or 0 result does not override the price.
Percent Disc Overrides the item discount percent, then the discount amount is calculated from percent x price x qty. Use a percent figure, for example 5 for 5 percent.
Value Disc Overrides the discount amount per unit, then multiplied by the transaction quantity. Do not return the total discount of a line unless you really want it multiplied by the quantity again.

Transaction Category

Endpoint POST /salesinvoiceformula reads rules from customer.salesdiscrules. The backend only uses Transaction category rules of type Percent Disc or Value Disc.

Type Effect in the transaction Notes
Sales Price Not used for the Transaction category. Use this type only for the Product category.
Percent Disc Overrides the invoice discount percent, then the discount amount is calculated from percent x salesgross. A result of 5 means a 5 percent invoice discount.
Value Disc Overrides the invoice discount amount. If a value disc is returned, the discount percent is recalculated from value disc / salesgross.

Available Variables

The variable list is taken from the view vw_salesdiscrules. Variable names follow the database column names, so the examples below are the main patterns; the full list can be searched through the available Variables field on the form.

Product Category
  • product.[kolom], for example product.productgroup
  • customer.[kolom], for example customer.pricelevel
  • salesman.[kolom], for example salesman.id
  • unit.[kolom], for example unit.salesprice1, unit.salesdisc
  • detail.[kolom], for example detail.salesqty, detail.price
  • salestotal, the current transaction subtotal
Transaction Category
  • customer.[kolom], for example customer.customergroup
  • salesman.[kolom], for example salesman.id
  • sales.[kolom], for example sales.division
  • salesgross, total before invoice discount
  • brandsummary["BRAND01"], total net amount for a particular brand
  • suppliersummary["S001"], total net amount for a particular supplier
  • factorysummary["F001"], total net amount for a particular factory
  • productgroupsummary["PG001"], total net amount for a particular product group
The autocomplete view shows the pattern detail.[kolom] for the Transaction category, but the current SalesInvoiceFormula implementation does not pass the variable detail to the evaluator. For invoice rules, use salesgross or the summary arrays.

Functions and Formula Syntax

ExpressionCalc uses the parser PHPExpression. A formula is written as a single expression and can use arithmetic, comparison, and logical operators, ternaries, property access with a dot, and array access with brackets.

Need Syntax
Arithmetic +, -, *, /, %, **
Comparison ==, !=, ===, !==, >, >=, <, <=
Logic &&, ||, !
Condition kondisi ? nilai_jika_benar : nilai_jika_salah
Property customer.customergroup, unit.salesprice1, detail.salesqty
Summary array brandsummary["BRAND01"], suppliersummary["S001"]
String "GROSIR" or 'GROSIR'
Number Functions
round, floor, ceil, abs, is_infinite, is_nan, is_finite
Text and Array Functions
trim, ltrim, rtrim, str_replace, implode, join, explode, isset
Current Time
currentday, currentmonth, currentyear, currenthour, currentminute, currentsecond
Tiered Discount
stackeddiscount(30, 40, 50) calculates stacked discounts, not the sum of 30 + 40 + 50.

Formula Examples

Use Category Product and Type Sales Price.

detail.salesqty >= 12 ? unit.salesdiscprice1 : unit.salesprice1

If the quantity is at least 12, the price uses salesdiscprice1; otherwise it uses selling price level 1.

Use Category Product and Type Percent Disc.

customer.customergroup == "GROSIR" ? stackeddiscount(unit.salesdisc, 5) : unit.salesdisc

If the customer belongs to the GROSIR group, the unit's default discount is combined with an extra 5 percent discount in a tiered way.

Use Category Product and Type Value Disc.

detail.salesqty >= 10 ? 2500 : 0

The result 2500 is the discount per unit. If the user buys 10 units, the line discount value becomes 25,000.

Use Category Transaction and Type Percent Disc.

salesgross >= 1000000 ? 2 : 0

If the sales gross is at least 1,000,000, the system gives a 2 percent invoice discount.

Use Category Transaction and Type Value Disc.

brandsummary["BRAND01"] >= 500000 ? 25000 : 0

If the total net amount of goods from brand BRAND01 is at least 500,000, the system gives an invoice discount of 25,000.

Use the category and type according to the promotion's needs.

currentmonth == 12 ? 3 : 0

This formula returns 3 in December. If the type is Percent Disc, it means a 3 percent discount.

Testing Checklist

Common Problems

Check whether the Product category rule is attached to the Product, the Customer has a Sales Disc rule list, and the transaction uses the same product and customer. The item calculation takes the formula from product.salesdiscrules.

Make sure the Transaction category rule is attached to the Customer and its type is Percent Disc or Value Disc. The Sales Price type is not used by the invoice discount calculation.

For the Product category and Value Disc type, the formula must return the discount per unit. The system multiplies the formula result by the line quantity.

Use variable names from the autocomplete. For summary arrays, use brackets and quotes such as brandsummary["BRAND01"]. For invoice rules, avoid detail.[kolom] because the variable detail has not been passed to the invoice evaluator.

The backend evaluates rules one by one and stores the result in the variable salesprice, percentdisc, or valuedisc. If several rules use the same type, the last rule's result becomes the final result.