Katkat

Stagehand Wage Ledger With Accrued and Settled Pay

RipplingSoftware Engineer · Technical Screen

Last updated

Design an in-memory class that tracks hourly pay for theater stagehands across logged call times, keeps running cost totals, and settles wages up to a cutoff without ever paying the same minute twice.

A stage crew booking tool pays stagehands by the hour for the time they spend on a "call" (a scheduled work block on a show). Implement a class that tracks this in memory.

Support these operations:

  • add_worker(worker_id, hourly_rate_usd) registers a stagehand with a USD hourly rate.
  • log_call(worker_id, start_time, end_time) records a completed call as the interval the worker was on the clock.
  • total_cost() returns the total labor cost accrued across every worker and every logged call, whether or not it has been paid.
  • pay_through(cutoff_time) settles wages for all work performed up to cutoff_time. Calling it again with the same or an earlier time must not pay anything twice.
  • unpaid_balance() returns the total wages still owed after the settlements so far.
ledger = StageWageLedger()
ledger.add_worker("crew_7", 42.0)
ledger.log_call("crew_7", 0, 7200)       # a 2-hour call -> $84
ledger.total_cost()                       # 84.0
ledger.pay_through(3600)                  # settle the first hour -> $42 paid
ledger.unpaid_balance()                   # 42.0
ledger.pay_through(3600)                  # no-op, already settled through here
ledger.unpaid_balance()                   # still 42.0

Constraints and Clarifications

  • Timestamps are integers in seconds (e.g. Unix time). A call's cost is the worker's hourly rate times the call's duration in hours.
  • A call may be logged after a cutoff has already been applied to that worker, with a start time before the cutoff; only the portion after the cutoff is unpaid.
  • A worker may have many overlapping or back-to-back calls; each logged call contributes its own duration independently.
  • total_cost() and unpaid_balance() may be called far more often than the other two operations.
  • Everything lives in memory in one process; no persistence or concurrent callers unless you choose to discuss them.

Clarifying Questions

  • Should amounts be exact to the cent even after many small calls and settlements?
  • If log_call is given a call that starts before a worker's last pay_through cutoff, is the pre-cutoff portion simply ignored, or is that an error?
  • Is pay_through ever called with a time before the previous cutoff, and if so, should it truly be a no-op?

Hints

Hint 1

Which of the two getters can be kept as running totals that you update incrementally, instead of rescanning every call on each call?

Hint 2

For pay_through, each call splits into a part before the cutoff and a part after it. Track a per-worker "paid through" timestamp so a repeat or earlier call changes nothing.

Hint 3

An hourly rate times a duration in seconds rarely lands on a whole number of cents. Keep a smaller, exact unit (like micro-cents) internally and only convert to dollars when returning a value.

Hint 4

Decide what happens when a call is logged with a start time already before the worker's paid-through pointer, and make log_call compute the correct already-paid portion for that call on arrival.

Follow-up questions

  • How would you support multiple currencies or an hourly rate that changes mid-engagement?
  • How would you make pay_through faster than O(calls logged so far), for a ledger with millions of calls?
  • What should total_cost() report for a call that has started but has no end_time yet?
  • How would this design change if log_call and pay_through could be called concurrently from multiple threads?