Extracting Room Charges and Taxes From a Hotel Folio
10 min read · updated August 11, 2026
The obvious validation on a hotel folio is that the nightly rate times the number of nights equals the room charge. It fails on most real stays, and when it fails the folio is usually right. The check that does work is the one the document was designed around: the running balance.
A folio is a ledger, not a receipt
A receipt states what something cost. A folio is an account: every charge, tax, payment, adjustment and reversal posted against one guest’s stay, in the order it was posted, with a running balance after each. That structural difference drives everything else on this page.
Because it is a ledger, entries are never deleted. A charge posted in error appears, and so does its reversal as a separate line with the opposite sign, and both remain visible forever. An extractor that sums charge lines to get a total will double-count every reversed charge. Negative amounts are frequently rendered in parentheses rather than with a minus sign — (45.00) means minus forty-five — and a numeric parser that strips punctuation turns that into a positive charge, which is the single most damaging parse error available on this document because the sign flip is invisible in the result. The parenthesised negative is one of the cases a currency amount validation rule exists to catch.
Payments appear as lines too, with the opposite sign to charges, so a settled folio has a closing balance of zero. That zero is not an absence of information: it is the assertion that debits equal credits, and it is the most checkable fact on the page.
The rate is not one number
Hotels price per night, and the price differs between nights. A Thursday and a Saturday in the same stay commonly carry different rates, a rate can change mid-stay when a promotional block ends, and an upgrade applied on arrival changes it from that night forward. The confirmation email quotes one rate, usually the first night’s or an average, and the folio posts the actual rate each night as its own line.
So rate x nights disagrees with the room total whenever any night differs, which is most stays of more than two nights. Extract the room charge as a list of dated lines, not as a rate and a count. The average rate is derivable from the list; the list is not derivable from the average.
Taxes compound the problem because there are usually several, at different rates, on different bases. A stay can attract a state sales tax, a city or county transient occupancy tax, a tourism district assessment and a flat per-night fee, and the mandatory resort or destination fee is itself typically taxable. Extract each tax line separately with its own label, rate and base if stated, exactly as on the auction receipt page — a single tax scalar cannot be reconciled against anything.
A worked folio, to the cent
Take a synthetic three-night stay, with every figure below stated as an assumption rather than as an observation of any real property. Assume nightly rates of 189.00, 189.00 and 249.00, a mandatory resort fee of 35.00 per night, an occupancy tax of 14.75% applied to both room and resort fee, and one minibar charge of 18.00 which is assumed untaxed for the purposes of the illustration.
room, night 1 189.00 room, night 2 189.00 room, night 3 249.00 room subtotal 627.00 naive check: quoted rate 189.00 x 3 = 567.00 -- disagrees by 60.00, and the folio is correct resort fee 35.00 x 3 105.00 occupancy tax on room 627.00 x 0.1475 = 92.4825 -> 92.48 occupancy tax on resort fee 105.00 x 0.1475 = 15.4875 -> 15.49 minibar 18.00 total 857.97
Now the rounding detail that produces most of the one-cent discrepancies in folio reconciliation. Taxing each night individually and summing gives a different answer from taxing the subtotal:
per-night rounding 189.00 x 0.1475 = 27.8775 -> 27.88 189.00 x 0.1475 = 27.8775 -> 27.88 249.00 x 0.1475 = 36.7275 -> 36.73 sum 92.49 per-subtotal rounding 92.48 one cent apart, both defensible
Neither is wrong. Property management systems differ on which they do, and they can differ between tax types on one folio. The consequence for a validator is that a tolerance of a cent or two per tax line is correct behaviour rather than laziness — and equally, that a tolerance of a dollar hides real errors. Set it at the level the rounding rule can actually produce, which is roughly one cent per independently rounded line.
The total on this folio is not the stay total
The failure that produces wrong expense reports is the split folio. A business traveller commonly has room and tax routed to a company account and incidentals to their own card, and the property produces two folios for one stay: folio A with room and tax, folio B with the minibar, parking and laundry. Each carries its own total and each total is correct for what it contains.
Extract the folio identifier — usually the room number plus a suffix such as 412-A — and treat the reservation or confirmation number as the key that groups them. A stay total is the sum across folios sharing that key, and a pipeline that assumes one folio per stay will under-report by exactly the incidentals, quietly and consistently.
Related is the distinction between the guest ledger and the city ledger. A charge transferred to a direct-bill account leaves the guest folio as a transfer line and appears on an invoice the guest never sees. A transfer line looks like a payment and is not one: nobody has paid anything, the liability has moved. Where the folio labels a line as a transfer, keep that as its own line type rather than folding it into payments.
The checks worth running
Three checks, in descending order of value, all deterministic and none requiring the model to be confident about anything:
- The running balance walks. If the folio prints a balance column, each row’s balance must equal the previous row’s balance plus this row’s signed amount — a cross-field amount validation rule applied down a column. This is the strongest check available because it localises the error: the first row where the walk breaks is the row that was misread. Nothing else on any document in this cluster points at the specific bad field so precisely.
- Debits minus credits equals the closing balance. Weaker than the walk, but it works on folios that print no balance column, and it catches an entirely missed line — including one that fell onto a second page the extractor never received.
- Night count matches the dates. The number of dated room lines must equal the check-out date minus the check-in date. An off-by-one here usually means a day-use or early-departure charge was read as a room night, or a room line was dropped.
Store the check outcomes on the record. A folio that walks is a folio you can post to a ledger without review; a folio that does not walk goes to a human regardless of what a confidence score says, for the same reason set out under extraction confidence — a self-consistency check is a fact about the document, and a confidence score is a guess about the extractor.