01Staged extraction
I never hand the contract to the model whole. One pass identifies the property, one pulls the top-level terms, one lists the room types, then each room is extracted on its own, so a five-room contract costs nine separate calls. Most stages retry twice with backoff and I validate each one independently; one unparseable room does not cost the whole contract.
02Graded OCR
I grade raw pdf-parse text before trusting it: at least 300 characters, an alphanumeric ratio above 0.35, contract vocabulary present. Fail that and pages render at 300 DPI for GPT-4o Vision, with Tesseract behind it both per page and for the whole document. A signal score weights table markers such as currency codes, rate, occupancy and season, and keeps whichever pass produced the more table-like text.
03Splitting the year
Contracts state one long validity range, then list peak dates, surcharges and blackouts against it. My date splitter cuts that range so every exception becomes its own priced period and the normal ranges fill the gaps: a year with two single-day surcharges becomes five periods. I key global date periods on range and check-in days together, so two periods over the same dates that differ only by weekday survive as separate rows.
04Every price enumerated
The pricing generator expands each room into every chargeable combination: adults, children by age bracket, sharing an existing bed or taking an extra one, extra-bed type, meal plan, date period. It writes one worksheet per meal plan in the schema the downstream system reads. A second generator applies promotions on top, honouring per-promotion flags for whether a discount reaches extra beds and meals.
05Approve, then reuse
Nothing exports until every section is approved, and the dashboard is not the only thing enforcing it. Both export routes re-check server-side and return 400 if a section is outstanding. Once approved, a contract can be saved as a template, shared into another user's library, cloned, or used to seed policies into a fresh session; over the last eight months I added that templates system, a second export format in Uni-Hotel JSON, and a separate pipeline for promotions.