Katkat

Studio Session Billing with Rate Schedules and Exact Rounding

RipplingSoftware Engineer · Technical Screen

Last updated

Build a command-driven billing ledger where technicians' hourly rates change over time, bookings are deduplicated, and fractional cents are kept exactly until an invoice is printed with a configurable rounding mode. Tests rate lookup by time, idempotency rules, and integer-exact money handling.

A recording studio rents out sound technicians by the hour. Each technician has a rate schedule that can change over time, and clients book sessions with a technician. You are building the billing ledger.

Implement:

def run_ledger(commands: list[list[str]], mode: str) -> list[str]:
    ...

Process commands in order and return one output string per command.

CommandEffectOutput
["HIRE", tech, rate]New technician with hourly rate effective from time 0"OK"
["RERATE", tech, rate, at]Technician's hourly rate becomes rate from second at onward"OK"
["BOOK", booking, tech, start, end]Bill a session from second start to second end"OK" or "REJECTED"
["INVOICE"]Report the running total of all billed sessionsdollars, e.g. "35.13"

Billing rules

  • A session is charged entirely at the rate in force at its start second: the schedule entry with the largest at <= start. Rate changes in the middle of a session are ignored.
  • Once a session is billed, its charge is frozen. A later RERATE, even one with an at in the past, does not change it.
  • RERATE commands may arrive with any at, in any order. Two entries with the same at for the same technician: the later command wins.
  • Booking IDs are global. A booking ID that has already been billed is REJECTED, whatever its payload.
  • A session longer than 4 hours (14,400 seconds) is REJECTED and does not use up its booking ID. Exactly 4 hours is fine. A zero-length session is billed at $0.
  • Overlapping sessions for the same technician are each billed in full.
  • Keep fractional cents exactly. Round only when INVOICE prints, and printing must not change the stored total. mode is "HALF_UP" or "HALF_EVEN" and decides what happens when the total lands exactly on half a cent.

Rates are strings with exactly two decimals ("0.00" to "9999.99"). Times are integers in [0, 10^9] with start <= end. Every technician referenced exists. Up to 10^5 commands.

Example

mode = "HALF_UP"
["HIRE", "ana", "30.00"]               -> OK
["HIRE", "raj", "19.99"]               -> OK
["RERATE", "ana", "45.00", "3600"]     -> OK
["BOOK", "s1", "ana", "0", "1800"]     -> OK        # 0.5 h at 30.00 = 15.00
["BOOK", "s2", "ana", "3000", "5400"]  -> OK        # starts before 3600, so 30.00: 20.00
["BOOK", "s3", "ana", "3600", "3610"]  -> OK        # 10 s at 45.00 = 0.125
["BOOK", "s1", "raj", "0", "100"]      -> REJECTED  # s1 already billed
["BOOK", "s4", "raj", "0", "20000"]    -> REJECTED  # longer than 4 h
["INVOICE"]                            -> 35.13     # exact 35.125
["BOOK", "s4", "raj", "0", "180"]      -> OK        # s4 was not used up; 0.9995
["INVOICE"]                            -> 36.12     # exact 36.1245

With mode = "HALF_EVEN" the first invoice prints 35.12 and the second still prints 36.12.

Hints

Hint 1

Store each technician's schedule so you can find the largest effective time <= a session's start in O(log k).

Hint 2

Never convert money to float. What single integer can you accumulate so that the exact total is that integer divided by a constant?

Hint 3

Compute rounding from divmod(acc, 3600): compare twice the remainder with 3600, and handle equality per mode.

Hint 4

Mark a booking ID as used only after the session passes validation.

Follow-up questions

  • How would you bill a session that crosses a rate change pro rata across both rates?
  • Commands now come from several API servers concurrently. How do you keep booking IDs idempotent?
  • How would you support refunding (voiding) a booking and still produce exact totals?
  • What changes if the per-technician schedule can hold millions of entries and rerates are frequent?