Let's talk
pricing

The message to the customer was 5,000 rupees short

The WhatsApp greeting told a customer to bring 18,725 rupees. The screen said 23,725. The gap was the deposit, and it changed with the operator's last click.

·

Your office sends every customer a WhatsApp greeting before pickup with the amount to have ready. One morning an operator notices that the message for a booking says 18,725 rupees, while the screen she is looking at says 23,725. She reloads and the message now says 23,725 too. She opens another booking, comes back, and it is 18,725 again.

For a self-drive car rental operator in Goa this was an urgent report, and rightly. The difference was exactly 5,000 rupees, the security deposit on the car, and a customer who arrives with the amount the message told them to bring is a customer who arrives 5,000 short, at the kerb, with the driver waiting. Worse than being wrong was being inconsistent: the same booking produced two different figures depending on which booking the operator had looked at before.

What was actually going on

Three separate faults, in one piece of the screen, and each on its own would have been hard to spot.

The first: two parts of the system had their own idea of when a deposit applies. The office ledger, which is what the settlement screen shows, always expects the car’s security deposit and only adds the inter-state deposit when the customer has chosen that extra. The part that writes the WhatsApp messages looked for the security deposit among the booking’s extras. A booking created at the counter, or on the portal itself, has no extras at all, so the message builder found no deposit and left 5,000 out of all five messages.

The second: when the message builder was given a booking with nothing to read, it stopped early without clearing the figures from the last booking it had shown. So after looking at a booking that did carry the deposit, the operator saw 23,725; on a fresh load of the same booking, 18,725. The number depended on her history, not on the booking.

The third: the outstanding amount in the message was fetched separately from the booking itself, and the message was sometimes composed before that answer had come back.

All three were confirmed against the live data for the reported booking, read-only, before any code was touched: extras empty, total 18,725.42, nothing paid, security deposit 5,000, inter-state deposit 5,000. The operator’s report was the only reason it was found: nothing in the system compared the message with the screen.

What we changed

The message builder now works out the deposit by exactly the same rule as the ledger, rather than by its own. Every deposit figure is cleared on every path through the builder, including the early exit, so nothing leaks from the previous booking. And the message links are rebuilt once the outstanding amount has actually arrived, rather than with whatever was there at the time.

Checked by reconciling the ledger’s arithmetic to the client’s figure of 23,725.42, then deployed to the test stack and to production the same day. It was a screen-only change; no data needed correcting.

What it did not fix

The log records no count of how many bookings had gone out with the short figure before the report, and the article will not guess one. Every counter-created booking on the old code would have produced it, but how many customers acted on it is not something the system recorded.

Any customer who had already been told 18,725 had already been told it. A corrected message is a new message, not a recall.

The mechanism, briefly

buildExtrasList() derived the security deposit from an extras key while the ledger’s computeDepositExpected always applies the vehicle’s security amount and gates only the inter-state deposit on an extras key; an early return on empty input never reset the component’s deposit fields, so the reused component leaked state across records; and the outstanding total came from a request that raced the reservation fetch. Deposit derivation now mirrors the ledger, all fields reset on every path, and links regenerate when the outstanding call lands.

Where this ends up

One amount, worked out by two rules, will disagree on the day it matters, which is why the amount a customer is asked to bring in Sazinga Rentals comes from the same ledger the settlement screen reads, and never from the message’s own arithmetic.

This came out of building Sazinga Rentals

Bookings, availability and the fleet standing behind them. The problem above is one we met while building it, and what we did about it is in the product.

If you run something like this, tell us how it works today and we will tell you what it would take to move.