cswork / libraryOpen practice workspace

Coding questions · DoorDash

Implement a gig worker payout calculator

This question evaluates skills in data modeling, time-series event handling, business-rule implementation, monetary rounding/precision, and idempotent processing, with an emphasis on SQL and Python proficiency for data manipulation.

Implement a payout calculator for gig workers (e.g., delivery drivers). Given a list of completed orders with timestamps, distances, and tips, plus policy tables for base pay, distance/time multipliers, surge/boosts, batching rules, cancellations, and minimum guarantees, compute per-order pay, per-shift summaries, and weekly statements. Handle edge cases such as partial cancellations, stacked deliveries, negative adjustments/chargebacks, rounding and currency precision, timezone boundaries, and idempotent reprocessing of late-arriving events. Provide function signatures or SQL, outline the schema, and include tests that cover typical and extreme scenarios.

Per-Order Gig Worker Payout Calculation

Using the tables orders, batches, base_pay_policy, surge_periods, and cancellation_policy, write a SQL query that computes the payout for each order for the sample week. For every row in orders, output one row with: order_id, worker_id, status, surge_multiplier, base_distance_cents_per_order, and payout_cents. Use the following rules: (1) Base and distance pay are determined per batch: batch_base_cents = base_per_order_cents + per_km_cents * batches.distance_km. For a batch with N orders, each order gets ROUND(batch_base_cents / N) cents of base+distance pay. (2) If the batch pickup_ts falls inside a surge_periods window for the same city_id, multiply the per-order base+distance pay by surge_multiplier; otherwise use 1.0. (3) For orders with status = 'completed', total payout_cents is the rounded surged base+distance pay plus tip_cents. (4) For any other status, ignore surge, distance, and tips and instead pay cancellation_policy.pay_cents. (5) All amounts are stored in cents as integers; when splitting batch pay and applying surge, round to the nearest cent. Return results for all rows in the orders table.

Table base_pay_policy

columntypeconstraints
effective_fromDATENOT NULL
effective_toDATENOT NULL
base_per_order_centsINTNOT NULL
per_km_centsINTNOT NULL

Sample data:

base_per_order_centseffective_fromeffective_toper_km_cents
2002025-01-012025-12-31100

Table surge_periods

columntypeconstraints
city_idINTNOT NULL
start_tsTIMESTAMPNOT NULL
end_tsTIMESTAMPNOT NULL
surge_multiplierDECIMAL(4,2)NOT NULL

Sample data:

city_idend_tsstart_tssurge_multiplier
12025-05-30 20:00:002025-05-30 18:00:001.5
12025-06-01 02:00:002025-05-31 22:00:002

Table cancellation_policy

columntypeconstraints
statusVARCHAR(32)PRIMARY KEY
pay_centsINTNOT NULL

Sample data:

pay_centsstatus
0completed
300cancelled_partial
0cancelled_full

Table batches

columntypeconstraints
batch_idINTPRIMARY KEY
worker_idINTNOT NULL
shift_idINTNOT NULL
pickup_tsTIMESTAMPNOT NULL
dropoff_tsTIMESTAMPNOT NULL
distance_kmDECIMAL(6,2)NOT NULL
city_idINTNOT NULL

Sample data:

batch_idcity_iddistance_kmdropoff_tspickup_tsshift_idworker_id
1152025-05-29 10:30:002025-05-29 10:00:001101
2142025-05-30 18:45:002025-05-30 18:15:002101
3132025-06-01 00:10:002025-05-31 23:30:004102
4202025-05-27 12:00:002025-05-27 12:00:003102
5122025-05-31 09:00:002025-05-31 09:00:005103

Table orders

columntypeconstraints
order_idINTPRIMARY KEY
batch_idINTNOT NULL
worker_idINTNOT NULL
statusVARCHAR(32)NOT NULL
tip_centsINTNOT NULL
completed_tsTIMESTAMPNOT NULL

Sample data:

batch_idcompleted_tsorder_idstatustip_centsworker_id
12025-05-29 10:30:001completed150101
22025-05-30 18:40:002completed0101
22025-05-30 18:42:003completed250101
42025-05-27 12:05:004cancelled_partial0102
52025-05-31 09:05:005cancelled_full0103
32025-06-01 00:05:006completed100102

Expected output

One row per order with the applied surge multiplier, the per-order base+distance pay before surge, and the final payout in cents after surge, tips, and cancellation rules.

base_distance_cents_per_orderorder_idpayout_centsstatussurge_multiplierworker_id
7001850completed1101
3002450completed1.5101
3003700completed1.5101
2004300cancelled_partial1102
40050cancelled_full1103
50061100completed2102

