π° Sales
Turn customer interest into confirmed revenue β quotations, sales orders, pricelists, and promotions in one place.
What is the Sales scope?
The Sales scope is where your team captures every customer commitment, from the first price quote to the final signed order. It keeps your pricing consistent, your promotions running on schedule, and every salesperson looking at the same live pipeline.
Use it to send quotations, confirm orders, lock in long-term contracts with blanket agreements, and watch how your sales are tracking against targets β all without leaving HMX.
How a sale moves through the system
A quotation is just a price offer. It only becomes a Sales Order β and a real commitment β the moment the customer agrees and you confirm it.
What you can do in Sales
Quotations & Orders
Draft a price offer, send it to the customer, and confirm it as a Sales Order once they agree.
Blanket Orders
Lock in a yearly contract with a customer β release individual orders against it as they need stock.
Pricelists
Different prices for different customers, regions, or date windows β managed centrally.
Promotions
Time-limited discounts or bundled offers, applied automatically when rules match.
Sales Analytics
See how sales are performing by customer, product, or rep β powered by HMX's OLAP engine.
Getting Started with Sales
Four quick steps to get your first quotation out the door.
Before you begin
Make sure these are in place β the Sales scope depends on them:
- Your customers exist in the Partners module (at least one).
- Your products or services are set up with selling prices.
- Your company's currency and tax settings are configured.
- The user creating orders has the Sales role assigned.
Your first sale β step by step
Step 1: Define at least one Order Type
Order Types let you separate regular orders from special ones (samples, consignment, service-only, etc.). You need at least one before you can confirm any sales order.
Step 2: Set up a default Pricelist
Even if you only sell in one currency with no discounts, every order needs a pricelist attached. Create one called "Public" to start.
Step 3: Create your first quotation
Open the Sales Orders screen and click New. Pick a customer, an order type, and the pricelist you just made. Add a product line and save as a draft quotation.
Step 4: (Optional) Set a credit limit for the customer
If you sell on credit, open the customer record and set a limit. HMX will warn you before confirming orders that push the customer past it.
Quotations
Send price offers to prospects and customers. A quotation is not a commitment β it becomes a Sales Order only when you confirm it.
What is a quotation?
A quotation is a formal price offer you send to a customer, listing the products or services they asked about, the quantities, the unit price, and any taxes or discounts. It has a validity date β after that date, the offer expires.
The customer can accept, ask for changes, or walk away. Nothing is booked in your books until you confirm the quotation and it becomes a Sales Order.
How to create and send a quotation
Step 1: Open the Sales Orders list
Quotations and confirmed orders live in the same list, separated by status. Click New to start a fresh quotation.
| Reference | Customer | Date | Status | Total |
|---|---|---|---|---|
| SO00123 | ACME Corp | 2026-04-18 | Draft | 1,250.00 |
| SO00122 | Globex Ltd | 2026-04-17 | Sent | 3,420.00 |
| SO00121 | Initech | 2026-04-15 | Confirmed | 8,900.00 |
Step 2: Pick customer and order type
Choose which customer you're quoting to, and which Order Type applies. The order type controls downstream rules like delivery method and approval workflow.
Step 3: Set dates and pricelist
Enter the quotation date, a validity date (how long the offer stays open), and confirm the pricelist. The pricelist decides which unit prices appear when you add lines.
Step 4: Add product lines
Add each item the customer is buying, with quantity. The unit price fills in from the pricelist, and you can override it or apply a line-level discount.
| Product | Qty | Unit Price | Disc % | Subtotal |
|---|---|---|---|---|
| Widget Pro | 10 | 100.00 | 5 | 950.00 |
| Install Service | 1 | 300.00 | 0 | 300.00 |
Step 5: Send to customer
Save the quotation, then click "Send". HMX generates a PDF and emails it to the customer contact. The status moves from Draft to Sent.
Step 6: Confirm the order
When the customer says yes, click "Confirm". The quotation becomes a Sales Order, a reference number is locked in, and downstream workflows (delivery, invoicing) can start.
Sales Orders
Confirmed, committed orders β the ones that drive delivery, invoicing, and revenue.
What is a Sales Order?
A Sales Order is a confirmed quotation β a locked-in agreement to deliver specific products at specific prices. Once confirmed, the order drives deliveries and invoices, and its totals show up in your sales reports.
Order statuses
| Status | What it means | What can change |
|---|---|---|
| Draft | Quotation in progress, not sent. | Anything. |
| Sent | Quotation emailed; waiting on the customer. | Still editable β resend after changes. |
| Sales Order | Customer accepted, order confirmed. | Locked. Use Revise to change pricing or qty. |
| Done | Fully delivered & invoiced. | Read-only. |
| Cancelled | Order pulled back before fulfilment. | Archived β reference only. |
How to revise a confirmed order
Step 1: Open the confirmed order
Find the Sales Order in the list. Its status should show "Sales Order" (green). You can't change lines directly β use Revise.
Step 2: Click Revise β a new revision opens
HMX creates a copy of the order in Draft, with a revision tag (e.g. SO00123/R1). The original is preserved so you can always see the history.
Step 3: Edit lines & resend
Adjust quantities, prices, or products. Save, then Send to push the revised PDF to the customer for approval.
| Product | Old Qty | New Qty | Ξ |
|---|---|---|---|
| Widget Pro | 10 | 12 | +2 |
| Install Service | 1 | 1 | β |
Step 4: Confirm the revision
Once the customer signs off, click Confirm. The revised order replaces the previous one as the active Sales Order, and downstream deliveries / invoices re-sync to it.
Blanket Orders
Long-term customer agreements β lock in pricing and quantities, release individual orders over time.
What is a Blanket Order?
A Blanket Order is a framework contract: a customer agrees to buy, say, 10,000 units across the next 12 months at a fixed price. Instead of confirming one big delivery, you release smaller Sales Orders against the blanket whenever the customer calls for stock β and HMX tracks the remaining balance.
How to set up and use a Blanket Order
Step 1: Create the blanket agreement
Open the Blanket Orders screen and create a new agreement. Pick the customer, the total committed quantity, and the validity window.
Step 2: Raise a Sales Order against it
When the customer calls for a release, create a normal Sales Order and link it to the Blanket Order. Quantities are drawn from the remaining balance.
Step 3: Confirm β HMX validates against the contract
On Confirm, HMX checks the release quantity against the remaining blanket balance and validity dates. If you exceed the balance or the contract is expired, confirmation is blocked.
Step 4: Watch the remaining balance
Open the Blanket Order any time to see committed, released, and remaining quantities β plus every linked Sales Order on the "Releases" tab.
| Sales Order | Date | Qty | Status |
|---|---|---|---|
| SO00200 | 2026-04-22 | 500 | Confirmed |
| SO00215 | 2026-05-10 | 800 | Done |
Pricelists
Centralized pricing β by customer, region, product group, or date window. One source of truth for every quote.
Why pricelists?
Most businesses don't sell at one fixed price. You might give Gold-tier customers 10% off, offer different prices in Europe vs. Asia, or run a quarterly promotion. Pricelists let you manage all of that in one place, then attach the right pricelist to each customer or order β prices flow in automatically.
How to set up a pricelist
Step 1: Open Pricelists and click New
Go to Sales β Configuration β Pricelists. You'll see every pricelist already on the system. Click New to begin.
| Name | Currency | Scope | Valid |
|---|---|---|---|
| Public | USD | All customers | Always |
| Gold Tier | USD | Gold customers | Always |
| EU Retail | EUR | Region: EU | 2026-01-01 β |
Step 2: Name the pricelist and pick its scope
Give it a clear name. Decide who it applies to: all customers, a specific customer group, or a region. Scope decides which orders automatically pick this pricelist.
Step 3: Choose a base pricing method
Decide how prices are calculated: straight from the product's list price, from cost + markup, or from another pricelist with an adjustment. Most users pick "Product Price" and add discount rules on top.
Step 4: (Optional) Add per-product or per-category rules
Need a different discount on some products? Add rules on the Rules tab. Each rule can target a product, a category, or a quantity breakpoint.
| Applies To | Min Qty | Rule | Value |
|---|---|---|---|
| Category: Software | 1 | % off | 15 |
| Product: Widget Pro | 50 | Fixed price | 85.00 |
Step 5: Set validity dates and save
Leave dates blank for "always active", or set a start/end to limit the pricelist to a window (like a quarterly promo). Save, and the pricelist is live.
Promotions
Run time-limited offers β percentage discounts, buy-one-get-one, free shipping β applied automatically at order time.
Promotions vs. Pricelists
Pricelists set long-term pricing for a segment (Gold tier, EU region). Promotions are short bursts β "Free shipping this weekend", "20% off first order", "Buy 2 get 1". A pricelist quietly adjusts every quote; a promotion fires only when its rules match and usually shows up as a named line on the order.
How to set up a promotion
Step 1: Open Promotions and click New
Each promotion is a record with rules, dates, and rewards. You can have many running at once.
| Name | Window | Status |
|---|---|---|
| Spring Sale 20% | Apr 1 β Apr 30 | Active |
| Free Ship > 500 | Always | Active |
| Summer BOGO | Jul 1 β Aug 31 | Scheduled |
Step 2: Name it and set the window
Give the promotion a name your team will recognise on a quote. Set the start and end dates β HMX will only apply the promo within that window.
Step 3: Decide who & what it targets
Limit the promo by customer segment (e.g. new customers only), by product category, or by minimum order value. Leave a field empty to mean "any".
Step 4: Pick the reward type
What do customers get? Choose from: percentage off, fixed amount off, free shipping, free product (BOGO), or tiered discount. Each reward type has its own fields.
Step 5: Activate and save
Flip the Active toggle and save. From the start date onward, any matching order will automatically show the discount β with the promo name on the order line.
| Line | Description | Amount |
|---|---|---|
| 1 | 2 Γ Spring Jacket | 240.00 |
| 2 | Promo: Spring Sale 20% | β48.00 |
Sales Analytics
Live dashboards on your sales data β powered by HMX's built-in OLAP engine.
What's different about Sales Analytics?
Transactional screens (orders, invoices) are great for day-to-day work but slow for big questions like "which salesperson sold the most widgets last quarter?". Sales Analytics uses HMX's OLAP engine β a fast, column-oriented copy of your sales data β so those questions return in seconds, even across years of records.
What reports come out of the box
Top Customers
Revenue by customer, ranked β with drilldown to their orders.
Product Performance
Units sold and revenue by product or category.
Rep Leaderboard
Which salesperson closed the most orders this period.
Sales by Period
Daily, weekly, monthly and yearly trend lines β compare periods.
Sample: Top Customers report
| Rank | Customer | Orders | Revenue | Share |
|---|---|---|---|---|
| 1 | ACME Corp | 42 | 184,200 | 18 % |
| 2 | Globex Ltd | 28 | 121,800 | 12 % |
| 3 | Initech | 19 | 93,450 | 9 % |
| 4 | Umbrella Co | 24 | 81,300 | 8 % |
| 5 | Soylent Corp | 17 | 67,900 | 7 % |
Click any row to drill down into the customer's orders, products, and payment status β all computed live from HMX's OLAP cube.
How the analytics work under the hood
The sale_olap module syncs Sales Order data into a DuckDB-backed analytical store. Reports query that store, not the live transactional tables, so there's no production-database load. Sync is incremental and runs after every order confirmation.
Technical Reference
Models, fields, states, hooks, and API endpoints β for developers extending the Sales scope.
Modules in the Sales scope
| Module | Purpose |
|---|---|
| sale | Top-level Sales app β exposes the entry models and API endpoints. |
| core_sale | Core sales logic: Sales Order lifecycle, Order Type, Revise flow, credit limit hooks. |
| master_sale | Master data for the Sales scope: customer category extensions, partner-as-customer helpers. |
| sale_olap | Sync layer that feeds Sales records into the DuckDB OLAP store for analytics. |
Key data models
| Model | Module | Role |
|---|---|---|
| saleorder | core_sale | Header of a Sales Order / Quotation. Owns status, customer, totals, validity. |
| saleorderline | core_sale | Line-level product, quantity, unit price, line discount. |
| saleordertype | core_sale | Order Type master β controls downstream rules (delivery, workflow). |
| salepricelist | core_sale | Pricelist header β scope, currency, base method, validity. |
| basepartner | core_sale / master_sale | Customer record extensions (credit limit, tier, default pricelist). |
| salecustomercategory | master_sale | Customer category master (Gold, Retail, Wholesale). |
Sales Order state machine
| From | Action | To |
|---|---|---|
| draft | action_send | sent |
| sent | action_confirm | sale |
| draft | action_confirm | sale |
| sale | action_revise | draft (new revision) |
| sale | action_done | done |
| any | action_cancel | cancel |
Extension hooks
saleorder._onchange_partner_idβ fills pricelist, payment terms, salesperson from the customer.saleorder._check_credit_limitβ pre-confirm guard; raises ifpartner.outstanding + order.total > partner.credit_limit.saleorder._compute_totalsβ recomputes taxed/untaxed totals; override for custom rounding or levies.saleorderline._onchange_product_idβ pulls unit price from the active pricelist, default UoM, taxes.salepricelist._get_priceβ the rules engine: override to inject custom pricing logic.
API endpoints
| Module | Method | Path | Purpose |
|---|---|---|---|
| sale | GET | / | Health + scope metadata. |
| sale | GET | /check | Module availability probe (used by deploy checks). |
OLAP models (sale_olap)
The sale_olap module mirrors Sales Order data into DuckDB for analytics. The fact table carries confirmed-order lines; dimension tables join customer, product, period, and salesperson.
| Table | Role |
|---|---|
| fact_sale_line | One row per confirmed order line β qty, revenue, discount, cost. |
| dim_customer | Customer attributes (name, category, region) at time of sync. |
| dim_product | Product attributes (name, category, cost). |
| dim_period | Calendar dimension β day, week, month, quarter, year keys. |
| dim_salesperson | Salesperson / team attributes. |