77 lines
7.9 KiB
Markdown
77 lines
7.9 KiB
Markdown
# Business Rules
|
||
|
||
## Durable Rules
|
||
|
||
- Human access to the desktop Finance workspace, detailed health, generic business APIs, uploads, traces and downloads
|
||
requires an authenticated ARR Web session. The H5 mobile dashboard is an intentional anonymous read-only exception:
|
||
only its page/assets, `/api/public/h5/months`, `/api/public/h5/analytics` and no-detail `/healthz` are public; the H5
|
||
projection contains aggregate channel/room metrics and omits source hashes and operational metadata.
|
||
- 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.
|
||
- Daily, monthly-publication and company-generation histories have independent viewing months and default to the most
|
||
recent month containing that record type. Viewing-month state is read-only: it resets list paging but never changes
|
||
monthly derivation or a company generation request. Company `生成月份` and history `查看月份` are separate inputs.
|
||
- 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
|