Hints

  1. First compute how many orders belong to each batch_id so you can split the batch base pay across orders.
  2. LEFT JOIN to surge_periods on city_id and pickup_ts, defaulting the surge multiplier to 1.0 when there is no matching surge window.
  3. Use CASE expressions to apply cancellation_policy.pay_cents for non-completed orders and to add surge and tips only for completed ones.

Per-Shift Gig Worker Earnings Summary

Using the same payout rules as in Question 1 and the additional shifts table, write a SQL query that produces a per-shift earnings summary for the sample data. Each shift is uniquely identified by shift_id. For every row in shifts, output: shift_id, worker_id, start_ts, end_ts, total_orders (count of orders in that shift, including cancelled ones), completed_orders (count of orders with status = 'completed'), and gross_earnings_cents (sum of per-order payout_cents including cancellation pay). Orders belong to a shift through their batch_id and the batches.shift_id column. Make sure the shift that crosses midnight is handled correctly by grouping by shift_id rather than by calendar date.

Table base_pay_policy

columntypeconstraints
effective_fromDATENOT NULL
effective_toDATENOT NULL
base_per_order_centsINTNOT NULL
per_km_centsINTNOT NULL

Sample data:

base_per_order_centseffective_fromeffective_toper_km_cents
2002025-01-012025-12-31100

Table surge_periods

columntypeconstraints
city_idINTNOT NULL
start_tsTIMESTAMPNOT NULL
end_tsTIMESTAMPNOT NULL
surge_multiplierDECIMAL(4,2)NOT NULL

Sample data:

city_idend_tsstart_tssurge_multiplier
12025-05-30 20:00:002025-05-30 18:00:001.5
12025-06-01 02:00:002025-05-31 22:00:002

Table cancellation_policy

columntypeconstraints
statusVARCHAR(32)PRIMARY KEY
pay_centsINTNOT NULL

Sample data:

pay_centsstatus
0completed
300cancelled_partial
0cancelled_full

Table batches

columntypeconstraints
batch_idINTPRIMARY KEY
worker_idINTNOT NULL
shift_idINTNOT NULL
pickup_tsTIMESTAMPNOT NULL
dropoff_tsTIMESTAMPNOT NULL
distance_kmDECIMAL(6,2)NOT NULL
city_idINTNOT NULL

Sample data:

batch_idcity_iddistance_kmdropoff_tspickup_tsshift_idworker_id
1152025-05-29 10:30:002025-05-29 10:00:001101
2142025-05-30 18:45:002025-05-30 18:15:002101
3132025-06-01 00:10:002025-05-31 23:30:004102
4202025-05-27 12:00:002025-05-27 12:00:003102
5122025-05-31 09:00:002025-05-31 09:00:005103

Table orders

columntypeconstraints
order_idINTPRIMARY KEY
batch_idINTNOT NULL
worker_idINTNOT NULL
statusVARCHAR(32)NOT NULL
tip_centsINTNOT NULL
completed_tsTIMESTAMPNOT NULL

Sample data:

batch_idcompleted_tsorder_idstatustip_centsworker_id
12025-05-29 10:30:001completed150101
22025-05-30 18:40:002completed0101
22025-05-30 18:42:003completed250101
42025-05-27 12:05:004cancelled_partial0102
52025-05-31 09:05:005cancelled_full0103
32025-06-01 00:05:006completed100102

Table shifts

columntypeconstraints
shift_idINTPRIMARY KEY
worker_idINTNOT NULL
start_tsTIMESTAMPNOT NULL
end_tsTIMESTAMPNOT NULL

Sample data:

end_tsshift_idstart_tsworker_id
2025-05-29 12:00:0012025-05-29 09:00:00101
2025-05-30 20:00:0022025-05-30 17:30:00101
2025-05-27 13:00:0032025-05-27 11:00:00102
2025-06-01 00:30:0042025-05-31 22:30:00102
2025-05-31 10:00:0052025-05-31 08:00:00103

Expected output

One row per shift with counts of total and completed orders and the total gross earnings (including cancellation pay) for that shift.

completed_ordersend_tsgross_earnings_centsshift_idstart_tstotal_ordersworker_id
12025-05-29 12:00:0085012025-05-29 09:00:001101
22025-05-30 20:00:00115022025-05-30 17:30:002101
02025-05-27 13:00:0030032025-05-27 11:00:001102
12025-06-01 00:30:00110042025-05-31 22:30:001102
02025-05-31 10:00:00052025-05-31 08:00:001103

Hints

  1. Put the per-order payout logic into a CTE and then aggregate it by shift_id.
  2. Join shifts to batches and orders via shift_id so the shift that crosses midnight is handled as a single unit.

Weekly Gig Worker Statement with Minimum Guarantees and Adjustments

