# Business Rules ## Durable Rules - Human access to desktop/H5 pages, detailed health, business APIs, uploads, traces and downloads requires an authenticated ARR Web session. Only login assets, `POST /api/login` and no-detail `/healthz` readiness are anonymous. - The single-operator login identity is injected as `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD`; real credentials are never stored in source or returned/logged. Missing credentials fail Web startup closed. - Login failures use a generic message and bounded attempt blocking. Logout revokes the server-side session; authenticated mutations continue to require CSRF. - The user uploads XML; the user does not submit a report year, report month, cutoff date, or separate monthly-generation request in the primary workflow. - Daily history and task trace show the validated browser-uploaded XML basename. Canonical internal source name `source.xml` is not user-facing; historical rows with no recorded basename display an unknown marker rather than a guessed filename. - ARR's frozen processor generates complete structured data, which must be independently validated and successfully committed to the database before downstream monthly processing starts. - The production XML path must not require Agent, MCP, prompts, model-generated JSON, fetch grants, or a public source URL. - A successful database commit automatically triggers the monthly-report processing program through the business system. - Automatic monthly processing runs in a dedicated worker process, not inside the Web request process. - The worker derives the affected month from retained `ARRIVAL` facts belonging to the committed daily version. - The monthly `as_of_date`/“更新至” is the greatest `ARRIVAL` actually included in the current monthly snapshot. The XML filename and wall-clock date are never authorities for it. - Year, month, cutoff/as-of date, and similar fields are internal derived execution/audit values. Their presence in code or metadata does not make them user inputs. - The monthly output contains the exact business dimension/field `TOTAL PRICE`. - Each monthly XLSX data-row `TOTAL PRICE` cell is a formula whose business meaning is `REAL PRICE × NIGHTS × NO_OF_ROOMS` (current layout example: `=R2*C2*G2`). - A stored database `total_price` may be used for integrity checking, but it does not replace the required formula in the exported workbook. - Monthly business/guest rows remain derived from current Finance facts. PostgreSQL persists only publication identity, lineage, channel counts and artifact metadata. - The outbox event is marked published only after a report is active/superseded and both workbook/result artifacts are registered; replay of the same snapshot must not create a duplicate publication. - A published monthly version automatically appears in the open monthly list; the primary workflow has no manual list-refresh step. - Company-channel detail generation is allowed for any period in the selected current month and for historical months. A future report month is rejected so the system does not create an intentionally empty future workbook. - Company-channel C/O periods keep fixed report cutoffs: `01-10` uses the 10th, `11-20` uses the 20th and `21-month-end` uses the selected month's natural month-end. The workbook uses the current committed Finance snapshot through that cutoff even when the calendar period has not ended; later-arriving facts require a rerun to be included. - The company-channel UI labels calendar status as `周期未结束` or `周期已结束`. These labels describe whether the selected period has reached its Bangkok completion boundary; they are not generation permission states. - In a company-channel detail workbook, a Finance fact with no normalized Group Code remains visible as its own row; `RES_COMMENT` and `Booking Room` are blank, and this expected blank does not create a warning or duplicate highlight. - A Finance stay segment with a consistent nonblank Group Code keeps that Group Code and its normal aggregation. When no current accepted Booking room can be resolved, only `Booking Room` is blank; the lookup miss does not create an error or warning and does not block the company's workbook. - The company-report Booking allocation model consists of normalized Group Code, room type and quantity. A raw intake adapter may derive these fields from a source workbook, but raw workbook columns and normalized result dimensions are separate contracts; worksheet, physical row, artifact hash and parse version remain provenance. - A raw Booking workbook is recognized only on a worksheet containing one Tour Code/Group Code header and the exact Thai `โรงแรม` header. Tour Codes are Unicode-normalized and all whitespace is removed before matching. - For repeated Tour Codes, the physically last source row replaces earlier rows. A final row with an explicit textual cancellation removes the Tour Code; cell color or other formatting alone never means cancellation. - Room extraction reads parenthesized hotel segments. A `【label】` token takes the following integer as quantity and defaults to one when no following integer exists. Multiple tokens become separate items even when they normalize to the same room type. - Numeric suffix variants of `U-TWN` and `U-DBL` normalize to `U-TWN` and `U-DBL`; `高级房TWN`/`高级房DBL` normalize to `TWN`/`DBL`. Unknown bracket labels retain their raw text and parsed/default quantity as pending. Recognized unbracketed names such as `6+4`, `Family Suite`, `Junior Suite` and `Superior Garden` also remain pending because the source omitted brackets. Surcharge, child-breakfast and guide-room tokens are not guest-room allocations. - Pending items do not contribute accepted Booking room quantity. Automatic and manual rows are both editable and deletable; activation requires zero pending items and at least one confirmed item. Re-extraction rebuilds the review draft from source and intentionally discards draft-only edits/deletions. - The review UI pages at 50 records. A delete request contains 1-50 unique item IDs and is all-or-nothing: every item must still be undeleted, belong to the same reviewing draft and be deletable, otherwise the transaction rolls back. - An open Booking review displays the draft artifact's validated browser-uploaded filename directly below `Booking记录提取`. Accepted historical-source readiness is not shown as provenance for draft extraction records. - Once accepted, a manually uploaded Booking workbook replaces the complete current source rather than appending silently to the previous workbook. Migrations 014/015 and the HTTP GET/POST/PATCH/DELETE adapter represent this boundary; the first real draft activation remains a controlled business acceptance action. - Booking normalizes the complete Group Code and globally aggregates repeated `(Group Code, room type)` rows by summed quantity. Worksheet, company/channel and stay dates are not current Booking join dimensions. This contract therefore requires one Group Code to identify one room allocation across those scopes; a different business reality requires a schema/view/report-join change, not merely extra unused Excel columns. - Contradictory Group Code source fields and invalid Finance dates, nights or price details remain strict company-report errors; the blank fallback applies only to absent or unresolved Booking enrichment. ## Last Reviewed 2026-07-31