A few months back, I landed a project building a POS application for a retail store. I thought it’d be straightforward: create some product forms, cashiers scan barcodes, customers pay, done. Boy, was I wrong.
The first issue hit during user testing. A cashier looked at me, frustrated: “Hey, when a member buys something that’s also on sale, which price do I use?” She pointed at the screen showing two buttons: “Apply Member Price” or “Apply Discount Price”.
Damn. The cashier had to decide manually. Transactions slowed down. Customers waiting in line started getting impatient.
That’s when it clicked: the system needs to figure out prices automatically, not force cashiers to think about it.
Finding the Right Approach
I started spending time at the store. Watched cashiers work, listened to their complaints. That’s where I learned some crucial things:
Cashiers work fast. Average transaction? 30-60 seconds. Scan product, scan member card (if applicable), enter quantity, customer pays, next. They don’t have time to click around choosing prices.
Customers expect the best deal automatically. If they’re a member, they expect the member price to just… happen. If there’s a sale going on, they expect it to apply. No hassle.
That’s when I started designing a clear price priority logic.
The Price Types I Encountered
In this project, the client had several price types:
- Cost Price (COGS) - purchase price from suppliers, internal only
- Base Price - standard retail price on the shelf
- Variant Price - same product but different sizes/colors can have different prices
- Member Price - special pricing for registered members
- Discount Price - promotional pricing with time limits
- Tiered Price - quantity-based pricing, buy more pay less per unit
The million-dollar question: when all these prices exist, which one wins?
The Priority Order I Implemented
After discussing with the client and testing different approaches, here’s what worked (highest to lowest priority):
1. Variant Price (Highest Priority)
This is the most specific. Here’s the thing: if a customer picks size 42 shoes, the price has to follow the size 42 pricing. You can’t just use the cheaper size 40 price.
Real example I dealt with:
Plain T-Shirt
├─ Size S = Rp 100.000
├─ Size M = Rp 100.000
├─ Size L = Rp 110.000
└─ Size XL = Rp 120.000
Customer picks XL? System grabs the Rp 120.000 price. Doesn’t matter if there’s a discount or not, it starts with the variant price.
2. Member Price
Once the specific product is selected, system checks: is the buyer a member? If the cashier scanned a member card, member pricing kicks in automatically.
My experience: Initially, I put member pricing at the lowest priority. Big mistake. Members complained they weren’t getting their perks. After bumping up the priority, complaints dropped by 80%.
3. Discount or Promotional Price
Discounts usually have time limits. System needs to check: is today within the promo period? If yes, apply it.
Interesting bug story: Once had a bug where a discount that should’ve ended on the 20th kept running until the 25th. Store lost millions of rupiah. Since then, I’ve been super strict about validating start and end dates.
4. Tiered Price
Tiered pricing triggers based on quantity. Buy more, pay less per unit.
Example:
Buy 1-4 units = Rp 100.000 each
Buy 5-10 units = Rp 95.000 each
Buy 11+ units = Rp 90.000 each
```23
System automatically checks quantity and applies the appropriate tier.
### 5. Base Price (Fallback)
This is the default. If none of the above conditions apply, use this.
## The Tricky Part: Combining Prices
The question I got asked most: **can these prices stack?**
Like: a member buys something that's on sale. Do they get both discounts?
**My answer after discussing with the client:**
**Can combine:**
- Member + Discount (members still get general sales)
- Member + Tiered Price (members buying bulk get even better prices)
**Cannot combine:**
- Member Price vs Discount Price (pick whichever benefits the customer more)
Turns out these rules vary by store policy. The key: **document it clearly and apply it consistently**.
## How I Store This in the Database
I created a `price_types` table with a `priority` column:
```sql
CREATE TABLE price_types (
id INT PRIMARY KEY,
name VARCHAR(50),
priority INT, -- 1 = highest
is_active BOOLEAN
);
INSERT INTO price_types VALUES
(1, 'Variant Price', 1, TRUE),
(2, 'Member Price', 2, TRUE),
(3, 'Discount Price', 3, TRUE),
(4, 'Tiered Price', 4, TRUE),
(5, 'Base Price', 5, TRUE);
This way, I can easily change priorities without touching the code.
The Workflow at the Register
Here’s how it flows after my improvements:
1. Cashier scans product barcode
→ System grabs Base Price
2. Customer selects variant (if applicable)
→ Switch to Variant Price
3. Cashier scans member card
→ Switch to Member Price
4. Cashier enters quantity
→ System checks Tiered Price thresholds
→ Apply if conditions met
5. System checks for active promotions
→ Check transaction date/time
→ Apply Discount Price if currently active
6. Display final price on screen
Everything happens automatically. Cashier doesn’t need to click anything extra.
What I Learned
- Automate as much as possible so cashiers don’t have to make many decisions.
- Display clear labels such as MEMBER, 20% OFF, or WHOLESALE PRICE so customers understand why prices differ.
- Show the original price crossed out, followed by the final price, to create a strong discount effect.
- Log every transaction, including the product, price used, cashier, and timestamp, to support auditing and tracking.
- Require manager approval with a password for any manual price changes, and record all overrides in the system.
Display on Cashier Screen
I designed it like this:
PRODUCT: Plain T-Shirt - Black - L
──────────────────────────────────
Regular Price Rp 200.000 [crossed out]
Variant Price Rp 210.000 [L is more expensive]
MEMBER ACTIVE Rp 189.000 [✓ 10% Discount]
──────────────────────────────────
TOTAL Rp 189.000
With this display, both cashier and customer understand how the price was calculated.
Results After Implementation
After implementing this price priority system:
- Transaction time dropped from 75 seconds to 45 seconds average
- Cashier complaints reduced by 80%
- Customers happier because they automatically get the best price
- No more manual price entry errors
My Takeaways
Building a good price priority system turned out way more complex than I expected. Required field observation, listening to user complaints, and continuous improvement.
What I learned:
- Prioritize automation - reduce manual decisions
- Make rules crystal clear - don’t let cashiers guess
- Test with real users - don’t just test in the lab
- Log everything - incredibly important for auditing
If you’re building a POS system, I hope my experience helps. Good luck!