For the pay week starting 2025-05-26 and ending 2025-06-01 (inclusive), build a weekly statement per worker that applies minimum guarantees and adjustments on top of the per-order payouts defined in Question 1. Using orders, batches, base_pay_policy, surge_periods, cancellation_policy, adjustments, and weekly_minimum_guarantee, write a SQL query that returns one row per worker with: worker_id, week_start_date, order_earnings_cents (sum of payout_cents from all that worker's orders completed in the week), adjustment_cents (sum of adjustments.amount_cents whose effective_date falls in the week, which may be positive or negative), gross_earnings_cents (orders + adjustments), guaranteed_earnings_cents, guarantee_topup_cents (max(guaranteed_earnings_cents - gross_earnings_cents, 0)), and final_payout_cents (max(gross_earnings_cents, guaranteed_earnings_cents)). Use DATE/TIMESTAMP filters explicitly for the range 2025-05-26 through 2025-06-01, and do not rely on any precomputed aggregates so that the query is idempotent if re-run.

Table base_pay_policy

columntypeconstraints
effective_fromDATENOT NULL
effective_toDATENOT NULL
base_per_order_centsINTNOT NULL
per_km_centsINTNOT NULL

Sample data:

base_per_order_centseffective_fromeffective_toper_km_cents
2002025-01-012025-12-31100

Table surge_periods

columntypeconstraints
city_idINTNOT NULL
start_tsTIMESTAMPNOT NULL
end_tsTIMESTAMPNOT NULL
surge_multiplierDECIMAL(4,2)NOT NULL

Sample data:

city_idend_tsstart_tssurge_multiplier
12025-05-30 20:00:002025-05-30 18:00:001.5
12025-06-01 02:00:002025-05-31 22:00:002

Table cancellation_policy

columntypeconstraints
statusVARCHAR(32)PRIMARY KEY
pay_centsINTNOT NULL

Sample data:

pay_centsstatus
0completed
300cancelled_partial
0cancelled_full

Table batches

columntypeconstraints
batch_idINTPRIMARY KEY
worker_idINTNOT NULL
shift_idINTNOT NULL
pickup_tsTIMESTAMPNOT NULL
dropoff_tsTIMESTAMPNOT NULL
distance_kmDECIMAL(6,2)NOT NULL
city_idINTNOT NULL

Sample data:

batch_idcity_iddistance_kmdropoff_tspickup_tsshift_idworker_id
1152025-05-29 10:30:002025-05-29 10:00:001101
2142025-05-30 18:45:002025-05-30 18:15:002101
3132025-06-01 00:10:002025-05-31 23:30:004102
4202025-05-27 12:00:002025-05-27 12:00:003102
5122025-05-31 09:00:002025-05-31 09:00:005103

Table orders

columntypeconstraints
order_idINTPRIMARY KEY
batch_idINTNOT NULL
worker_idINTNOT NULL
statusVARCHAR(32)NOT NULL
tip_centsINTNOT NULL
completed_tsTIMESTAMPNOT NULL

Sample data:

batch_idcompleted_tsorder_idstatustip_centsworker_id
12025-05-29 10:30:001completed150101
22025-05-30 18:40:002completed0101
22025-05-30 18:42:003completed250101
42025-05-27 12:05:004cancelled_partial0102
52025-05-31 09:05:005cancelled_full0103
32025-06-01 00:05:006completed100102

Table weekly_minimum_guarantee

columntypeconstraints
worker_idINTNOT NULL
week_start_dateDATENOT NULL
guaranteed_earnings_centsINTNOT NULL

Sample data:

guaranteed_earnings_centsweek_start_dateworker_id
20002025-05-26101
25002025-05-26102
15002025-05-26103

Table adjustments

columntypeconstraints
adjustment_idINTPRIMARY KEY
worker_idINTNOT NULL
effective_dateDATENOT NULL
amount_centsINTNOT NULL
reasonVARCHAR(100)NOT NULL
order_idINT

Sample data:

adjustment_idamount_centseffective_dateorder_idreasonworker_id
11002025-05-31NULLPromo bonus101
2-2002025-05-312Fraud chargeback101
35002025-06-01NULLWeekly incentive102

Expected output

One row per worker for the week starting 2025-05-26, showing earnings from orders, adjustments, gross earnings, applied minimum guarantees, any top-up amount, and the final payout in cents.

adjustment_centsfinal_payout_centsgross_earnings_centsguarantee_topup_centsguaranteed_earnings_centsorder_earnings_centsweek_start_dateworker_id
-10020001900100200020002025-05-26101
50025001900600250014002025-05-26102
0150001500150002025-05-26103

Hints

  1. Reuse the per-order payout logic in a CTE and then aggregate payouts by worker over the fixed week 2025-05-26 to 2025-06-01.
  2. Aggregate adjustments over the same date range, join to weekly_minimum_guarantee, and use CASE or GREATEST-style logic to compute guarantee top-ups and final payouts.