dimesum

Home / Blog / Money

Money

How to split an itemized bill when one dish wasn't shared

· 6 min read ·

Split a $525 dinner equally and one person quietly pays $70 for a dish they never touched. The fix is one expense with two rules: name who ate the unshared line, and let tax follow consumption.

Split a $525 dinner equally and one person overpays $70. Two of the three ate the $200 seafood platter and the third did not, yet an equal split charges all three $175. Dimesum handles that bill as one expense: equal at the top, with the $200 line carried only by the two who ordered it.

Mostly-equal bills with one unshared line are the commonest thing a splitting app is asked to do, and Dimesum's first data model could not express one. The earlier design made ITEMIZED a split type, mutually exclusive with EQUAL, PERCENT and SHARES. A real restaurant bill is both at once.

Itemising is detail on an expense, not a different kind of split

An itemised expense in Dimesum is an ordinary expense that happens to carry its lines. The expense keeps everything expenses already have: a total, participants, a split type, one or several payers. To that it adds items, the lines of the bill, and adjustments, the tax, tip, fees and discounts printed underneath them.

Two levels then decide every cent. The expense's split type divides any line with nobody named on it. A line with assignees is carried by exactly those people, and by nobody else.

The two levels that decide a line's split, from docs/tech/18-itemized-expenses.md §1.
LevelWhat it decidesApplies to
Expense split typeHow a shared cost divides: equally, by percent, or by weighted sharesEvery item with no assignees named
Item assigneesWho consumed this line, and in what proportionThat line only
A line with no assignees divides by the expense split type; a line with assignees is carried only by the people it names LEVEL 1: NOBODY NAMED ON THE LINE Shared plates $300 no assignees EQUAL over 3 Asha $100 Bhavna $100 Chetan $100 LEVEL 2: THE LINE NAMES WHO ATE IT Non-veg dish $200 assignees: Asha, Bhavna its 2 assignees Asha $100 Bhavna $100 Chetan $0
The two levels on the $500 of food. Shared plates name nobody, so EQUAL divides them three ways; the seafood dish names two people, so Chetan's food subtotal stops at $100.

A $525 dinner where the person who ate least pays $105

Asha, Bhavna and Chetan eat dinner, split equally at the top. Shared plates come to $300. A seafood dish comes to $200, and only Asha and Bhavna ate it. Service is 5%, and Chetan paid the whole $525.

Worked example: one bill, two lines, one adjustment, and the share each person carries.
LineAmountAshaBhavnaChetan
Shared plates, no assignees$300$100$100$100
Seafood platter, Asha and Bhavna$200$100$100$0
Subtotal$500$200$200$100
Service 5%, by subtotal$25$10$10$5
Total$525$210$210$105

Chetan paid $525 and owes $105, so he is owed $420: $210 from Asha and $210 from Bhavna. Who paid is orthogonal to who owes. The payer being the person who barely ate changes none of the shares, which is why Dimesum keeps payers at expense level and assignees at line level.

Tax follows what you ate, not how many of you there are

Chetan's share of the $25 service charge is $5, not $8.33. Every adjustment allocates in proportion to each person's item subtotal, so the 200:200:100 split of the food decides the split of the service charge as well. Allocating by headcount would quietly charge Chetan tax on a dish he never touched.

Someone who ordered nothing therefore pays no tax and receives no discount. The weights are the pre-adjustment subtotals rather than the running total, which also stops each adjustment's rounding from compounding into the next one's weights.

Adjustments apply in order, so a discount moves the GST base

A bill reading "$1,000, less 10%, plus 5% GST" charges GST on $900, and the GST line is $45. Reverse the two and GST is charged on $1,000, giving $50. Adjustments are an ordered list, and a percentage applies to the running total at its own position. Both orderings are real bills, so the list is ordered rather than a set.

The same discount and GST in two orders, showing the base each one is charged on and the total each produces Discount first GST first Item subtotal $1,000 less 10% of $1,000 discount of $100 $900 plus 5% GST of $900 GST of $45 $945 Bill total $945 Item subtotal $1,000 plus 5% GST of $1,000 GST of $50 $1,050 less 10% of $1,050 discount of $105 $945 Bill total $945 SAME BILL, FLAT $100 OFF INSTEAD OF 10% $100 off, then GST $945 GST, then $100 off $950
Two percentages land on the same $945 because the multiplication commutes, yet the GST line differs: $45 against $50. Swap the percentage discount for a flat $100 and the totals separate as well, $945 against $950. Order is arithmetic here, not presentation.
Why the order is stored explicitly

Each adjustment carries its own position. An ordering that rested on insertion order, or on an id generator staying monotonic, would silently change what everyone owes after a database restore.

