| ← All Docs Business Rules

Business Rules

Core billing, pricing, payment, and credit logic

Billing Calculations — Per-item Pricing

AquaDealers supports two product categories with different pricing models. The pricing strategy depends on the product type.

Feed Products Simple

selling_price is the rate the farmer pays. There is no MRP/discount split. The price is directly editable by the dealer at billing time.

  • Rate comes from inventory or farmer-specific memory
  • Dealer can override on each bill line
  • No computed discount field

Medicine Products Computed

Price is derived from MRP and a discount percentage:

unit_price = MRP × (1 − discount_percentage / 100)

Discount cascade (checked in order):

  1. Farmer-product specific discount
  2. Farmer default discount
  3. Product default discount

Per-Farmer Rate Memory

When a bill is saved, the specific rate given to a farmer for each product is stored via upsertFarmerProductDiscount(). The next time that farmer purchases the same product, their previously-given rate is automatically pre-filled in the product selector.

This means dealers don't need to remember individual farmer rates — the system tracks the last rate used for each farmer+product combination and applies it as the default on the next purchase.

GST Handling

Per-Item GST Calculation

gst_amount = ROUND(unit_price × quantity × gst_rate / 100, 2)
Known issue: Step 1 (ProductSelector) uses a flat 18% GST rate for display preview purposes, while Step 3 (ReviewStep) uses the actual per-item gst_rate from the product record. The final bill total is always correct — only the in-progress preview may differ slightly.

Bill Total Formula

subtotal = Σ(unit_price × quantity) for each item
gst      = Σ(item.gst_amount) for each item
total    = GREATEST(ROUND(subtotal + gst − discount_amount, 2), 0)

The GREATEST(..., 0) guard ensures the total never goes negative even if the discount exceeds the subtotal + GST.

Payment Methods

MethodCodeBehavior
CashcashStandard cash payment. Amount entered manually.
UPIupiDigital payment. Amount entered manually.
CreditcreditAuto-sets amount_paid = 0. Full amount added to farmer's due.
OtherotherBank transfer, cheque, etc. Amount entered manually.

Balance Due

balance_due = total − amount_paid

Walk-in Bills Must Pay Full

Walk-in customers must pay in full. This is enforced in useCheckout.ts (line ~163–164). No credit is allowed for walk-in bills.

Farmer Bills Partial OK

Farmer bills allow partial payment. The remaining balance_due is added to the farmer's outstanding total and can be collected later.

No round-off field: The system does not currently support automatic rounding of bill totals (e.g., rounding to nearest rupee). This is a known limitation.

Settlement Discount

Settlement discounts are applied at payment time, separate from per-item discounts on the bill itself. They reduce the effective total the farmer needs to pay.

Farmer Due Calculation

Farmer dues are trigger-based — recalculated automatically whenever bills, payments, or payment allocations change.

total_due = GREATEST(0,
  opening_balance
  + Σ(active_bills.balance_due)
  − (Σ(payments.amount) − Σ(allocated_payments))
  − return_credit_balance
)

Triggers That Recalculate Dues

TriggerFires OnAction
farmer_due_from_billbills table INSERT/UPDATE/DELETERecalculates total_due from all active bills
farmer_due_from_paymentpayments table INSERT/UPDATE/DELETERecalculates total_due factoring payment totals
farmer_due_from_payment_allocationpayment_allocations table INSERT/UPDATE/DELETERecalculates total_due factoring allocated amounts
The GREATEST(0, ...) wrapper ensures a farmer's due never shows as a negative number, even if overpayments exist. Overpayments are tracked but do not create a “credit” balance on the farmer record itself.

Payment Allocation

When a payment is collected from a farmer, it can be allocated in one of two modes:

Mode 1: Specific Bill Targeted

Payment targets a single bill. This typically happens at bill creation time when the farmer pays for a specific purchase.

  • Payment linked directly to one bill
  • Creates a single payment_allocation record
  • Reduces that specific bill's balance_due

Mode 2: Oldest First Auto-Allocate

The collect_farmer_payment_v2 RPC iterates unpaid bills ordered by bill_date ASC and allocates until the payment is exhausted.

  • Creates multiple payment_allocations records
  • Each record has an allocation_order
  • Oldest outstanding bills are cleared first

Credit Limits

How Credit Limits Work

Credit limits are advisory, not enforced. Dealers can override the limit and proceed with billing. The override is logged for audit purposes, including the reason entered by the dealer.

Bill Numbering

AD-{YYYYMMDD}-{sequence}

Walk-in Customers

Walk-in Bill Rules

Walk-in bills are identified by farmer_id = NULL or a designated walk-in farmer record.

Why walk-in bills require full payment

Since walk-in customers have no farmer profile, the system cannot track outstanding dues or collect future payments. Enforcing full payment at checkout ensures the dealer doesn't accidentally create uncollectable credit for anonymous customers.