Three validations stand between a typo and the arithmetic. A pool states either a flat amount or a rate in basis points, never both, because storing both stores a number and its own derivation. A negative amount is refused, with an error naming DISCOUNT as the way to write a reduction. A rate above 100,000 basis points is refused as well, a cap set far above the 10,000 basis points that mean 100%, because a hundred percent off is a real discount.

The four adjustment kinds in internal/platform/splitcalc, and what each does to the running total.
KindEffect on the running totalWhy it is its own kind
TAXAddsWording only; identical to the calculation
TIPAddsWording only; identical to the calculation
FEEAddsWording only; identical to the calculation
DISCOUNTSubtractsThe only kind that changes the arithmetic, so the direction comes from the kind and never from a sign

The total comes from the lines, and a typed total is only a checksum

An itemised total is derived: the sum of the items plus the sum of the adjustments. The lines already say what the bill costs, so a separately stated total would be a second source of truth that can disagree with the first. Somebody who mis-enters a bill fixes a line, which is where the mistake actually is, and the total follows. Correcting a bill that is already posted goes through an edit that restates its split, so the correction lands as a new version rather than as an overwrite.

A client may still send amount_minor, and Dimesum then treats it as a checksum rather than as the answer. Agreement means the client read the same bill. Disagreement stops the expense before any money moves.

An earlier version auto-added an "Other" line for whatever a stated total did not account for, and removed the line when the gap closed. That line worked, and it is gone. Deriving the total dissolved the problem the "Other" line existed to manage, which only arose because the total was stated twice.

Why EXACT and items are refused together

EXACT names each member's final amount. Items derive each member's amount. Both at once is over-determined: either they agree, in which case one is redundant, or they disagree, in which case the expense has two answers and no rule says which wins. Dimesum refuses the combination at the door rather than settling it with a precedence rule nobody would remember.

EQUAL, PERCENT and SHARES all combine with items, because each is a rule for dividing a shared cost rather than a set of final amounts.

Where a discount used to lose a cent

Every allocation uses the largest-remainder method, and each one conserves its own total exactly. Ties in that method used to go to the lowest member id, which handed the group's oldest member the extra cent on every equally split expense; the tie now rotates on a hash of the expense id. Shared lines are pooled and divided once rather than line by line, so the odd cent does not land on the same member for every line of the bill.

Our apportion function had a real defect here. Go's integer division truncates toward zero, so a negative total floored the wrong way and the leftover was never handed out: −$10.00 over weights of 100, 200 and 300 came back as −$9.99. A vanished cent would have made a correct discounted bill fail its own sum check, and the user would have been told their arithmetic was wrong. Negative totals are now apportioned by magnitude and negated back.

Enter the bill the way it is printed

Type the lines you can see, name the people who ate the ones that were not shared, and add the tax, tip, fee and discount in the order the receipt prints them. Dimesum derives the total, allocates each adjustment by what each person consumed, and leaves the payer out of it. Next time one dish costs twice what everyone else ordered, add it as its own line with two names on it.

Common questions

How do you split a restaurant bill when one person ordered something expensive?

Put that dish on its own line and name the people who ate it. In Dimesum the rest of the bill still divides by the expense's own rule, usually equally, while the named line is carried only by its assignees. Tax and service then allocate by each person's subtotal, so whoever skipped the dish pays less of both.

Should tax and tip be split equally or by what each person ordered?

By what each person ordered. Dimesum allocates every adjustment in proportion to each person's item subtotal rather than by headcount. In a $525 dinner where one person ate $100 of food, that person carries $5 of a $25 service charge instead of $8.33. Anyone who ordered nothing pays no tax at all.

Does the order of a discount and a tax change what the bill costs?

Yes. Adjustments apply in order against a running total, so a bill reading "$1,000, less 10%, plus 5% GST" charges the GST on $900 and the GST line is $45. Put the GST first and it is charged on $1,000, giving $50. With a flat $100 discount the totals differ as well, $945 against $950.

Why can't you use exact amounts on an itemised bill?

EXACT names each person's final amount and items derive it, so the two together give one expense two answers. Dimesum refuses the combination rather than picking a winner with a precedence rule nobody would remember. EQUAL, PERCENT and SHARES all work alongside items, because each is a rule for dividing shared lines rather than a set of final amounts.

Do you have to type the total of an itemised bill?

No. Dimesum derives an itemised total as the sum of the items plus the sum of the adjustments, because the lines already say what the bill costs. A total sent by a client is checked as a checksum, and a mismatch stops the expense before money moves. Correcting a wrong figure means correcting the line it came from.