From 0c057a8e75e7b5756872c2ba5e304ca74d291617 Mon Sep 17 00:00:00 2001 From: Wyndham ARR Date: Thu, 8 Oct 2026 20:19:27 +0800 Subject: [PATCH] Exclude cancelled reservations before ARR field review --- .../tasks/20261008-production-review-9e7b.md | 40 ++- ...iew-9e7b__eligibility-before-completion.md | 9 + README.md | 2 +- arr-opera-daily-ingest.skill | Bin 89945 -> 92058 bytes arr-opera-daily-ingest.zip | Bin 89945 -> 92058 bytes arr-opera-daily-ingest/SKILL.md | 7 +- .../references/business-rules.md | 9 +- .../data-structured-result.schema.json | 15 +- .../references/field-contracts.md | 5 +- .../references/structured-output.md | 5 +- .../references/structured-result.schema.json | 15 +- .../scripts/process_daily.py | 58 +++- .../scripts/validate_daily.py | 9 +- arr_ingestion/direct_postgres.py | 3 +- arr_ingestion/postgres.py | 37 ++- arr_ingestion/validation.py | 24 +- arr_web/DIRECT_DATA_ENTRY.md | 16 +- arr_web/arr_data_review.py | 93 +++++- arr_web/local_ohip.py | 2 +- arr_web/local_replay_database.py | 4 +- arr_web/processing_runtime.py | 1 + arr_web/static/app.js | 7 + arr_web/static/i18n.js | 1 + .../020_daily_cancelled_exclusion.down.sql | 26 ++ database/020_daily_cancelled_exclusion.sql | 43 +++ deploy/OHIP_RELEASE_HANDOVER.md | 2 + integrations/ohip/arr_data.py | 1 + integrations/ohip/reservation_status.py | 227 +++++++++++++ tests/javascript/arr_data_review.cjs | 25 ++ tests/local_postgres.py | 8 +- tests/test_arr_cancelled_ingestion.py | 169 ++++++++++ tests/test_arr_cancelled_review.py | 119 +++++++ tests/test_arr_cancelled_scope.py | 213 ++++++++++++ tests/test_arr_direct_data.py | 27 ++ .../test_arr_download_postgres_integration.py | 35 ++ tests/test_arr_download_runtime.py | 18 + tests/test_arr_ingestion_postgres.py | 2 +- tests/test_arr_opera_daily_ingest.py | 15 +- tests/test_ohip_reservation_status.py | 310 ++++++++++++++++++ 39 files changed, 1546 insertions(+), 56 deletions(-) create mode 100644 .project-docs/60-reflection/cases/20261008-production-review-9e7b__eligibility-before-completion.md create mode 100644 database/020_daily_cancelled_exclusion.down.sql create mode 100644 database/020_daily_cancelled_exclusion.sql create mode 100644 integrations/ohip/reservation_status.py create mode 100644 tests/test_arr_cancelled_ingestion.py create mode 100644 tests/test_arr_cancelled_review.py create mode 100644 tests/test_arr_cancelled_scope.py create mode 100644 tests/test_ohip_reservation_status.py diff --git a/.project-docs/30-worklog/tasks/20261008-production-review-9e7b.md b/.project-docs/30-worklog/tasks/20261008-production-review-9e7b.md index e947816..884f00b 100644 --- a/.project-docs/30-worklog/tasks/20261008-production-review-9e7b.md +++ b/.project-docs/30-worklog/tasks/20261008-production-review-9e7b.md @@ -12,7 +12,7 @@ ## Scope -- 修复生产 OHIP 查询、环境内任务恢复及接口字段人工完善;保留 XML 和原筛选、定价、日报/月报规则。 +- 修复生产 OHIP 查询、环境内任务恢复及接口字段人工完善;保留 XML、原费率筛选、定价、日报/月报规则,并落实用户确认的取消预订排除规则。 ## Intent And Constraints @@ -24,7 +24,7 @@ - Added source-field completion before deterministic processing: only candidate records' unresolved/invalid fields may be edited. Normal optional block/package omission is now recognized under the narrow Oracle association policy described below; unresolved, failed or contradictory evidence still needs resolution. Original bytes are retained, revisioned staff decisions record the authenticated actor, and confirmation freezes a derived source. The same download request resumes from its saved acquisition without querying OHIP again. Existing price review, Finance validation, daily generation and monthly outbox behavior remain in use; XML processing is retained. - Browser recovery is scoped to this service/source context and user, preventing an old sandbox request from restoring into the production instance. Lost save/finalize responses, concurrent revisions and interrupted confirmation recover without a second processing intent. - Updated the existing local service at `http://127.0.0.1:8875/` to run this isolated checkout, retaining hotel57106, credentials, private database and output storage. Previous private launcher/config/plist were backed up before restart. No production deployment, remote push or integration into the unowned dirty primary checkout occurred. -- Reused the complete, immutable real 2026-10-07 capture; verified all531 capture files and both original hashes before importing into request `45e0e20ee75a46e098a0e32d650133a9`. Its initial26 source decisions were1 room,18 block codes and7 package lists. The optional-association follow-up below reinterpreted only corroborated normal omissions, preserving all original bytes and staff decisions. Current live review is revision1 with only1 unresolved required room; no processing job, staff confirmations or real daily/monthly exist. Import and reanalysis made zero new Oracle calls. +- Reused the complete, immutable real 2026-10-07 capture; verified all531 capture files and both original hashes before importing into request `45e0e20ee75a46e098a0e32d650133a9`. Its initial26 source decisions were1 room,18 block codes and7 package lists. The optional-association follow-up below reinterpreted only corroborated normal omissions, preserving all original bytes and staff decisions. The later approved cancellation follow-up supersedes this pending-room state: the source review is finalized revision4 with zero field decisions, and the same10/7 request has entered the existing two-key price review. No real daily/monthly exists yet. Import, reanalysis and cancellation-scope continuation made zero new Oracle calls. ## Verification @@ -38,7 +38,7 @@ ## Follow-ups -- The user explicitly deferred API/XML population alignment. That issue remains separate and must not block fixing normal optional omission or be represented as resolved by this change. The remaining10/7 room belongs to confirmation300007418, a Cancelled/unassigned source reservation; do not invent a room or silently exclude the reservation. Real report acceptance still requires resolving the actual remaining input and, when resumed by the user, the desired source selection/text mappings. Existing price review and real daily/monthly acceptance remain pending. +- The user explicitly deferred API/XML population alignment. That issue remains separate and must not block fixing normal optional omission or be represented as resolved by this change. The user has now explicitly approved cancellation exclusion, so300007418 no longer requires a room.10/7 awaits only two existing manual-price keys (LIAN TAI/WHO1/Opera0 and QBD/GRPA3/Opera0); each affects one record/three nights. Do not choose a price, equate an Oracle0 with the processed price, or claim final daily/monthly acceptance. Source population/text alignment remains separately deferred.9/17 has received saved-status evidence but its old optional-source gaps were not reinterpreted this turn;30 active source decisions remain. - Integration owner should promote the accepted behavior and production compatibility evidence, then integrate this branch into the primary project. Keep this checkout while the local service runs from it. It is manually created, not eligible for automatic skill-managed retirement. - Production deployment/configuration and remote publishing are separate follow-ups. This turn only activated the local instance against the existing production read service. @@ -155,3 +155,37 @@ Read: memory-index, project-positioning, current-state latest September sections - Independent saved-evidence recheck: confirmation300007418/internal5131529 has no roomId in first/final search, detail roomStay/currentRoomInfo or arrival-day rate segments. Room calendar returned HTTP200 with empty roomCalendar and no associated room/history. `PM` is a room type, not a usable room number. Both search/detail show Cancelled and rateGL2200KR in the original whitelist. Existing capture cannot legitimately supply a room; cancellation is not asserted as the cause of missing data. No new Oracle call occurred. - Verification:46 JavaScript tests passed, including failure/conflict precedence for room issues, pending dates/drafts, explicit finalization and uncertain request recovery; node syntax and Git whitespace checks passed. CUA refreshed the live page and verified the new explanation for300007418,0/1 confirmed and disabled report generation. Selecting10/6 enabled download without submitting; returning to10/7 restored the same item. No new task or actual download was started. Authenticated local read confirms revision1/pending1/job=null; original source SHA remains805799ba2f3edd7f1cf18c14e5304908061d6194a241c5fc255192728b7f388a. Static assets were served immediately; no service restart was needed. - Private screenshot: `/Users/chillishark/Library/Application Support/ARR2.0/production-validation-20261007/room-missing-in-scope-20261008.png`. Result tab retained as deliverable. No real source/manual decision was changed and no real daily/monthly generated. Follow-up remains a verified room value or a separately requested business-rule/source-scope decision; do not invent a room, silently drop a candidate or call the report complete. Promote the more precise explanation to canonical product documentation at integration if useful. + +## Same-task Follow-up: Cancelled and Unassigned Is Not a Room-completion Task + +- User challenges the direction: a cancelled/unassigned reservation reasonably has no room. The previous repeated instruction to obtain/fill a real room was not an adequate product resolution. Original code's required-room validation explains the technical block but does not establish that the expanded API population should be subjected to an impossible business completion step. Do not invent a room or assume cancellation universally removes historical room assignments. +- Concurrent gate resumed the same owned feature task/worktree/branch successfully with no peers; retained project context applies. Planning gate Passed for read-only current-query/capture scope analysis, not approval to add a status exclusion. The user had earlier deferred broad XML/API population alignment; this challenge authorizes investigating the immediate cancellation/room contradiction, not silently adopting a universal new status filter. +- Current search passes only arrivalStartDate/arrivalEndDate/limit/offset. Saved actual result contains6 Cancelled rows;2 are in the rate whitelist. Confirmation300007418 is the only whitelisted cancelled/unassigned row; confirmation300755329 is whitelisted Cancelled but retains a valid room. Total58 source rows/40 rate candidates. Excluding only the row that causes a room error would be an arbitrary pass-making rule; a consistent proposed Cancelled exclusion affects2 rate candidates, not only1. +- Independent Oracle contract check confirms arrival dates, reservationStatuses, actualArrivals, expectedArrivals and dayOfArrivalCancels are separate parameters. The latter is only arrival-day cancellations, not a general exclude-cancelled switch. Saved production data proves this particular date-only request returned Cancelled; do not assert an undocumented universal API default or infer RES_DETAIL equivalence/CheckedOut-only selection. Official references: https://raw.githubusercontent.com/oracle/hospitality-api-docs/main/rest-api-specs/property/v1/rsv.json and https://docs.oracle.com/cd/E98457_01/opera_5_6_core_help/res_detail_help.htm. +- Product proposal made concrete from saved evidence: preserve all original58 source rows and their audit, but exclude cancelled reservations from daily candidates if the user selects that new reporting policy. Under the captured10/7 rate selection it affects2 of40 candidates. No claim is made that the remaining daily/monthly already passes pricing or full acceptance. +- Asked one business-scope clarification with the concrete2-row impact and reason: original code does not filter status, so the user must establish whether Cancelled belongs in the daily report. Options are exclude Cancelled while retaining original data, or retain current scope and leave10/7 unresolved. This is a business requirement, not a new skill/security permission requirement. While pending, no dependent status-filter implementation, real data edits, new Oracle reads, review finalization or report generation are authorized by elapsed time. +- Verification: read original source contract/current parameter sheet, inspect saved request and all58 captured reservation statuses, join them by typed Reservation ID to the unchanged reanalyzed data and original rate whitelist; independent official-document check. No program/business code or service change. Earlier in-scope missing-room copy is not a completed solution to this user objection; do not present it as such. +- Follow-up/promotion candidate: once the reporting scope is established, implement the selected consistent status policy before room completion, with explicit excluded-row lineage and unchanged source bytes. Separate entity eligibility from mandatory-field validation and distinguish cancelled with retained room from cancelled without room; neither may be guessed from the room field alone. Broad remaining XML/API report equivalence stays a distinct deferred issue. + + +## Same-task Follow-up: Approved Cancellation Exclusion + +- User explicitly selected “日报排除已取消预订” on 2026-10-08 after the scope explanation. This supersedes only the prior lack of a cancellation filter; no NoShow/actual-arrival/CheckedOut-only or broad XML/API population alignment is authorized. +- Concurrent Task Gate passed: same task/feature/codex, owned worktree and branch match; base2417b1a; no peers or overlaps. Primary unknown dirty documentation remains untouched. +- Project Context Loaded: retained task record, read-before-planning/planning-gate, memory-index, project-positioning, current-state, decision-index, ADR006/007, system-overview/module-map/data-flow, business-rules/success-criteria, evidence/reflection/commitments/stale indexes. Goal remains two input paths sharing deterministic Finance/monthly logic. Accepted new cancellation exclusion overrides the old status-blind classification only; preserve raw rows, rate/price/date/dedup rules and audited staff changes. Canonical September deployment/delegation restrictions are stale where October explicit production/multi-agent authorization applies. Gate Passed. +- Plan: carry verified reservation status from OHIP; use one cancellation predicate before field validation for both input paths; independently record excluded_cancelled without mislabelling rate exclusions; update schemas/Finance forward migration; append saved-status interpretation to pending reviews without changing source bytes or staff decisions; explicitly migrate only unprocessed local tasks to the new rule identity with backups and receipt; replay saved10/7 without Oracle calls, validate and activate local service. +- Boundary: ambiguous XML short codes such as CA are not guessed as cancellation. Clearly identified Cancelled/CXL values may be excluded; missing/unknown/conflicting evidence keeps existing behavior. Broad source-population alignment remains deferred. + + +### Approved Cancellation Exclusion — Outcome and Verification + +- Implemented processor4.3.0 for XML4/OHIP5: exclude explicit cancellations before missing-rate, required business values, pricing and room/date deduplication. Source rows remain in the structured audit under `excluded_cancelled` with exactly `RESERVATION_CANCELLED_EXCLUDED` and null price/channel facts. Rate exclusions keep their separate count. Unknown/missing/conflicting status remains governed by existing validation; no new NoShow or CheckedOut-only rule. Frozen legacy direct-MCP3 keeps its old scope. +- OHIP acquisition carries the verified `reservation_status` outside the15 business fields. Internal saved-status interpretation verifies every original capture file, request/response identity, both complete search rounds, sequence and search/detail status/version agreement, then appends immutable evidence to the derived source. There is no HTTP mutation route for statuses. Original acquisition bytes, source reanalysis and staff decisions remain intact; cancelled-row historical decisions stay audited but are no longer editable or blocking. +- Finance keeps schema4/5 compatibility by processor identity, adds forward migration020 with a separate nonnegative cancellation count and balanced total/outcome constraints, and checks020 before enabling either processing path. Old rows receive0 without changing released facts; rollback rejects existing cancellation facts. No old migration was edited. +- UI shows the cancellation count and retains original-data notice. Removed review items clear obsolete unsaved drafts; edits still in the review are preserved. Both processor distribution packages were rebuilt byte-identically to source (SHA256 `2017aaa192571f8e2869ede0a1e86310e6d05cdf517f53e94b17a0a9359bc3ba`). +- Verification:23 processor/XML/schema/package regressions passed;14 saved-status evidence tests passed;5 cancellation-review tests passed;114 review/source/direct processing/local wrapper regressions passed with disposable real PostgreSQL, no skipped checks. Two grouped root test commands also named a nonexistent runtime module and ended with import errors; no actual loaded test failed, and the intended `test_arr_direct_runtime` plus cancellation-review/status suite was subsequently run correctly:21 tests passed. Finance agent ran62 distinct scoped tests in batches, including019→020 migration preservation, restricted-role/constraint checks, XML and JSON cancellation handling, failed-batch audit, monthly formulas and no-reacquisition retry.48 JavaScript tests and syntax/whitespace checks passed. Counts overlap and must not be added as distinct coverage. +- Local activation: verified queue idle, stopped LaunchAgent and private PostgreSQL, made a cold full backup outside the live root, and dry-ran an explicit policy-pin upgrade on copied pending state. Only unprocessed/unfrozen tasks were eligible. The durable intent records every parent/new file hash and target policy; previous revisions, source bytes and receipts are retained. Old optional-source receipt was separately rebound under the new context; no staff decision changed. Both10/7 and9/17 received statuses from their existing complete captures. Migration020 applied only to the owned private local database, Finance version count stayed0; updated private launcher and restarted8875. +- Actual10/7 result:58 original rows;6 cancellations excluded (2 were among prior40 rate candidates),14 non-whitelist exclusions,0 duplicates,36 controlled candidates and2 unmatched-price rows. Source review pending0/finalized revision4 with0 manual field decisions. Authenticated browser explicitly continued the original request to the existing `needs_review` state; price items are LIAN TAI/WHO1/Opera0 and QBD/GRPA3/Opera0, each1 record/3 nights. No price was entered and no real daily/monthly was committed. The user chose to enter these two prices directly in the page; it is left open at the price-review panel for that action. No price value was supplied to the agent, so it cannot finalize pricing. Generating the actual report remains dependent on the user's saved price decision. +- Actual9/17 maintenance result:18 raw cancellations excluded from review; active decisions38→30, original unchanged, revision2. Its prior optional source omissions remain a separate pending follow-up, and this date was not submitted/processed or refetched. +- Private evidence: cold backup `/Users/chillishark/Library/Application Support/ARR2.0/policy43-service-backup-20261008-121313`; live `policy43-upgrade-intent.json`, `policy43-upgrade-completed.json`, `policy43-activation.json`; dry run `production-validation-20261007/policy43-dryrun-da8e24ea`; private executable `upgrade_cancelled_scope.py`. Browser screenshots `cancelled-scope-no-room-review-20261008.png` and `cancelled-scope-price-review-20261008.png`. The authenticated result tab is retained. No credential/environment/grant change, Oracle business read, production deployment, remote push or primary-checkout integration occurred. +- Promotion candidate: update canonical source-selection/business-rules/current-state with user-approved cancellation exclusion and processor4.3/020 requirements, retaining the deferred broad population alignment. Human confirmation for the cancellation policy is satisfied by this conversation; integration ownership is still required for canonical writes. diff --git a/.project-docs/60-reflection/cases/20261008-production-review-9e7b__eligibility-before-completion.md b/.project-docs/60-reflection/cases/20261008-production-review-9e7b__eligibility-before-completion.md new file mode 100644 index 0000000..cfcf41f --- /dev/null +++ b/.project-docs/60-reflection/cases/20261008-production-review-9e7b__eligibility-before-completion.md @@ -0,0 +1,9 @@ +# Check report eligibility before requesting missing values + +The repeated room-completion explanation was technically accurate about the old status-blind validator but led to the wrong product action: an unassigned Cancelled booking was treated as though staff must supply a room. Field requirements only apply after the record belongs in the report. A missing mandatory schema value does not by itself justify asking staff to fabricate or locate a value that may legitimately never exist. + +Inspect source status and report membership before opening field completion. If the old code has no relevant eligibility rule, distinguish the existing validator from the missing business decision. Explain the full population impact and obtain the business requirement; never drop just the row that fails validation. Here the user explicitly chose to exclude all cancelled bookings, affecting two rate candidates (six raw records), so both assigned and unassigned cancellations now follow the same audited decision before field checks and deduplication. + +Keep original evidence and source order, record exclusion separately from rate filtering, version the processing rules, and upgrade pending immutable identities explicitly. A safe code fix is not a final report: the real10/7 run subsequently reached legitimate two-key price review, requiring actual prices before Finance/monthly acceptance. + +Promotion candidate: add eligibility-before-completion as a project debugging/review principle at integration; no new blanket permission or approval workflow is proposed. diff --git a/README.md b/README.md index 18eb3ec..e2209b4 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ cp .env.example .env.local 均为 `true` 才表示页面处理和下载能力可用;未登录的容器只使用无详情的 `GET /healthz` readiness。 worker 是独立无端口进程,应由进程管理器单独保活。 -自动数据入口要求 `database/008_arr_mvp_v1_rebuild.sql` 加009–019增量迁移;019支持`ohip_json`来源和5.0结果,原XML保持4.0兼容。既有数据库实际版本须在部署时核对,不自动执行迁移。017必须在同一发布窗口先确认/应用016后才可应用;它增加价格复核case/item/event审计、`awaiting_review`生命周期、最终人工价格lineage和受保护回滚。018为冻结人工清单增加`manual_override_json`工件类型。012增加月报发布元数据和Finance日版本lineage;016允许新月报OSS工件并保留旧local记录;013保存用户上传XML文件名;014/015增加Booking当前整表指针和Excel提取草稿。ARR使用原有`artifact_callback`通用工件交付表,不读写009/010的grant/MCP submission表。 +自动数据入口要求 `database/008_arr_mvp_v1_rebuild.sql` 加009–020增量迁移;020记录已取消预订排除数量和原因;019支持`ohip_json`来源和5.0结果,原XML保持4.0兼容。既有数据库实际版本须在部署时核对,不自动执行迁移。017必须在同一发布窗口先确认/应用016后才可应用;它增加价格复核case/item/event审计、`awaiting_review`生命周期、最终人工价格lineage和受保护回滚。018为冻结人工清单增加`manual_override_json`工件类型。012增加月报发布元数据和Finance日版本lineage;016允许新月报OSS工件并保留旧local记录;013保存用户上传XML文件名;014/015增加Booking当前整表指针和Excel提取草稿。ARR使用原有`artifact_callback`通用工件交付表,不读写009/010的grant/MCP submission表。 ## Booking Excel 房型提取 diff --git a/arr-opera-daily-ingest.skill b/arr-opera-daily-ingest.skill index 4b600c526f7a60da3d8ce2159b24ae74ffed2f88..0ca58d9a3e4f921dc819593aead26261544d9b2d 100644 GIT binary patch delta 51229 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

  • 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 z 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 Q8dm%XB1x|uMP57b59*p5Bme*a delta 49086 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%0ATkqc}sqhILYW*)~*dT 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
  • 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 z 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 Q8dm%XB1x|uMP57b59*p5Bme*a delta 49086 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%0ATkqc}sqhILYW*)~*dT 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 Tuple[date, List[Dict or not isinstance(row.get("fields"), dict) or set(row["fields"]) != fields): invalid("接口预订身份、顺序或字段不完整") ids.add(row["reservation_id"]) + reservation_status = row.get("reservation_status", "") + if "reservation_status" in row and (not isinstance(reservation_status, str) or not reservation_status.strip()): + invalid("接口预订状态必须为非空文本") values, gaps = {}, [] for key, observation in row["fields"].items(): if not isinstance(observation, dict) or observation.get("state") not in { @@ -860,6 +866,7 @@ def read_data_source(path: Path, *, input_cleanup=None) -> Tuple[date, List[Dict record = normalize_source_record(values, index) record["_DATA_GAPS"] = gaps record["_SOURCE_KIND"] = "ohip_data" + record["_RESERVATION_STATUS"] = reservation_status.strip() records.append(record) if input_cleanup is not None: input_cleanup["ignored_emoji_count"] = ignored @@ -872,10 +879,10 @@ def read_source(path: Path, *, input_cleanup=None, ignore_emojis=True): return read_xml(path, input_cleanup=input_cleanup, ignore_emojis=ignore_emojis) -def classify_input_records(reservations, business_date): +def classify_input_records(reservations, business_date, *, apply_scope=True): if reservations and isinstance(reservations[0], dict): - return classify_normalized_records(reservations, business_date) - return classify_source_records(reservations, business_date) + return classify_normalized_records(reservations, business_date, apply_scope=apply_scope) + return classify_source_records(reservations, business_date, apply_scope=apply_scope) def required_text(record: ET.Element, tag: str, index: int, code: str) -> str: @@ -917,7 +924,17 @@ def source_record(node: ET.Element, index: int) -> Dict[str, Any]: DEPARTURE=node.findtext("TRUNC_END"), RES_COMMENT=first_nonempty(node.findall("./LIST_G_COMMENT_RESV_NAME_ID/G_COMMENT_RESV_NAME_ID/RES_COMMENT")), TRACE_TEXT=first_nonempty(node.findall("./LIST_G_DEPT_ID/G_DEPT_ID/TRACE_TEXT"))) - return normalize_source_record(fields, index) + # Reservation type (e.g. CA/TA/GC) is not a cancellation signal. Only these + # explicit status fields and the approved exact cancellation values count. + statuses = [text_or_blank(status.text) for tag in XML_RESERVATION_STATUS_FIELDS + for status in node.findall(tag) if text_or_blank(status.text)] + fields["RESERVATION_STATUS"] = statuses[0] if statuses else "" + record = normalize_source_record(fields, index) + normalized_statuses = {status.upper() for status in statuses} + record["_RESERVATION_STATUS_CONFLICT"] = bool( + normalized_statuses & CANCELLED_RESERVATION_STATUSES + and normalized_statuses - CANCELLED_RESERVATION_STATUSES) + return record def normalize_source_record(values: Mapping[str, Any], index: int) -> Dict[str, Any]: @@ -962,6 +979,8 @@ def normalize_source_record(values: Mapping[str, Any], index: int) -> Dict[str, "_SOURCE_ROW_NO": None, "_RAW_RATE_CODE": raw_rate_code, "_NORMALIZED_RATE_CODE": raw_rate_code.upper(), + "_RESERVATION_STATUS": text_or_blank(values.get("RESERVATION_STATUS")), + "_RESERVATION_STATUS_CONFLICT": False, "_GROUP_CODE_KEY": normalize_group_code(res_comment), "_COMPANY_KEY": company_key, "_OUTCOME": "pending", @@ -1082,19 +1101,29 @@ def build_record(node: ET.Element, index: int, business_date: date, rate_code: s def classify_source_records( - reservations: Sequence[ET.Element], business_date: date + reservations: Sequence[ET.Element], business_date: date, *, apply_scope: bool = True ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]], int, int, List[ErrorItem]]: """Classify every XML reservation and retain source order and duplicate lineage.""" all_records = [source_record(node, index) for index, node in enumerate(reservations, 1)] - return classify_normalized_records(all_records, business_date) + return classify_normalized_records(all_records, business_date, apply_scope=apply_scope) -def classify_normalized_records(all_records, business_date): +def is_cancelled_record(record: Mapping[str, Any]) -> bool: + """Recognize explicit cancellation only; absent, unknown or conflicting status stays in scope.""" + return (not record.get("_RESERVATION_STATUS_CONFLICT", False) + and text_or_blank(record.get("_RESERVATION_STATUS")).upper() in CANCELLED_RESERVATION_STATUSES) + + +def classify_normalized_records(all_records, business_date, *, apply_scope=True): candidates: List[Dict[str, Any]] = [] errors: List[ErrorItem] = [] removed_by_rate = 0 for record in all_records: + if apply_scope and is_cancelled_record(record): + record["_OUTCOME"] = "excluded_cancelled" + append_decision(record, "RESERVATION_CANCELLED_EXCLUDED") + continue normalized_rate = record["_NORMALIZED_RATE_CODE"] if not normalized_rate: error = xml_record_error( @@ -2048,7 +2077,7 @@ def validate_structured_completeness(payload: Dict[str, Any]) -> None: failures.append("source_sequence必须从1开始连续且保持XML顺序") counts = payload.get("outcome_counts", {}) if not isinstance(counts, dict) or set(counts) != FINAL_OUTCOMES: - failures.append("outcome_counts必须覆盖六种固定outcome") + failures.append("outcome_counts必须覆盖七种固定outcome") counts = {} for outcome in FINAL_OUTCOMES: actual = sum(1 for record in records if record.get("outcome") == outcome) @@ -2074,6 +2103,7 @@ def validate_structured_completeness(payload: Dict[str, Any]) -> None: payload.get("removed_by_rate_code", 0) + payload.get("removed_as_duplicates", 0) + payload.get("output_rows", 0) + + counts.get("excluded_cancelled", 0) ): failures.append("成功payload的源行分解不平衡") if payload.get("review_required_rows") or payload.get("review_issue_count"): @@ -2091,7 +2121,7 @@ def validate_structured_completeness(payload: Dict[str, Any]) -> None: failures.append("待复核payload必须包含缺价行") if payload.get("candidate_rows", 0) + payload.get("review_required_rows", 0) + counts.get( "excluded_rate_code", 0 - ) + counts.get("duplicate", 0) != source_rows: + ) + counts.get("duplicate", 0) + counts.get("excluded_cancelled", 0) != source_rows: failures.append("待复核payload的源行分解不平衡") issues = payload.get("review_issues") if not isinstance(issues, list) or payload.get("review_issue_count") != len(issues): @@ -2182,6 +2212,12 @@ def validate_structured_completeness(payload: Dict[str, Any]) -> None: or len(decisions) != len(dict.fromkeys(decisions)) ): failures.append("记录decision_codes必须是非空且不重复的数组") + if record.get("outcome") == "excluded_cancelled": + if decisions != ["RESERVATION_CANCELLED_EXCLUDED"]: + failures.append("取消排除记录必须保留明确的取消排除原因") + if any(record.get(field) is not None for field in ( + "real_price", "total_price", "kb_amount", "channel_key", "pricing_method")): + failures.append("取消排除记录不得参与定价或渠道归属") if record.get("normalized_rate_code") != ( text_or_blank(record.get("rate_code")).upper() or None ): @@ -2426,7 +2462,7 @@ def process(args: argparse.Namespace) -> int: removed_duplicates, classification_errors, ) = classify_input_records( - reservations, business_date + reservations, business_date, apply_scope=not legacy_v3_output ) metrics["removed_by_rate_code"] = removed_rate metrics["removed_as_duplicates"] = removed_duplicates diff --git a/arr-opera-daily-ingest/scripts/validate_daily.py b/arr-opera-daily-ingest/scripts/validate_daily.py index e68e6b8..9693d0c 100755 --- a/arr-opera-daily-ingest/scripts/validate_daily.py +++ b/arr-opera-daily-ingest/scripts/validate_daily.py @@ -457,6 +457,7 @@ def validate_structured_result_contract( manual_manifest: Optional[core.ManualOverrideManifest] = None, manual_override_path: Optional[Path] = None, ignore_emojis: bool = True, + apply_scope: bool = True, ) -> None: required = { "result_schema_version", @@ -536,6 +537,7 @@ def validate_structured_result_contract( ) expected_outcome_counts = { "duplicate": removed_duplicates, + "excluded_cancelled": source_rows - removed_by_rate - removed_duplicates - len(expected_records), "excluded_rate_code": removed_by_rate, "candidate": 0, "price_unmatched": 0, @@ -609,7 +611,7 @@ def validate_structured_result_contract( _date, reservations = core.read_source(xml_path, ignore_emojis=ignore_emojis) all_records, retained, _removed_rate, _removed_duplicates, classification_errors = ( - core.classify_input_records(reservations, business_date) + core.classify_input_records(reservations, business_date, apply_scope=apply_scope) ) if classification_errors: errors.append( @@ -763,6 +765,7 @@ def validate_review_structured_result_contract( ) expected_outcome_counts = { "candidate": candidate_rows, + "excluded_cancelled": sum(record.get("_OUTCOME") == "excluded_cancelled" for record in all_records), "duplicate": removed_duplicates, "excluded_rate_code": removed_by_rate, "price_unmatched": review_rows, @@ -978,6 +981,7 @@ def validate_legacy_direct_success_contracts( ) replay_outcomes = dict(outcomes) replay_outcomes["candidate"] = 0 + replay_outcomes["excluded_cancelled"] = 0 replay_structured["outcome_counts"] = replay_outcomes replay_artifacts = dict(artifacts) replay_artifacts["manual_override_json"] = None @@ -1008,6 +1012,7 @@ def validate_legacy_direct_success_contracts( replay_result, errors, ignore_emojis=False, + apply_scope=False, ) @@ -1201,7 +1206,7 @@ def validate(args: argparse.Namespace) -> List[core.ErrorItem]: xml_path, input_cleanup=input_cleanup, ignore_emojis=not legacy_v3_output ) all_records, records, removed_rate, removed_duplicates, classification_errors = ( - core.classify_input_records(reservations, business_date) + core.classify_input_records(reservations, business_date, apply_scope=not legacy_v3_output) ) if classification_errors: raise core.ProcessingFailure(classification_errors) diff --git a/arr_ingestion/direct_postgres.py b/arr_ingestion/direct_postgres.py index 7de1d2b..3e39091 100644 --- a/arr_ingestion/direct_postgres.py +++ b/arr_ingestion/direct_postgres.py @@ -756,12 +756,13 @@ class PostgresDirectIngestionRepository(PostgresIngestionRepository): duplicate_rows, validation_failed_rows, price_unmatched_rows, + excluded_cancelled_rows, validated_at, result_delivery_mode ) VALUES ( %s, %s, %s, %s, 'validated', %s, %s, %s, %s, - %s, %s, %s, %s, %s, %s, now(), 'direct_mcp' + %s, %s, %s, %s, %s, %s, %s, now(), 'direct_mcp' ) RETURNING id """, diff --git a/arr_ingestion/postgres.py b/arr_ingestion/postgres.py index 41bdb15..d72bd19 100644 --- a/arr_ingestion/postgres.py +++ b/arr_ingestion/postgres.py @@ -158,7 +158,7 @@ def _is_transient_database_error(error: Exception) -> bool: return isinstance(sqlstate, str) and sqlstate in TRANSIENT_SQLSTATES -def _outcome_counts(payload: Mapping[str, Any]) -> Tuple[int, int, int, int, int]: +def _outcome_counts(payload: Mapping[str, Any]) -> Tuple[int, int, int, int, int, int]: values = payload.get("outcome_counts") if not isinstance(values, Mapping): raise IngestionError( @@ -167,13 +167,14 @@ def _outcome_counts(payload: Mapping[str, Any]) -> Tuple[int, int, int, int, int ) try: counts = tuple( - int(values[name]) + int(values.get(name, 0) if name == "excluded_cancelled" else values[name]) for name in ( "retained", "excluded_rate_code", "duplicate", "validation_failed", "price_unmatched", + "excluded_cancelled", ) ) except (KeyError, TypeError, ValueError): @@ -337,6 +338,32 @@ class PostgresIngestionRepository(IngestionRepository): if any(value not in definitions.get(key, "") for key, value in expected.items()): raise IngestionError("DATABASE_MIGRATION_MISSING", "direct data entry requires migration 019") self._run_transaction(check, "direct data schema could not be checked") + self.assert_cancelled_scope_schema() + + def assert_cancelled_scope_schema(self) -> None: + """Require the count and outcome constraints before either input is enabled.""" + def check(cursor): + cursor.execute("""SELECT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'finance' AND table_name = 'daily_versions' + AND column_name = 'excluded_cancelled_rows' + AND data_type = 'integer' AND is_nullable = 'NO' + ), ( + SELECT pg_get_constraintdef(oid) FROM pg_constraint + WHERE conrelid = 'finance.daily_versions'::regclass + AND conname = 'daily_versions_counts_reconcile' + ), ( + SELECT pg_get_constraintdef(oid) FROM pg_constraint + WHERE conrelid = 'finance.daily_records'::regclass + AND conname = 'daily_records_outcome_check' + )""") + row = cursor.fetchone() + if (not row or row[0] is not True + or "excluded_cancelled_rows" not in str(row[1] or "") + or "excluded_cancelled" not in str(row[2] or "")): + raise IngestionError("DATABASE_MIGRATION_MISSING", + "ARR processing requires migration 020") + self._run_transaction(check, "ARR cancellation schema could not be checked") def register_job(self, registration: JobRegistration) -> None: if ( @@ -1809,13 +1836,14 @@ class PostgresIngestionRepository(IngestionRepository): duplicate_rows, validation_failed_rows, price_unmatched_rows, + excluded_cancelled_rows, failure_code, failure_message, validated_at ) VALUES ( NULL, NULL, %s, %s, %s, %s, %s, 'rejected', - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'deterministic processing failed', now() ) RETURNING id @@ -2047,11 +2075,12 @@ class PostgresIngestionRepository(IngestionRepository): duplicate_rows, validation_failed_rows, price_unmatched_rows, + excluded_cancelled_rows, validated_at ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'validated', - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, now() + %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, now() ) RETURNING id """, diff --git a/arr_ingestion/validation.py b/arr_ingestion/validation.py index fea6d22..6f454e9 100644 --- a/arr_ingestion/validation.py +++ b/arr_ingestion/validation.py @@ -483,6 +483,18 @@ V4_OUTCOME_FIELDS = { "retained", "validation_failed", } +CANCELLED_OUTCOME_PROCESSOR_VERSIONS = {"4.3.0"} + + +def _v4_outcome_fields(processor_version: str) -> set[str]: + # Schema 4/5 remain readable for older immutable deliveries. The new + # outcome is part of the explicitly approved 4.3 rule identity only; the + # delivery validator also binds that identity to the approved rule hash. + if processor_version in CANCELLED_OUTCOME_PROCESSOR_VERSIONS: + return V4_OUTCOME_FIELDS | {"excluded_cancelled"} + return V4_OUTCOME_FIELDS + + V4_RESULT_METRIC_FIELDS = { "source_rows", "removed_by_rate_code", @@ -519,7 +531,8 @@ def _validate_v4_records(payload: Mapping[str, Any]) -> Tuple[Dict[str, int], in records = payload.get("records") if not isinstance(records, list) or len(records) != source_rows: raise IngestionError("RESULT_CONTRACT_INVALID", "structured record count is invalid") - actual_outcomes = {outcome: 0 for outcome in V4_OUTCOME_FIELDS} + outcome_fields = _v4_outcome_fields(str(payload.get("processor_version"))) + actual_outcomes = {outcome: 0 for outcome in outcome_fields} manual_rows = 0 for index, record in enumerate(records, 1): values = _require_exact_mapping(record, RECORD_FIELDS, "structured record") @@ -528,7 +541,7 @@ def _validate_v4_records(payload: Mapping[str, Any]) -> Tuple[Dict[str, int], in if values.get("source_worksheet") is not None or values.get("source_row_no") is not None: raise IngestionError("RESULT_CONTRACT_INVALID", "direct XML source coordinates are invalid") outcome = values.get("outcome") - if outcome not in V4_OUTCOME_FIELDS: + if outcome not in outcome_fields: raise IngestionError("RESULT_CONTRACT_INVALID", "structured outcome is invalid") actual_outcomes[str(outcome)] += 1 duplicate_of = values.get("duplicate_of_source_sequence") @@ -635,8 +648,9 @@ def _validate_structured_payload_v4( "manually_priced_rows", ) } - outcomes = _require_exact_mapping(payload.get("outcome_counts"), V4_OUTCOME_FIELDS, "outcome counts") - for outcome in V4_OUTCOME_FIELDS: + outcome_fields = _v4_outcome_fields(envelope.processor_version) + outcomes = _require_exact_mapping(payload.get("outcome_counts"), outcome_fields, "outcome counts") + for outcome in outcome_fields: _require_nonnegative_integer(outcomes.get(outcome), f"outcome_counts.{outcome}") actual_outcomes, manual_rows = _validate_v4_records(payload) if dict(outcomes) != actual_outcomes: @@ -694,6 +708,7 @@ def _validate_structured_payload_v4( or outcomes.get("candidate") != 0 or counts["source_rows"] != counts["removed_by_rate_code"] + counts["removed_as_duplicates"] + counts["output_rows"] + + outcomes.get("excluded_cancelled", 0) or channel_rows != counts["output_rows"] or review_keys ): @@ -721,6 +736,7 @@ def _validate_structured_payload_v4( + counts["removed_as_duplicates"] + counts["candidate_rows"] + counts["review_required_rows"] + + outcomes.get("excluded_cancelled", 0) or channels or not review_keys or manual_ref is not None diff --git a/arr_web/DIRECT_DATA_ENTRY.md b/arr_web/DIRECT_DATA_ENTRY.md index bab6418..36de111 100644 --- a/arr_web/DIRECT_DATA_ENTRY.md +++ b/arr_web/DIRECT_DATA_ENTRY.md @@ -27,7 +27,7 @@ ## 字段完善的保存与继续处理 `DataFieldReviews`只接受`collection_complete=true`的完整采集,以当前冻结处理器的费率白名单和字段验证识别阻塞项。 -预订列表仍保留全部取得的记录及原顺序;非白名单记录按原规则处理,不因Cancelled/NoShow等状态在取数阶段被丢弃。 +预订列表仍保留全部取得的记录及原顺序;非白名单记录按原规则处理,不因Cancelled/NoShow等状态在取数阶段被丢弃。处理阶段按用户已确认的规则排除已取消预订,再识别其余候选的待完善字段;NoShow没有新增排除规则。 费率本身未确定时先完善费率,再重新识别该记录的其余问题。查询失败或采集不完整仍走原重试流程。 人工仅能完善任务列出的异常字段。团队代码、预订备注、套餐和房型允许明确“确认无此项”; @@ -112,7 +112,7 @@ LC_ALL=C LANG=C .venv/bin/python -m arr_web.local_ohip serve \ ## 正常运行配置 -数据库须已应用019迁移;2026-09-18首次验证只在独立本机测试库应用,未修改当时既有数据库。 +数据库须已应用020迁移;2026-09-18首次验证只在独立本机测试库应用,未修改当时既有数据库。 该迁移允许真实的 `ohip_json` 源制品和5.0结果进入已有复核流程。 启动会先检查迁移,未具备条件时不开放下载服务。原来的月报后台程序仍须正常运行。 @@ -125,8 +125,7 @@ LC_ALL=C LANG=C .venv/bin/python -m arr_web.local_ohip serve \ 以上三个 OHIP 参数必须一起提供;数据库、对象存储、网页登录沿用现有私有配置。 凭据文件仅在实际点击获取数据时读取,不在启动时查询酒店。 -私有任务目录须在仓库之外,并绑定酒店、来源类型、查询上限和处理规则;规则更换后使用新目录, -不将旧任务悄悄改绑。旧版本未完成的价格复核遵循原有“规则已变更”保护。 +私有任务目录须在仓库之外,并绑定酒店、来源类型、查询上限和处理规则;规则更换后通常使用新目录;同环境尚未进入处理且未冻结的待完善任务,可在停服务、备份和逐文件升级收据保护下显式接续,不能悄悄改绑。旧版本未完成的价格复核遵循原有“规则已变更”保护。 也支持 `DirectARRSource` 注入共享运行依赖。原有 `CapturedARRSource` 为历史 XML 模式保留, 它不再是新数据入口的前置条件。不得把模拟传输当成实际平台传输。 @@ -142,10 +141,17 @@ ARR_TEST_LOCAL_POSTGRES=1 .venv/bin/python -m unittest tests.test_arr_direct_dat ``` 检查覆盖6笔正常订单总额18200、字段完善后继续处理、既有缺价复核、必填及可空字段验证、去重、原始数据保留、独立报表核对、 -字段修订冲突与冻结恢复、页面请求、月报公式、提交后断线恢复、来源版本隔离,以及019迁移检查和回退保护。 +字段修订冲突与冻结恢复、页面请求、月报公式、提交后断线恢复、来源版本隔离,以及019/020迁移检查和回退保护。 浏览器检查:`tests/browser/arr_direct_data.cjs`,只接受明确指定的本机模拟实例。 历史8875沙箱曾交给用户会话后台服务运行,避免对话结束后登录页无法连接。仅在用户会话运行; 该历史OHIP沙箱的停止命令为 `launchctl bootout gui/$(id -u)/com.arr.local-ohip.8875`。服务配置位于私有实例的 `service.plist`;重新加载使用 `launchctl bootstrap gui/$(id -u) /absolute/private/instance/service.plist`。 后台环境已设 `LC_ALL=C`、`LANG=C`。先停后台服务再使用前台serve命令,不同时启动两份。 + + +## 已取消预订(2026-10-08确认) + +日报排除已取消预订,无论是否已有房号。取消单不要求补房号或其他报表字段,不参与去重、定价及月报;完整原始数据和排除原因保留。 +接口使用已核对一致的查询与详情预订状态;旧待完善任务可由内部维护操作从原始完整捕获补充状态,不重新查询Oracle,不修改原始数据或人工决定。XML使用明确的取消状态值CXL/CANCELLED/CANCELED;CA等未确认缩写不猜测。 +处理器版本4.3.0,Finance需先应用`database/020_daily_cancelled_exclusion.sql`。旧已发布结果不重新处理。 diff --git a/arr_web/arr_data_review.py b/arr_web/arr_data_review.py index db47da2..35016ad 100644 --- a/arr_web/arr_data_review.py +++ b/arr_web/arr_data_review.py @@ -30,6 +30,7 @@ from integrations.ohip.collect_arr_source import CollectionError VERSION = "arr-source-field-review/v1" REANALYSIS_POLICY = "oracle-optional-association/v1" REANALYSIS_VERSION = "arr-source-reanalysis/v1" +STATUS_POLICY = "arr-exclude-cancelled/v1" LIMIT = 100 * 1024 * 1024 OPTIONAL = frozenset({"BLOCK_CODE", "RES_COMMENT", "PRODUCTS", "ROOM_CATEGORY_LABEL"}) LABELS = { @@ -136,6 +137,7 @@ class DataFieldReviews: and reference.get("source_manifest_sha256") == meta["source_manifest_sha256"], "data_review_reanalysis_context_changed") self._reanalyzed_source(directory, original, state) + self._status_evidence(directory, meta, original, state) return meta, original, state def _publish(self, directory, state): @@ -221,9 +223,47 @@ class DataFieldReviews: require(fingerprint(receipt) == fingerprint(expected_receipt), "data_review_reanalysis_receipt_changed") return reanalyzed, receipt + def _status_evidence(self, directory, meta, original, state): + reference = state.get("reservation_status_evidence") + if reference is None: + return None + require(type(reference) is dict and reference.get("policy_id") == STATUS_POLICY, + "data_review_status_reference_invalid") + digest = reference.get("sha256", "") + require(type(digest) is str and bool(re.fullmatch(r"[0-9a-f]{64}", digest)) + and reference.get("file") == f"reservation-status-{digest}.json", + "data_review_status_reference_invalid") + raw = protected_read(directory / reference["file"], LIMIT) + require(_hash(raw) == digest, "data_review_status_evidence_changed") + evidence = strict_json(raw) + require(evidence.get("version") == "arr-reservation-status-evidence/v1" + and evidence.get("policy_id") == STATUS_POLICY + and evidence.get("original_sha256") == meta["original_sha256"] + and evidence.get("source_manifest_sha256") == meta["source_manifest_sha256"] + and evidence.get("report_date") == meta["report_date"] + and evidence.get("hotel_id") == original["hotel_id"] + and type(evidence.get("records")) is list + and len(evidence["records"]) == len(original["records"]), + "data_review_status_context_changed") + for row, status in zip(original["records"], evidence["records"]): + require(type(status) is dict and status.get("source_sequence") == row["source_sequence"] + and status.get("reservation_id") == row["reservation_id"] + and type(status.get("reservation_status")) is str + and bool(status["reservation_status"].strip()) + and ("reservation_status" not in row + or row["reservation_status"] == status["reservation_status"]) + and type(status.get("sources")) is list and bool(status["sources"]), + "data_review_status_records_changed") + return evidence + def _derive(self, directory, original, state): source, _receipt = self._reanalyzed_source(directory, original, state) data = copy.deepcopy(source) + meta = strict_json(protected_read(directory / "identity.json", 65536)) + evidence = self._status_evidence(directory, meta, original, state) + if evidence is not None: + for row, status in zip(data["records"], evidence["records"]): + row["reservation_status"] = status["reservation_status"] for item_id, decision in state["decisions"].items(): sequence, field = item_id.split(":") row = data["records"][int(sequence) - 1] @@ -246,6 +286,8 @@ class DataFieldReviews: business_date, records = self.rules.read_data_source(Path(stream.name)) issues = {} for row, record in zip(data["records"], records): + if self.rules.is_cancelled_record(record): + continue rate = record["_NORMALIZED_RATE_CODE"] fields = {} if not rate or "RATE_CODE" in record["_DATA_GAPS"]: @@ -273,7 +315,10 @@ class DataFieldReviews: def _public(self, directory, meta, original, state): data = self._derive(directory, original, state) issues = self._issues(directory, data) - keys = set(issues) | set(state["decisions"]) + cancelled_sequences = {row["source_sequence"] for row in data["records"] + if self.rules.is_cancelled_record({"_RESERVATION_STATUS": row.get("reservation_status", "")})} + keys = set(issues) | {key for key in state["decisions"] + if int(key.split(":")[0]) not in cancelled_sequences} items = [] for item_id in sorted(keys, key=lambda key: (int(key.split(":")[0]), key.split(":")[1])): sequence, field = item_id.split(":") @@ -289,6 +334,7 @@ class DataFieldReviews: return {"request_id": meta["request_id"], "report_date": meta["report_date"], "revision": state["revision"], "status": state["status"], "items": items, "pending_count": len(issues), "total_count": len(items), + "excluded_cancelled_count": len(cancelled_sequences), "can_finalize": not issues and state["status"] == "editing"} def prepare(self, request_id, payload, manifest_sha256, report_date): @@ -322,13 +368,48 @@ class DataFieldReviews: # A complete ordinary source needs no human step or derived source. # A reanalysis remains an explicit review until finalized, even if # its validated source interpretation resolves every listed gap. - return review if review["total_count"] or "source_reanalysis" in state else None + return review if review["total_count"] or "source_reanalysis" in state or "reservation_status_evidence" in state else None def get(self, request_id): directory = self._directory(request_id) with self._lock(directory): return self._public(directory, *self._read(directory)) + def apply_saved_status_evidence(self, request_id, capture_root, *, expected_revision): + """Bind saved search/detail statuses; no HTTP route or new business read. + + Evidence is reconstructed from the complete original capture, never from + staff-entered status values. Fields, order and original bytes stay intact. + """ + from integrations.ohip.reservation_status import saved_status_evidence + directory = self._directory(request_id) + with self._lock(directory): + meta, original, state = self._read(directory) + if state["status"] != "editing" or (directory / "finalize-intent.json").exists(): + raise _error("FROZEN", "已确认生成,预订状态不能再补充", 409) + evidence = saved_status_evidence(capture_root, meta["source_manifest_sha256"], + protected_read(directory / "original.json", LIMIT)) + raw = json_bytes(evidence) + digest = _hash(raw) + if type(expected_revision) is not int: + self._revision(state, expected_revision) + prior = state.get("reservation_status_evidence") + if prior is not None: + if prior.get("sha256") == digest: + return self._public(directory, meta, original, state) + raise _error("CONFLICT", "该来源已补充预订状态,请核对现有记录", 409) + self._revision(state, expected_revision) + reference = {"policy_id": STATUS_POLICY, "file": f"reservation-status-{digest}.json", "sha256": digest} + _write_once(directory / reference["file"], raw) + state["reservation_status_evidence"] = reference + state["revision"] += 1 + state["events"].append({"revision": state["revision"], "action": "reservation_status_evidence", + "policy_id": STATUS_POLICY, "evidence_sha256": digest, + "at": datetime.now(timezone.utc).isoformat()}) + result = self._public(directory, meta, original, state) + self._publish(directory, state) + return result + def apply_source_reanalysis(self, request_id, payload, manifest_sha256, policy_id, *, expected_revision): """Attach a validated offline reinterpretation, without changing acquisition or staff decisions. @@ -441,6 +522,10 @@ class DataFieldReviews: self._revision(state, revision) actor = self._actor(actor) data = self._derive(directory, original, state) + sequence = int(item_id.split(":")[0]) if isinstance(item_id, str) and re.fullmatch(r"[1-9][0-9]*:[A-Z_]+", item_id) else 0 + if 1 <= sequence <= len(data["records"]) and self.rules.is_cancelled_record( + {"_RESERVATION_STATUS": data["records"][sequence - 1].get("reservation_status", "")}): + raise _error(message="已取消预订不参与日报,无需完善字段") if item_id not in self._issues(directory, data) and item_id not in state["decisions"]: raise _error(message="只能完善当前任务列出的异常字段") sequence, field = item_id.split(":") @@ -470,6 +555,10 @@ class DataFieldReviews: _source, receipt = self._reanalyzed_source(directory, original, state) if receipt is not None: audit["source_reanalysis"] = {**receipt, "receipt_sha256": state["source_reanalysis"]["receipt_sha256"]} + evidence = self._status_evidence(directory, meta, original, state) + if evidence is not None: + audit["reservation_status_evidence"] = {**evidence, + "evidence_sha256": state["reservation_status_evidence"]["sha256"]} data = self._derive(directory, original, state) data["manual_data_review"] = {"manifest": audit, "manifest_sha256": fingerprint(audit)} binding = fingerprint({"source_manifest_sha256": meta["source_manifest_sha256"], diff --git a/arr_web/local_ohip.py b/arr_web/local_ohip.py index 71dd6c3..21f5633 100644 --- a/arr_web/local_ohip.py +++ b/arr_web/local_ohip.py @@ -215,7 +215,7 @@ def open_portal(root, port): load_key(credential_file) access = AccessState(root) with job_lock(root): - database = ReplayDatabase(root, schema_version=19) + database = ReplayDatabase(root, schema_version=20) try: database.start() def factory(*, root, snapshot, **dependencies): diff --git a/arr_web/local_replay_database.py b/arr_web/local_replay_database.py index cb89cf6..788a6b6 100644 --- a/arr_web/local_replay_database.py +++ b/arr_web/local_replay_database.py @@ -14,8 +14,8 @@ from integrations.ohip.capture_job import atomic_json, private_directory class ReplayDatabase: - def __init__(self, root: Path, *, schema_version: int = 18): - if schema_version not in (18, 19): + def __init__(self, root: Path, *, schema_version: int = 20): + if schema_version not in (18, 19, 20): raise ValueError("unsupported_local_schema") self.schema_version = schema_version self.root = root diff --git a/arr_web/processing_runtime.py b/arr_web/processing_runtime.py index e957a9e..e5daffc 100644 --- a/arr_web/processing_runtime.py +++ b/arr_web/processing_runtime.py @@ -79,6 +79,7 @@ def compose_programmatic_processing( database_config, connect=connect, ) + repository.assert_cancelled_scope_schema() policy = load_processor_policy(project_root) ingestion = IngestionService(DeliveryValidator(object_store, policy), repository) processor = LocalDailyProcessor(policy) diff --git a/arr_web/static/app.js b/arr_web/static/app.js index f483c50..4518f5b 100644 --- a/arr_web/static/app.js +++ b/arr_web/static/app.js @@ -682,6 +682,9 @@ $("#arr-data-review-progress").textContent = review ? I18N.t("data_review.progress", { date: review.report_date, completed: Number(review.total_count) - Number(review.pending_count), total: review.total_count, }) : I18N.t("data_review.loading"); + if (Number.isInteger(review?.excluded_cancelled_count) && review.excluded_cancelled_count > 0) { + $("#arr-data-review-progress").textContent += ` · ${I18N.t("data_review.excluded_cancelled", { count: review.excluded_cancelled_count })}`; + } $("#arr-data-review-refresh").textContent = I18N.t("data_review.refresh"); $("#arr-data-review-refresh").disabled = busy; const finalize = $("#arr-data-review-finalize"); @@ -731,6 +734,10 @@ state.arrDataReview = review; state.arrDataReviewDisconnected = false; state.arrDataReviewFinalizing = review.status === "finalized"; + const currentItems = new Set((review.items || []).map((item) => item.item_id)); + Object.keys(state.arrDataReviewDrafts).forEach((itemId) => { + if (!currentItems.has(itemId)) delete state.arrDataReviewDrafts[itemId]; + }); review.items?.forEach((item) => { if (item.confirmed && state.arrDataReviewDrafts[item.item_id] === arrDataReviewInputValue(item)) delete state.arrDataReviewDrafts[item.item_id]; }); diff --git a/arr_web/static/i18n.js b/arr_web/static/i18n.js index cd1a73f..6f5bc69 100644 --- a/arr_web/static/i18n.js +++ b/arr_web/static/i18n.js @@ -170,6 +170,7 @@ "data_review.refresh": ["刷新字段", "Refresh fields", "รีเฟรชข้อมูล"], "data_review.loading": ["正在读取待完善字段…", "Loading fields for review…", "กำลังโหลดข้อมูลที่ต้องตรวจสอบ…"], "data_review.no_items": ["没有待完善字段。", "No fields need review.", "ไม่มีข้อมูลที่ต้องตรวจสอบ"], + "data_review.excluded_cancelled": ["已排除 {count} 笔取消预订,原始数据保留", "{count} cancelled reservations excluded; original data retained", "ไม่รวมการจองที่ยกเลิก {count} รายการ โดยเก็บข้อมูลต้นฉบับไว้"], "data_review.region": ["待完善报表字段", "Report fields to review", "ข้อมูลรายงานที่ต้องตรวจสอบ"], "data_review.disconnected": ["字段读取中断,请刷新字段后继续。", "Field connection lost. Refresh the fields to continue.", "การเชื่อมต่อข้อมูลขัดข้อง โปรดรีเฟรชข้อมูลเพื่อดำเนินการต่อ"], "data_review.saving": ["正在保存并确认字段…", "Saving and confirming field…", "กำลังบันทึกและยืนยันข้อมูล…"], diff --git a/database/020_daily_cancelled_exclusion.down.sql b/database/020_daily_cancelled_exclusion.down.sql new file mode 100644 index 0000000..6cbe5e9 --- /dev/null +++ b/database/020_daily_cancelled_exclusion.down.sql @@ -0,0 +1,26 @@ +-- Rollback is safe only before any cancellation-exclusion facts are committed. +BEGIN; +DO $$ +BEGIN + IF current_database() <> 'booking_test' THEN + RAISE EXCEPTION 'ARR migration is allowed only in booking_test'; + END IF; + IF EXISTS (SELECT 1 FROM finance.daily_versions WHERE excluded_cancelled_rows <> 0) + OR EXISTS (SELECT 1 FROM finance.daily_records WHERE outcome = 'excluded_cancelled') THEN + RAISE EXCEPTION 'refusing rollback: cancellation-exclusion facts exist'; + END IF; +END $$; + +ALTER TABLE finance.daily_records DROP CONSTRAINT daily_records_outcome_check; +ALTER TABLE finance.daily_records + ADD CONSTRAINT daily_records_outcome_check CHECK (outcome IN ( + 'retained', 'excluded_rate_code', 'duplicate', 'validation_failed', 'price_unmatched' + )); +ALTER TABLE finance.daily_versions DROP CONSTRAINT daily_versions_counts_reconcile; +ALTER TABLE finance.daily_versions + ADD CONSTRAINT daily_versions_counts_reconcile CHECK ( + source_rows = retained_rows + excluded_rate_code_rows + duplicate_rows + + validation_failed_rows + price_unmatched_rows + ); +ALTER TABLE finance.daily_versions DROP COLUMN excluded_cancelled_rows; +COMMIT; diff --git a/database/020_daily_cancelled_exclusion.sql b/database/020_daily_cancelled_exclusion.sql new file mode 100644 index 0000000..8532b05 --- /dev/null +++ b/database/020_daily_cancelled_exclusion.sql @@ -0,0 +1,43 @@ +-- Add an auditable cancellation exclusion count without changing released facts. +-- PostgreSQL 15+. Migrations 008 through 019 remain immutable. +BEGIN; +DO $$ +BEGIN + IF current_database() <> 'booking_test' THEN + RAISE EXCEPTION 'ARR migration is allowed only in booking_test'; + END IF; + IF to_regclass('finance.daily_versions') IS NULL + OR to_regclass('finance.daily_records') IS NULL + OR NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = 'ingestion.artifacts'::regclass + AND conname = 'artifacts_artifact_kind_check' + AND pg_get_constraintdef(oid) LIKE '%ohip_json%' + ) THEN + RAISE EXCEPTION 'ARR migration 019 must be applied first'; + END IF; +END $$; + +ALTER TABLE finance.daily_versions + ADD COLUMN excluded_cancelled_rows integer NOT NULL DEFAULT 0 + CHECK (excluded_cancelled_rows >= 0); + +ALTER TABLE finance.daily_versions + DROP CONSTRAINT daily_versions_counts_reconcile; +ALTER TABLE finance.daily_versions + ADD CONSTRAINT daily_versions_counts_reconcile CHECK ( + source_rows = retained_rows + excluded_rate_code_rows + duplicate_rows + + validation_failed_rows + price_unmatched_rows + excluded_cancelled_rows + ); + +ALTER TABLE finance.daily_records + DROP CONSTRAINT daily_records_outcome_check; +ALTER TABLE finance.daily_records + ADD CONSTRAINT daily_records_outcome_check CHECK (outcome IN ( + 'retained', 'excluded_rate_code', 'duplicate', 'validation_failed', + 'price_unmatched', 'excluded_cancelled' + )); + +COMMENT ON COLUMN finance.daily_versions.excluded_cancelled_rows IS + 'Source rows explicitly excluded as cancelled under the pinned processor rules; older versions remain zero.'; +COMMIT; diff --git a/deploy/OHIP_RELEASE_HANDOVER.md b/deploy/OHIP_RELEASE_HANDOVER.md index 53ae99a..e7cf978 100644 --- a/deploy/OHIP_RELEASE_HANDOVER.md +++ b/deploy/OHIP_RELEASE_HANDOVER.md @@ -1,3 +1,5 @@ +2026-10-08补充:用户已确认日报排除已取消预订。当前处理器4.3.0需在019后应用020;Web与处理器同时发布,月报复用保留的日报事实。完整原始数据保留,取消单不补房号。已有未处理任务接续必须备份并显式记录新旧规则身份,不能复用旧身份直接运行新规则。下文2026-09-18部署/测试情况为历史记录,不代表当前本机生产连接状态。 + # ARR 自动取数交付与生产启用说明 交付日期:2026-09-18。状态:**开发收尾完成,生产配置和真实数据验收待完成。** diff --git a/integrations/ohip/arr_data.py b/integrations/ohip/arr_data.py index 96d6d53..aad1048 100644 --- a/integrations/ohip/arr_data.py +++ b/integrations/ohip/arr_data.py @@ -375,6 +375,7 @@ def _record(search, detail, reader, sequence): "DEPARTURE": observe(lambda: dates().departure.isoformat()), } return {"source_sequence": sequence, "reservation_id": base.reservation_id(detail), + "reservation_status": detail["reservationStatus"], "fields": values, "related": related} diff --git a/integrations/ohip/reservation_status.py b/integrations/ohip/reservation_status.py new file mode 100644 index 0000000..069ee54 --- /dev/null +++ b/integrations/ohip/reservation_status.py @@ -0,0 +1,227 @@ +"""Read-only reservation status evidence from a pinned, complete data capture. + +This module never creates a transport or changes acquired data. Unknown nonempty +Oracle status strings remain verbatim evidence; no abbreviations are interpreted. +""" +from __future__ import annotations + +import hashlib +import os +from pathlib import Path +import re +import stat +from urllib.parse import parse_qs, urlsplit + +from . import collect_arr_source as base +from .audit_arr_capture import protected_read +from .data_client import FETCH, typed_id + + +VERSION = "arr-reservation-status-evidence/v1" +POLICY_ID = "arr-exclude-cancelled/v1" +SOURCE_VERSION = "arr-ohip-data/v1" +MAX_DATA_BYTES = 25 * 1024 * 1024 +FILE_PATTERN = re.compile(r"(?:capture\.json|arr-data\.json|replay-provenance\.json|" + r"data-request-[0-9]{6}\.(?:json|meta\.json|response\.bin))") +REQUEST_PATTERN = re.compile(r"data-request-([0-9]{6})\.json") +RESPONSE_PATTERN = re.compile(r"data-request-[0-9]{6}\.response\.bin") +require = base.require + + +def _hash(raw): + return hashlib.sha256(raw).hexdigest() + + +def saved_status_evidence(capture_root, expected_manifest_sha256, original_payload: bytes) -> dict: + """Verify saved HTTP evidence and bind one unchanged status to every source row.""" + require(type(expected_manifest_sha256) is str + and bool(re.fullmatch(r"[0-9a-f]{64}", expected_manifest_sha256)), "status_evidence_manifest_pin_invalid") + require(type(original_payload) is bytes and len(original_payload) <= MAX_DATA_BYTES, + "status_evidence_original_invalid") + root = Path(capture_root) + info = root.lstat() + require(stat.S_ISDIR(info.st_mode) and info.st_uid == os.getuid() + and stat.S_IMODE(info.st_mode) == 0o700, "status_evidence_directory_unsafe") + manifest_raw = protected_read(root / "result.json", base.MAX_MANIFEST_BYTES) + require(_hash(manifest_raw) == expected_manifest_sha256, "status_evidence_manifest_changed") + manifest = base.strict_json(manifest_raw) + require(manifest.get("version") == SOURCE_VERSION and manifest.get("collection_complete") is True + and manifest.get("status") in {"collected", "collected_with_gaps"} + and manifest.get("error") is None and manifest.get("finance_ready") is False, + "status_evidence_capture_incomplete") + entries = manifest.get("files") + require(type(entries) is list and 2 <= len(entries) <= 300002, "status_evidence_inventory_invalid") + inventory, raw_files, total = {}, {}, 0 + for entry in entries: + require(type(entry) is dict and set(entry) == {"name", "bytes", "sha256"}, + "status_evidence_inventory_invalid") + name, size, digest = entry["name"], entry["bytes"], entry["sha256"] + require(type(name) is str and bool(FILE_PATTERN.fullmatch(name)) and name not in inventory, + "status_evidence_inventory_name_invalid") + require(type(size) is int and 0 <= size <= MAX_DATA_BYTES and type(digest) is str + and bool(re.fullmatch(r"[0-9a-f]{64}", digest)), "status_evidence_inventory_invalid") + total += size + require(total <= base.MAX_ARCHIVE_BYTES, "status_evidence_archive_too_large") + raw = protected_read(root / name, size) + require(len(raw) == size and _hash(raw) == digest, "status_evidence_archive_changed") + inventory[name], raw_files[name] = entry, raw + require({p.name for p in root.iterdir()} == set(inventory) | {"result.json"}, + "status_evidence_inventory_changed") + require(raw_files.get("arr-data.json") == original_payload + and manifest.get("data_sha256") == _hash(original_payload), "status_evidence_original_changed") + original = base.strict_json(original_payload) + require(original.get("version") == SOURCE_VERSION and original.get("collection_complete") is True + and original.get("status") == manifest["status"] + and original.get("source_kind") == manifest.get("source_kind"), "status_evidence_original_invalid") + require("capture.json" in raw_files, "status_evidence_capture_missing") + capture = base.strict_json(raw_files["capture.json"]) + require(capture.get("version") == SOURCE_VERSION and capture.get("service_url") == base.SERVICE + and capture.get("application_id") == base.APPLICATION + and capture.get("source_kind") == original.get("source_kind") + and capture.get("source_kind") in {"ohip_platform", "test_transport"}, "status_evidence_context_mismatch") + require(type(capture.get("options")) is dict + and set(capture["options"]) == {"arrival_date", "hotel_id", "page_size", "max_pages", "max_records"}, + "status_evidence_context_mismatch") + options = base.Options(**capture["options"]) + options.validate() + require(original.get("hotel_id") == options.hotel_id and original.get("report_date") == options.arrival_date, + "status_evidence_context_mismatch") + rows = original.get("records") + require(type(rows) is list and 1 <= len(rows) <= options.max_records + and type(manifest.get("records")) is int and manifest["records"] == len(rows), + "status_evidence_records_invalid") + identities = [] + for sequence, row in enumerate(rows, 1): + require(type(row) is dict and type(row.get("source_sequence")) is int + and row["source_sequence"] == sequence and type(row.get("sources")) is list + and bool(row["sources"]) and type(row.get("fields")) is dict, "status_evidence_records_invalid") + identity = row.get("reservation_id") + require(type(identity) is str and bool(re.fullmatch(r"[A-Za-z0-9_-]{1,128}", identity)) + and identity not in identities, "status_evidence_records_invalid") + identities.append(identity) + refs = row["sources"] + require(all(type(ref) is str and bool(RESPONSE_PATTERN.fullmatch(ref)) and ref in inventory for ref in refs) + and len(set(refs)) == len(refs), "status_evidence_source_reference_invalid") + + requests = sorted(name for name in inventory if REQUEST_PATTERN.fullmatch(name)) + require(type(manifest.get("http_attempts")) is int and len(requests) == manifest["http_attempts"] + and requests == [f"data-request-{i:06d}.json" for i in range(1, len(requests) + 1)], + "status_evidence_request_inventory_invalid") + request_prefixes = {name[:-5] for name in requests} + require(all(name.rsplit(".", 2)[0] in request_prefixes + for name in inventory if name.endswith((".meta.json", ".response.bin"))), + "status_evidence_request_inventory_invalid") + documents, searches, details, operations = {}, [], {}, {} + for request_name in requests: + prefix = request_name[:-5] + meta_name, response_name = prefix + ".meta.json", prefix + ".response.bin" + require(meta_name in raw_files, "status_evidence_request_metadata_missing") + request, meta = base.strict_json(raw_files[request_name]), base.strict_json(raw_files[meta_name]) + operation = request.get("operation_id") + operations[response_name] = operation + status = meta.get("http_status") + if type(status) is not int or not 200 <= status < 300: + continue # Retried failures cannot be evidence, but their bytes are still pinned. + require(response_name in raw_files and meta.get("error") is None and meta.get("oversized", False) is False, + "status_evidence_response_invalid") + if operation not in {base.SEARCH, base.DETAIL}: + continue + envelope = base.strict_json(raw_files[response_name]) + require(envelope.get("operation_id") == operation and envelope.get("hotel_id") == options.hotel_id + and type(envelope.get("oracle_request_id")) is str and bool(envelope["oracle_request_id"].strip()) + and type(envelope.get("data")) is dict, "status_evidence_response_context_mismatch") + if "upstream_status" in envelope: + require(type(envelope["upstream_status"]) is int and 200 <= envelope["upstream_status"] < 300, + "status_evidence_upstream_failure") + base.check_warnings(envelope) + item = {"request": request, "data": envelope["data"], "response": response_name, + "number": int(REQUEST_PATTERN.fullmatch(request_name)[1])} + documents[response_name] = item + if operation == base.SEARCH: + require(request.get("method") == "POST" and request.get("path") == "/api/v1/reservations/searches" + and type(request.get("body")) is dict, "status_evidence_search_request_invalid") + searches.append(item) + else: + require(type(request.get("path")) is str, "status_evidence_detail_request_mismatch") + path = urlsplit(request["path"]) + collection = envelope["data"].get("reservations") + reservations = collection.get("reservation") if type(collection) is dict else None + require(type(reservations) is list and len(reservations) == 1 and type(reservations[0]) is dict, + "status_evidence_detail_ambiguous") + detail = reservations[0] + identity = base.validate_row(detail, options) + require(typed_id(detail.get("reservationIdList"), "Reservation") == identity + and request.get("method") == "GET" and request.get("body") is None + and not path.scheme and not path.netloc and not path.fragment + and path.path == f"/api/v1/reservations/{identity}" + and parse_qs(path.query, keep_blank_values=True) == {"fetchInstructions": list(FETCH)}, + "status_evidence_detail_request_mismatch") + require(identity in identities and identity not in details, "status_evidence_detail_ambiguous") + item["row"] = detail + details[identity] = item + # The complete collector performs two identical paginated searches. Recheck + # them offline rather than trusting a new interpretation of row membership. + rounds = [] + for item in searches: + if item["request"]["body"].get("offset") == 0: + rounds.append([]) + require(bool(rounds), "status_evidence_search_round_invalid") + rounds[-1].append(item) + require(len(rounds) == 2 and set(details) == set(identities), "status_evidence_search_round_invalid") + + def replay_search(items): + class SavedReader: + index = 0 + + def read(self, operation, *, body): + require(self.index < len(items), "status_evidence_search_round_invalid") + item = items[self.index] + self.index += 1 + require(operation == base.SEARCH and item["request"]["body"] == body, + "status_evidence_search_request_invalid") + return item["data"] + + reader = SavedReader() + found = base.search_day(reader, options, server_sort=False) + require(reader.index == len(items), "status_evidence_search_round_invalid") + return found + + initial, final = (replay_search(items) for items in rounds) + require(initial == final and [base.reservation_id(row) for row in initial] == identities, + "status_evidence_search_changed") + result = [] + for source_row, search in zip(rows, initial): + identity = source_row["reservation_id"] + detail_item = details[identity] + detail = detail_item["row"] + require(rounds[0][-1]["number"] < detail_item["number"] < rounds[1][0]["number"], + "status_evidence_request_order_invalid") + status = search.get("reservationStatus") + require(type(status) is str and bool(status.strip()) and status == detail.get("reservationStatus"), + "status_evidence_status_conflict") + modified = search.get("lastModifyDateTime") + require(type(modified) is str and bool(modified.strip()) and modified == detail.get("lastModifyDateTime"), + "status_evidence_state_conflict") + if "reservation_status" in source_row: + require(source_row["reservation_status"] == status, "status_evidence_existing_status_conflict") + require(all(operations.get(ref) not in {base.SEARCH, base.DETAIL} or ref in documents + for ref in source_row["sources"]), "status_evidence_source_binding_failed") + initial_refs = [item["response"] for item in rounds[0] + if any(base.reservation_id(row) == identity for row in item["data"]["reservations"]["reservationInfo"])] + require(len(initial_refs) == 1 and initial_refs[0] in source_row["sources"] + and detail_item["response"] in source_row["sources"], "status_evidence_source_binding_missing") + claimed_details = [ref for ref in source_row["sources"] + if ref in documents and documents[ref]["request"]["operation_id"] == base.DETAIL] + require(claimed_details == [detail_item["response"]], "status_evidence_source_binding_ambiguous") + final_refs = [item["response"] for item in rounds[1] + if any(base.reservation_id(row) == identity for row in item["data"]["reservations"]["reservationInfo"])] + proof_names = [] + for ref in initial_refs + [detail_item["response"]] + final_refs: + prefix = ref.removesuffix(".response.bin") + proof_names.extend((prefix + ".json", prefix + ".meta.json", ref)) + result.append({"source_sequence": source_row["source_sequence"], "reservation_id": identity, + "reservation_status": status, + "sources": [{"file": name, "sha256": inventory[name]["sha256"]} for name in proof_names]}) + return {"version": VERSION, "policy_id": POLICY_ID, "original_sha256": _hash(original_payload), + "source_manifest_sha256": expected_manifest_sha256, "report_date": options.arrival_date, + "hotel_id": options.hotel_id, "records": result} diff --git a/tests/javascript/arr_data_review.cjs b/tests/javascript/arr_data_review.cjs index 10859c3..6a0674e 100644 --- a/tests/javascript/arr_data_review.cjs +++ b/tests/javascript/arr_data_review.cjs @@ -10,6 +10,31 @@ const item=(extra={})=>({item_id:'1:BLOCK_CODE',source_sequence:1,confirmation_n const review=(items,revision=1)=>({request_id:requestId,report_date:task.report_date,status:'editing',revision,items,pending_count:items.filter(x=>!x.confirmed).length,total_count:items.length,can_finalize:items.every(x=>x.confirmed)}); const plain=value=>JSON.parse(JSON.stringify(value)); +test('cancelled source rows need no fields and the exclusion stays visible before continuation',async()=>{ + const h=harness(()=>response(200,{...review([]),excluded_cancelled_count:6})); + await h.acceptARRDownloadTask(task,{sync:false}); + assert.equal(h.element('#arr-data-review-body').innerHTML,''); + assert.match(h.element('#arr-data-review-progress').textContent,/data_review\.excluded_cancelled/); + assert.equal(h.element('#arr-data-review-finalize').disabled,false); + assert.equal(h.calls.every(call=>call.method==='GET'),true); +}); + +test('removed cancelled items clear obsolete drafts but retain edits still in the review',async()=>{ + let current=review([item(),item({item_id:'2:DISP_ROOM_NO',source_sequence:2,field:'DISP_ROOM_NO',can_be_empty:false})]); + const h=harness(()=>response(200,current)); + await h.acceptARRDownloadTask(task,{sync:false}); + h.state.arrDataReviewDrafts['1:BLOCK_CODE']='CANCELLED-DRAFT'; + h.state.arrDataReviewDrafts['2:DISP_ROOM_NO']='ROOM-DRAFT'; + current=review([current.items[1]],2); + await h.loadARRDataReview(requestId); + assert.equal(h.state.arrDataReviewDrafts['1:BLOCK_CODE'],undefined); + assert.equal(h.state.arrDataReviewDrafts['2:DISP_ROOM_NO'],'ROOM-DRAFT'); + current={...review([],3),excluded_cancelled_count:2}; + await h.loadARRDataReview(requestId); + assert.equal(Object.keys(h.state.arrDataReviewDrafts).length,0); + assert.equal(h.element('#arr-data-review-finalize').disabled,false); +}); + for (const fixture of [ {name:'missing room retains the original required rule', field:'DISP_ROOM_NO', can_be_empty:false, source_state:'missing', reason_code:'calendar_missing_rooms', expected:'room_missing_in_scope'}, diff --git a/tests/local_postgres.py b/tests/local_postgres.py index d005cf3..f066806 100644 --- a/tests/local_postgres.py +++ b/tests/local_postgres.py @@ -76,7 +76,9 @@ class TemporaryPostgres: connection.close() raise - def reset_database(self): + def reset_database(self, *, schema_version=20): + if schema_version not in (19, 20): + raise ValueError("unsupported fixture schema version") # Only this owned cluster has passed data_directory/socket checks above. with self.connect(database="postgres", autocommit=True) as connection: connection.execute("DROP DATABASE booking_test WITH (FORCE)") @@ -86,8 +88,8 @@ class TemporaryPostgres: if existing: raise RuntimeError("schema bootstrap requires an empty fixture database") paths = sorted(p for p in (PROJECT / "database").glob("[0-9][0-9][0-9]_*.sql") - if ".down." not in p.name and 8 <= int(p.name[:3]) <= 19) - if len(paths) != 12: + if ".down." not in p.name and 8 <= int(p.name[:3]) <= schema_version) + if len(paths) != schema_version - 7: raise RuntimeError("unexpected fixture migration set") for path in paths: connection.execute(path.read_text(), prepare=False) diff --git a/tests/test_arr_cancelled_ingestion.py b/tests/test_arr_cancelled_ingestion.py new file mode 100644 index 0000000..a2356ff --- /dev/null +++ b/tests/test_arr_cancelled_ingestion.py @@ -0,0 +1,169 @@ +"""Cancellation outcome contracts and additive Finance migration, synthetic only.""" +from __future__ import annotations + +import copy +from dataclasses import replace +import json +import os +from pathlib import Path +import tempfile +import unittest + +from arr_ingestion.contracts import DeliveryEnvelope, IngestionError +from arr_ingestion.postgres import DatabaseConfig, PostgresIngestionRepository, _outcome_counts +from arr_ingestion.validation import DeliveryValidator, _validate_structured_payload_v4 +from tests.local_postgres import TemporaryPostgres +from tests.test_arr_ingestion_validation import build_delivery, policy +from tests.test_arr_opera_daily_ingest import reservation, xml_document + + +PROJECT = Path(__file__).resolve().parents[1] + + +def cancelled_xml(*, rate_amount="900"): + cancelled = reservation(1).replace( + "SYNTHETIC-ROOM-1", "" + ).replace("", "CANCELLED") + return xml_document(cancelled, reservation(2, rate_amount=rate_amount)) + + +class CancelledIngestionContractTests(unittest.TestCase): + def generated(self, root): + raw, store, _ = build_delivery(cancelled_xml(), root) + envelope = DeliveryEnvelope.from_dict(json.loads(raw)) + payload = json.loads(store.objects[envelope.artifacts["structured_result_json"].object_key]) + return raw, store, envelope, payload + + def test_new_outcome_is_validated_and_replayed_without_room(self): + with tempfile.TemporaryDirectory() as temporary: + raw, store, _, payload = self.generated(Path(temporary)) + verified = DeliveryValidator(store, policy()).validate(raw) + self.assertEqual(verified.envelope.status, "success") + self.assertEqual(payload["outcome_counts"]["excluded_cancelled"], 1) + self.assertEqual(payload["output_rows"], 1) + self.assertEqual(payload["records"][0]["outcome"], "excluded_cancelled") + self.assertEqual(payload["records"][0]["disp_room_no"], "") + self.assertNotIn("removed_as_cancelled", payload) + + def test_new_identity_requires_zero_count_and_old_identity_rejects_new_key(self): + with tempfile.TemporaryDirectory() as temporary: + raw, store, _ = build_delivery(xml_document(reservation(1)), Path(temporary)) + envelope = DeliveryEnvelope.from_dict(json.loads(raw)) + payload = json.loads(store.objects[envelope.artifacts["structured_result_json"].object_key]) + self.assertEqual(payload["outcome_counts"]["excluded_cancelled"], 0) + _validate_structured_payload_v4(payload, envelope) + missing = copy.deepcopy(payload) + del missing["outcome_counts"]["excluded_cancelled"] + with self.assertRaisesRegex(IngestionError, "outcome counts contract"): + _validate_structured_payload_v4(missing, envelope) + old_envelope = replace(envelope, processor_version="4.2.0") + old_payload = copy.deepcopy(payload) + old_payload["processor_version"] = "4.2.0" + with self.assertRaisesRegex(IngestionError, "outcome counts contract"): + _validate_structured_payload_v4(old_payload, old_envelope) + del old_payload["outcome_counts"]["excluded_cancelled"] + _validate_structured_payload_v4(old_payload, old_envelope) + + def test_old_identity_cannot_disguise_cancelled_record_or_rule_hash(self): + with tempfile.TemporaryDirectory() as temporary: + raw, store, envelope, payload = self.generated(Path(temporary)) + old = copy.deepcopy(payload) + old["processor_version"] = "4.2.0" + del old["outcome_counts"]["excluded_cancelled"] + with self.assertRaisesRegex(IngestionError, "structured outcome is invalid"): + _validate_structured_payload_v4(old, replace(envelope, processor_version="4.2.0")) + bad_hash = json.loads(raw) + bad_hash["rule_set_sha256"] = "0" * 64 + with self.assertRaises(IngestionError) as caught: + DeliveryValidator(store, policy()).validate(json.dumps(bad_hash).encode()) + self.assertEqual(caught.exception.code, "PROCESSOR_NOT_ALLOWED") + + def test_finance_counts_preserve_old_payload_and_add_new_count(self): + counts = dict(retained=2, excluded_rate_code=1, duplicate=3, + validation_failed=4, price_unmatched=5) + self.assertEqual(_outcome_counts({"outcome_counts": counts}), (2, 1, 3, 4, 5, 0)) + counts["excluded_cancelled"] = 6 + self.assertEqual(_outcome_counts({"outcome_counts": counts}), (2, 1, 3, 4, 5, 6)) + + def test_cancelled_count_balances_pending_price_review(self): + with tempfile.TemporaryDirectory() as temporary: + raw, store, _ = build_delivery(cancelled_xml(rate_amount="1800"), Path(temporary)) + verified = DeliveryValidator(store, policy()).validate(raw) + self.assertEqual(verified.envelope.status, "review_required") + self.assertEqual(verified.structured_payload["outcome_counts"]["excluded_cancelled"], 1) + self.assertEqual(verified.structured_payload["review_required_rows"], 1) + + +@unittest.skipUnless(os.environ.get("ARR_TEST_LOCAL_POSTGRES") == "1", "owned PostgreSQL opt-in required") +class CancelledFinanceMigrationTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.database = TemporaryPostgres().__enter__() + cls.addClassCleanup(cls.database.__exit__, None, None, None) + + def test_upgrade_preserves_history_permissions_guards_and_count_constraints(self): + import psycopg + self.database.reset_database(schema_version=19) + repository = PostgresIngestionRepository(DatabaseConfig("owned-fixture"), connect=self.database.connect) + with self.assertRaises(IngestionError) as missing: + repository.assert_cancelled_scope_schema() + self.assertEqual(missing.exception.code, "DATABASE_MIGRATION_MISSING") + with self.database.connect(autocommit=True) as connection: + artifact = connection.execute("""INSERT INTO ingestion.artifacts + (artifact_kind, storage_provider, bucket_alias, object_key, original_filename, sha256, byte_size) + VALUES ('opera_xml','local_fixture','fixture','old/source.xml','source.xml',%s,0) RETURNING id""", + ("a" * 64,)).fetchone()[0] + run = connection.execute("""INSERT INTO ingestion.processing_runs + (run_key,pipeline_type,source_artifact_id,run_status,result_delivery_mode,business_date, + delivered_processor_version,delivered_rule_set_sha256,result_schema_version, + delivery_sha256,validated_at,finished_at) + VALUES ('old-success','opera_daily',%s,'accepted','direct_mcp','2026-07-27', + '3.0.0',%s,'3.0',%s,now(),now()) RETURNING id""", + (artifact, "b" * 64, "c" * 64)).fetchone()[0] + version = connection.execute("""INSERT INTO finance.daily_versions + (business_date,version_no,processing_run_id,source_artifact_id,version_status, + processor_version,rule_set_sha256,result_schema_version,result_sha256,source_rows, + retained_rows,excluded_rate_code_rows,duplicate_rows,validation_failed_rows, + price_unmatched_rows,validated_at,result_delivery_mode) + VALUES ('2026-07-27',1,%s,%s,'validated','3.0.0',%s,'3.0',%s, + 1,0,1,0,0,0,now(),'direct_mcp') RETURNING id""", + (run, artifact, "b" * 64, "c" * 64)).fetchone()[0] + before = connection.execute("SELECT to_jsonb(v) FROM finance.daily_versions v WHERE id=%s", (version,)).fetchone()[0] + guards = connection.execute("SELECT tgname,pg_get_triggerdef(oid) FROM pg_trigger WHERE NOT tgisinternal ORDER BY tgname").fetchall() + connection.execute("CREATE ROLE arr_cancelled_fixture") + connection.execute("GRANT USAGE ON SCHEMA finance TO arr_cancelled_fixture") + connection.execute("GRANT SELECT, INSERT ON finance.daily_versions TO arr_cancelled_fixture") + connection.execute((PROJECT / "database/020_daily_cancelled_exclusion.sql").read_text(), prepare=False) + repository.assert_cancelled_scope_schema() + after = connection.execute("SELECT to_jsonb(v) FROM finance.daily_versions v WHERE id=%s", (version,)).fetchone()[0] + self.assertEqual(after.pop("excluded_cancelled_rows"), 0) + self.assertEqual(after, before) + self.assertEqual(connection.execute("SELECT tgname,pg_get_triggerdef(oid) FROM pg_trigger WHERE NOT tgisinternal ORDER BY tgname").fetchall(), guards) + self.assertEqual(connection.execute("SELECT has_column_privilege('arr_cancelled_fixture','finance.daily_versions','excluded_cancelled_rows','INSERT'),has_table_privilege('arr_cancelled_fixture','finance.daily_versions','UPDATE')").fetchone(), (True, False)) + with self.assertRaises(psycopg.errors.CheckViolation): + connection.execute("UPDATE finance.daily_versions SET excluded_cancelled_rows=1 WHERE id=%s", (version,)) + connection.execute("UPDATE finance.daily_versions SET excluded_rate_code_rows=0, excluded_cancelled_rows=1 WHERE id=%s", (version,)) + with self.assertRaisesRegex(psycopg.errors.RaiseException, "cancellation-exclusion facts exist"): + connection.execute((PROJECT / "database/020_daily_cancelled_exclusion.down.sql").read_text(), prepare=False) + connection.execute("ROLLBACK") + connection.execute("SET ROLE arr_cancelled_fixture") + with self.assertRaises(psycopg.errors.InsufficientPrivilege): + connection.execute("UPDATE finance.daily_versions SET excluded_cancelled_rows=0 WHERE id=%s", (version,)) + connection.execute("RESET ROLE") + + def test_empty_database_allows_guarded_rollback_and_reapply(self): + self.database.reset_database() + repository = PostgresIngestionRepository(DatabaseConfig("owned-fixture"), connect=self.database.connect) + repository.assert_data_source_schema() + with self.database.connect(autocommit=True) as connection: + connection.execute((PROJECT / "database/020_daily_cancelled_exclusion.down.sql").read_text(), prepare=False) + with self.assertRaises(IngestionError) as missing: + repository.assert_data_source_schema() + self.assertEqual(missing.exception.code, "DATABASE_MIGRATION_MISSING") + with self.database.connect(autocommit=True) as connection: + connection.execute((PROJECT / "database/020_daily_cancelled_exclusion.sql").read_text(), prepare=False) + repository.assert_data_source_schema() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_arr_cancelled_review.py b/tests/test_arr_cancelled_review.py new file mode 100644 index 0000000..827e940 --- /dev/null +++ b/tests/test_arr_cancelled_review.py @@ -0,0 +1,119 @@ +"""Cancellation eligibility before field review; synthetic, offline sources only.""" +import copy +import hashlib +import json +from pathlib import Path +import tempfile +import unittest +from unittest.mock import patch + +from arr_processing.policy import load_processor_policy +from arr_web.arr_data_review import DataFieldReviews +from arr_web.contracts import PortalError +from integrations.ohip.collect_arr_source import CollectionError +from tests.test_arr_data_review import source_document, raw, gap, CONTEXT, DAY, REQUEST, ACTOR, SOURCE_MANIFEST + + +class CancelledReviewTests(unittest.TestCase): + def setUp(self): + temporary = tempfile.TemporaryDirectory(prefix="arr-cancelled-review-") + self.addCleanup(temporary.cleanup) + self.root = Path(temporary.name) / "reviews" + self.policy = load_processor_policy(Path(__file__).resolve().parents[1]) + self.service = DataFieldReviews(root=self.root, policy=self.policy, context=CONTEXT) + self.document = source_document(2) + + def prepare(self): + return self.service.prepare(REQUEST, raw(self.document), SOURCE_MANIFEST, DAY) + + def evidence(self, statuses=("Cancelled", "InHouse")): + return {"version": "arr-reservation-status-evidence/v1", "policy_id": "arr-exclude-cancelled/v1", + "original_sha256": hashlib.sha256(raw(self.document)).hexdigest(), + "source_manifest_sha256": SOURCE_MANIFEST, "hotel_id": CONTEXT["hotel_id"], "report_date": DAY, + "records": [{"source_sequence": row["source_sequence"], "reservation_id": row["reservation_id"], + "reservation_status": status, "sources": [{"file": "synthetic.response.bin", "sha256": "f" * 64}]} + for row, status in zip(self.document["records"], statuses)]} + + def apply(self, revision, evidence=None): + with patch("integrations.ohip.reservation_status.saved_status_evidence", return_value=evidence or self.evidence()): + return self.service.apply_saved_status_evidence(REQUEST, Path("unused-synthetic-capture"), + expected_revision=revision) + + def test_cancelled_fields_and_rate_are_not_manual_requirements_but_other_rows_still_are(self): + self.document["records"][0]["reservation_status"] = "Cancelled" + for field in ("DISP_ROOM_NO", "RATE_CODE", "BLOCK_CODE", "ADULTS"): + gap(self.document, field) + gap(self.document, "DISP_ROOM_NO", sequence=2) + review = self.prepare() + self.assertEqual([item["item_id"] for item in review["items"]], ["2:DISP_ROOM_NO"]) + self.assertEqual(review["excluded_cancelled_count"], 1) + self.assertFalse(review["can_finalize"]) + + def test_saved_statuses_remove_room_task_preserve_original_and_freeze_audited_source(self): + gap(self.document, "DISP_ROOM_NO") + before = self.prepare() + original = self.service.original(REQUEST) + review = self.apply(before["revision"]) + self.assertEqual(review["pending_count"], 0) + self.assertEqual(review["items"], []) + self.assertTrue(review["can_finalize"]) + self.assertEqual(self.service.original(REQUEST), original) + self.assertIsNone(self.service.payload(REQUEST)) + self.assertEqual(self.prepare(), review, "a saved-status overlay must not bypass explicit continuation") + self.assertEqual(self.apply(before["revision"]), review, "exact retry is idempotent") + final = self.service.finalize(REQUEST, review["revision"], ACTOR) + restored = DataFieldReviews(root=self.root, policy=self.policy, context=CONTEXT) + payload, binding = restored.payload(REQUEST) + derived = json.loads(payload) + self.assertEqual(derived["records"][0]["fields"], self.document["records"][0]["fields"]) + self.assertEqual(derived["records"][0]["reservation_status"], "Cancelled") + audit = derived["manual_data_review"]["manifest"] + self.assertEqual(audit["changes"], []) + self.assertEqual(audit["reservation_status_evidence"]["original_sha256"], hashlib.sha256(original[0]).hexdigest()) + self.assertEqual([event["action"] for event in audit["events"]], ["reservation_status_evidence", "finalize"]) + self.assertEqual(restored.get(REQUEST), final) + self.assertEqual(restored.original(REQUEST), original) + self.assertEqual(len(binding), 64) + with self.assertRaises(PortalError): + self.apply(final["revision"]) + + def test_prior_staff_decision_on_cancelled_row_stays_audited_but_cannot_be_edited(self): + gap(self.document, "BLOCK_CODE") + before = self.prepare() + saved = self.service.update(REQUEST, "1:BLOCK_CODE", before["revision"], "VERIFIED", ACTOR) + review = self.apply(saved["revision"]) + self.assertEqual(review["items"], []) + with self.assertRaises(PortalError): + self.service.update(REQUEST, "1:BLOCK_CODE", review["revision"], "CHANGED", ACTOR) + self.service.finalize(REQUEST, review["revision"], ACTOR) + derived = json.loads(self.service.payload(REQUEST)[0]) + self.assertEqual(derived["manual_data_review"]["manifest"]["changes"][0]["value"], "VERIFIED") + self.assertEqual(derived["records"][0]["fields"]["BLOCK_CODE"]["value"], "VERIFIED") + + def test_overlay_rejects_wrong_order_context_and_revision_without_publishing(self): + gap(self.document, "DISP_ROOM_NO") + review = self.prepare() + with self.assertRaises(PortalError): + self.apply(review["revision"] + 1) + for alter in (lambda e: e["records"].reverse(), + lambda e: e.update(original_sha256="0" * 64), + lambda e: e["records"].pop(), + lambda e: e["records"][0].update(reservation_status="")): + evidence = self.evidence() + alter(evidence) + with self.assertRaises(CollectionError): + self.apply(review["revision"], evidence) + self.assertEqual(self.service.get(REQUEST), review) + + def test_status_evidence_tamper_is_rejected(self): + gap(self.document, "DISP_ROOM_NO") + review = self.prepare() + self.apply(review["revision"]) + evidence_file = next((self.root / REQUEST).glob("reservation-status-*.json")) + evidence_file.write_bytes(evidence_file.read_bytes() + b" ") + with self.assertRaises(CollectionError): + self.service.get(REQUEST) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_arr_cancelled_scope.py b/tests/test_arr_cancelled_scope.py new file mode 100644 index 0000000..e2e9cc3 --- /dev/null +++ b/tests/test_arr_cancelled_scope.py @@ -0,0 +1,213 @@ +"""Offline cancellation scope checks for both daily report input paths.""" +from __future__ import annotations + +import argparse +import contextlib +import copy +import io +import json +from pathlib import Path +import tempfile +import unittest +import xml.etree.ElementTree as ET + +from tests.test_arr_opera_daily_ingest import core, reservation, run_processor, xml_document +from tests.test_arr_data_review import source_document + + +def xml_row(sequence, statuses=(), *, blank_room=False, blank_rate=False, invalid_fields=False): + node = ET.fromstring(reservation(sequence)) + for tag, value in statuses: + ET.SubElement(node, tag).text = value + if blank_room: + node.find("DISP_ROOM_NO").text = "" + if blank_rate: + node.find("RATE_CODE").text = "" + if invalid_fields: + for tag, value in (("COMPANY_NAME", ""), ("FULL_NAME", ""), ("ADULTS", "-1"), + ("NO_OF_ROOMS", "0"), ("TRUNC_END", "2026-07-26")): + node.find(tag).text = value + return ET.tostring(node, encoding="unicode") + + +class CancelledScopeTests(unittest.TestCase): + def setUp(self): + temporary = tempfile.TemporaryDirectory(prefix="arr-cancelled-scope-") + self.addCleanup(temporary.cleanup) + self.root = Path(temporary.name) + + def classify_xml(self, *rows, apply_scope=True): + path = self.root / "source.xml" + path.write_text(xml_document(*rows)) + business_date, nodes = core.read_xml(path) + return core.classify_input_records(nodes, business_date, apply_scope=apply_scope) + + def read_data(self, document): + path = self.root / "source.json" + path.write_text(json.dumps(document)) + return core.read_data_source(path) + + def run_data(self, document, output_name="output"): + path = self.root / "source.json" + path.write_text(json.dumps(document)) + output = self.root / output_name + args = argparse.Namespace(xml=None, data_json=str(path), output_dir=str(output), + result_json=str(output / "result.json"), structured_result_json=str(output / "structured-result.json")) + before = path.read_bytes() + with contextlib.redirect_stdout(io.StringIO()): + result = core.process(args) + self.assertEqual(path.read_bytes(), before) + return result, json.loads((output / "structured-result.json").read_text()) + + def test_xml_cancellation_precedes_rate_room_other_validation_and_keeps_lineage(self): + all_rows, eligible, rate_removed, duplicates, errors = self.classify_xml( + xml_row(1, (("RESV_STATUS", " CxL "),), blank_room=True, blank_rate=True, invalid_fields=True), + reservation(2)) + self.assertEqual(errors, []) + self.assertEqual((rate_removed, duplicates), (0, 0)) + self.assertEqual([row["_SOURCE_INDEX"] for row in eligible], [2]) + self.assertEqual(all_rows[0]["_OUTCOME"], "excluded_cancelled") + self.assertEqual(all_rows[0]["_DECISION_CODES"], ["RESERVATION_CANCELLED_EXCLUDED"]) + self.assertEqual(all_rows[0]["CONFIRMATION_NO"], "SYNTHETIC-CONF-1") + self.assertEqual(all_rows[0]["_SOURCE_LOCATION"], "reservation[1]") + + def test_cancelled_with_a_room_never_occupies_duplicate_key(self): + rows = [ET.fromstring(reservation(index, room="SAME-ROOM")) for index in (1, 2, 3)] + ET.SubElement(rows[0], "SHORT_RESV_STATUS").text = "CANCELLED" + all_rows, eligible, _, duplicates, errors = self.classify_xml( + *(ET.tostring(node, encoding="unicode") for node in rows)) + self.assertEqual(errors, []) + self.assertEqual([row["_SOURCE_INDEX"] for row in eligible], [2]) + self.assertEqual([row["_OUTCOME"] for row in all_rows], ["excluded_cancelled", "pending", "duplicate"]) + self.assertEqual(all_rows[2]["_DUPLICATE_OF_SOURCE_SEQUENCE"], 2) + self.assertEqual(duplicates, 1) + + def test_only_exact_cancellation_values_in_explicit_status_fields_are_excluded(self): + for field in ("RESV_STATUS", "RESERVATION_STATUS", "SHORT_RESV_STATUS"): + for status in ("CXL", " cancelled ", "CaNcElEd"): + with self.subTest(field=field, status=status): + rows, _, _, _, errors = self.classify_xml( + xml_row(1, ((field, status),), blank_room=True), reservation(2)) + self.assertEqual(errors, []) + self.assertTrue(core.is_cancelled_record(rows[0])) + for status in ("CA", "CD", "GC", "TA", "NoShow", "CKOT", "CancelledByGuest", ""): + with self.subTest(status=status): + rows, _, _, _, errors = self.classify_xml( + xml_row(1, (("SHORT_RESV_STATUS", status),), blank_room=True), reservation(2)) + self.assertFalse(core.is_cancelled_record(rows[0])) + self.assertIn("XML_ROOM_MISSING", {error.code for error in errors}) + for field in ("RESERVATION_TYPE", "RESV_TYPE", "BOOKING_TYPE"): + with self.subTest(type_field=field): + rows, _, _, _, errors = self.classify_xml( + xml_row(1, ((field, "CANCELLED"),), blank_room=True), reservation(2)) + self.assertFalse(core.is_cancelled_record(rows[0])) + self.assertIn("XML_ROOM_MISSING", {error.code for error in errors}) + + def test_conflicting_xml_status_is_retained_and_cancel_aliases_agree(self): + for fields in ((("RESV_STATUS", "CXL"), ("SHORT_RESV_STATUS", "CKIN")), + (("RESERVATION_STATUS", "CANCELLED"), ("SHORT_RESV_STATUS", "CA")), + (("RESV_STATUS", "CXL"), ("RESV_STATUS", "RESERVED"))): + with self.subTest(fields=fields): + rows, _, _, _, errors = self.classify_xml(xml_row(1, fields, blank_room=True), reservation(2)) + self.assertTrue(rows[0]["_RESERVATION_STATUS_CONFLICT"]) + self.assertFalse(core.is_cancelled_record(rows[0])) + self.assertIn("XML_ROOM_MISSING", {error.code for error in errors}) + rows, _, _, _, errors = self.classify_xml(xml_row(1, + (("RESV_STATUS", "CXL"), ("SHORT_RESV_STATUS", "CANCELED")), blank_room=True), reservation(2)) + self.assertEqual(errors, []) + self.assertFalse(rows[0]["_RESERVATION_STATUS_CONFLICT"]) + self.assertTrue(core.is_cancelled_record(rows[0])) + + def test_data_cancellation_is_shared_and_unknown_or_absent_status_keeps_validation(self): + for status in ("Cancelled", " CANCELED ", "cxl"): + with self.subTest(status=status): + document = source_document(2) + document["records"][0]["reservation_status"] = status + for field in document["records"][0]["fields"]: + document["records"][0]["fields"][field] = {"state": "missing", "value": None} + business_date, rows = self.read_data(document) + all_rows, _, rate_removed, _, errors = core.classify_input_records(rows, business_date) + self.assertEqual(errors, []) + self.assertEqual(rate_removed, 0) + self.assertEqual(all_rows[0]["_OUTCOME"], "excluded_cancelled") + for status in (None, "NoShow", "InHouse", "CA"): + with self.subTest(status=status): + document = source_document(2) + if status is not None: + document["records"][0]["reservation_status"] = status + document["records"][0]["fields"]["DISP_ROOM_NO"] = {"state": "missing", "value": None} + business_date, rows = self.read_data(document) + _, _, _, _, errors = core.classify_input_records(rows, business_date) + self.assertIn("DATA_ROOM_MISSING", {error.code for error in errors}) + + def test_data_status_rejects_invalid_types_and_empty_strings(self): + for value in (None, True, 1, [], {}, "", " "): + with self.subTest(value=value): + document = source_document() + document["records"][0]["reservation_status"] = value + with self.assertRaises(core.ProcessingFailure) as raised: + self.read_data(document) + self.assertEqual(raised.exception.errors[0].code, "INPUT_DATA_INVALID") + + def test_xml_success_and_independent_validation_audit_cancellation_separately(self): + result, source, _, _, payload = run_processor(xml_document( + xml_row(1, (("SHORT_RESV_STATUS", "CXL"),), blank_room=True, blank_rate=True), + reservation(2)), self.root) + self.assertEqual(result, 0, payload["errors"]) + self.assertEqual((payload["source_rows"], payload["removed_by_rate_code"], payload["output_rows"]), (2, 0, 1)) + self.assertEqual(payload["outcome_counts"]["excluded_cancelled"], 1) + self.assertEqual(set(payload["outcome_counts"]), core.FINAL_OUTCOMES) + self.assertEqual(payload["processor_version"], "4.3.0") + self.assertEqual([row["outcome"] for row in payload["records"]], ["excluded_cancelled", "retained"]) + self.assertEqual(payload["records"][0]["decision_codes"], ["RESERVATION_CANCELLED_EXCLUDED"]) + self.assertEqual(payload["records"][0]["confirmation_no"], "SYNTHETIC-CONF-1") + self.assertEqual(payload["artifacts"]["source_xml"]["sha256"], core.sha256_file(source)) + for field, value in (("decision_codes", ["RATE_CODE_NOT_WHITELISTED"]), + ("real_price", 900), ("channel_key", "QBD")): + with self.subTest(tampered=field): + changed = copy.deepcopy(payload) + changed["records"][0][field] = value + with self.assertRaises(core.ProcessingFailure): + core.validate_structured_completeness(changed) + for name in ("structured-result.schema.json", "data-structured-result.schema.json"): + schema = json.loads((core.SKILL_ROOT / "references" / name).read_text()) + self.assertIn("excluded_cancelled", schema["properties"]["outcome_counts"]["required"]) + self.assertIn("excluded_cancelled", schema["$defs"]["record"]["properties"]["outcome"]["enum"]) + + def test_data_success_review_and_failure_keep_cancelled_rows_outside_business_rules(self): + baseline = source_document(2) + baseline["records"][0]["reservation_status"] = "Cancelled" + for field in baseline["records"][0]["fields"]: + baseline["records"][0]["fields"][field] = {"state": "missing", "value": None} + result, payload = self.run_data(baseline) + self.assertEqual(result, 0, payload["errors"]) + self.assertEqual(payload["outcome_counts"]["excluded_cancelled"], 1) + self.assertEqual(payload["removed_by_rate_code"], 0) + self.assertEqual(payload["output_rows"], 1) + review = copy.deepcopy(baseline) + review["records"][1]["fields"]["EFFECTIVE_RATE_AMOUNT"]["value"] = "8765" + result, payload = self.run_data(review, "review") + self.assertEqual(result, 0, payload["errors"]) + self.assertEqual(payload["status"], "review_required", payload["errors"]) + self.assertEqual(payload["outcome_counts"]["excluded_cancelled"], 1) + self.assertEqual(payload["outcome_counts"]["price_unmatched"], 1) + failed = copy.deepcopy(baseline) + failed["records"][1]["fields"]["DISP_ROOM_NO"] = {"state": "missing", "value": None} + result, payload = self.run_data(failed, "failure") + self.assertNotEqual(result, 0) + self.assertEqual(payload["status"], "failed") + self.assertEqual(payload["outcome_counts"]["excluded_cancelled"], 1) + self.assertEqual(payload["outcome_counts"]["validation_failed"], 1) + + def test_legacy_v3_replay_retains_original_scope_and_original_outcome_contract(self): + rows = (xml_row(1, (("SHORT_RESV_STATUS", "CXL"),)), reservation(2)) + result, _, _, _, payload = run_processor(xml_document(*rows), self.root, legacy_v3_output=True) + self.assertEqual(result, 0, payload["errors"]) + self.assertEqual(payload["result_schema_version"], "3.0") + self.assertEqual(payload["output_rows"], 2) + self.assertEqual(set(payload["outcome_counts"]), core.LEGACY_DIRECT_FINAL_OUTCOMES) + self.assertEqual([row["outcome"] for row in payload["records"]], ["retained", "retained"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_arr_direct_data.py b/tests/test_arr_direct_data.py index f5814d2..97df005 100644 --- a/tests/test_arr_direct_data.py +++ b/tests/test_arr_direct_data.py @@ -291,6 +291,33 @@ class DirectDataPostgresTests(unittest.TestCase): self.assertEqual(self.monthly_worker().process_next().status, "published") self.assertEqual(self.coordinator.get_price_review(outcome.job_id, 50, 0)["case_status"], "completed") + def test_cancelled_direct_source_keeps_audit_but_no_room_review_or_monthly_row(self): + row = self.transport.rows[0] + row["reservationStatus"] = "Cancelled" + row["roomStay"].pop("roomId", None) + row["roomStay"].pop("currentRoomInfo", None) + for rate in row["roomStay"]["roomRates"]: + rate.pop("roomId", None) + self.transport.calendar_rooms = [] + self.assertEqual(self.execute().status, "succeeded") + self.assertEqual(self.sql("SELECT result_schema_version,source_rows,retained_rows,excluded_cancelled_rows FROM finance.daily_versions"), + [("5.0", 6, 5, 1)]) + self.assertEqual(self.sql("SELECT outcome FROM finance.daily_records WHERE source_sequence=1"), [("excluded_cancelled",)]) + self.assertEqual(self.count("finance.v_active_daily_facts"), 5) + self.assertEqual(self.monthly_worker().process_next().status, "published") + key, size = self.sql("SELECT a.object_key,a.byte_size FROM reporting.monthly_runs r JOIN ingestion.artifacts a ON a.id=r.workbook_artifact_id")[0] + output = self.files / "monthly.xlsx" + self.store.materialize(key, output, size) + workbook = load_workbook(output, data_only=False) + try: + self.assertEqual(sum(c.data_type == "f" for sheet in workbook for row in sheet for c in row), 5) + finally: + workbook.close() + calls = len(self.transport.calls) + self.assertEqual(self.execute().status, "succeeded") + self.assertEqual(len(self.transport.calls), calls) + self.assertEqual(self.count("finance.daily_versions"), 1) + def test_commit_ack_loss_retry_has_one_result_and_no_new_fetch(self): state = {"armed": True} self.repository._connect = lambda dsn: pg_helpers.LoseCommitConnection(self.database.connect(dsn), state) diff --git a/tests/test_arr_download_postgres_integration.py b/tests/test_arr_download_postgres_integration.py index e3017f4..cdb4fe6 100644 --- a/tests/test_arr_download_postgres_integration.py +++ b/tests/test_arr_download_postgres_integration.py @@ -193,6 +193,41 @@ class PostgresDownloadIntegrationTests(unittest.TestCase): self.assertEqual(json.loads(response.body)["data"]["job_id"], task["job_id"]) self.assertEqual((self.reader_calls, self.adapter.calls, self.validator.calls, self.processor.calls), (1, 1, 1, 1)) + def source_with_cancelled_first(self, **kwargs): + cancelled = FixtureExecutor.xml(room="SYNTHETIC-CANCELLED").replace( + b"SYNTHETIC-CANCELLED", b"" + ).replace(b"", b"CANCELLED") + start = cancelled.index(b"") + end = cancelled.index(b"") + len(b"") + return FixtureExecutor.xml(**kwargs).replace( + b"", b"" + cancelled[start:end] + ) + + def test_cancelled_without_room_is_audited_but_daily_monthly_use_retained_only(self): + self.adapter.payload = self.source_with_cancelled_first() + self.execute() + self.assert_one_commit() + self.assertEqual(self.sql("SELECT source_rows,retained_rows,excluded_cancelled_rows FROM finance.daily_versions"), [(2, 1, 1)]) + self.assertEqual(self.sql("SELECT outcome,disp_room_no FROM finance.daily_records ORDER BY source_sequence"), + [("excluded_cancelled", ""), ("retained", "SYNTHETIC-ROOM-1")]) + self.assertEqual(self.count("finance.v_active_daily_facts"), 1) + self.assertEqual(self.monthly_worker().process_next().status, "published") + self.assertEqual(self.count("reporting.monthly_runs"), 1) + self.execute() + self.assert_one_commit() + self.assertEqual(self.sql("SELECT excluded_cancelled_rows FROM finance.daily_versions"), [(1,)]) + + def test_failed_batch_retains_cancelled_audit_count_without_monthly_commit(self): + self.adapter.payload = self.source_with_cancelled_first(departure="2026-09-14") + self.execute() + self.assertEqual(self.sql("SELECT version_status,source_rows,excluded_cancelled_rows,validation_failed_rows FROM finance.daily_versions"), + [("rejected", 2, 1, 1)]) + self.assertEqual(self.sql("SELECT outcome FROM finance.daily_records ORDER BY source_sequence"), + [("excluded_cancelled",), ("validation_failed",)]) + self.assertEqual(self.daily_event_count(), 0) + self.assertEqual(self.count("finance.current_daily_versions"), 0) + self.assertEqual(self.monthly_worker().process_next().status, "idle") + def test_rejected_commit_lost_ack_recovers_same_failure_without_new_capture(self): self.adapter.payload = FixtureExecutor.xml(rate_code="") state = {"armed": True} diff --git a/tests/test_arr_download_runtime.py b/tests/test_arr_download_runtime.py index 9055405..04c5913 100644 --- a/tests/test_arr_download_runtime.py +++ b/tests/test_arr_download_runtime.py @@ -40,6 +40,7 @@ class RuntimeTests(unittest.TestCase): self.root = Path(temporary.name) self.store = ManagedObjectStore(FilesystemObjectBackend(self.root / 'objects', create=True)) self.repository = InMemoryIngestionRepository() + self.repository.assert_cancelled_scope_schema = Mock() self.processor = CountingProcessor(self.policy) self.client = Mock() with patch.object(processing_runtime, 'compose_object_store', return_value= @@ -56,6 +57,23 @@ class RuntimeTests(unittest.TestCase): mapping_validator=FixtureValidator(), reader_factory=self.readers, page_size=2, max_pages=10, max_records=10) + def test_processing_composition_checks_finance_cancellation_schema(self): + self.repository.assert_cancelled_scope_schema.assert_called_once_with() + + def test_missing_cancellation_migration_closes_storage_and_disables_composition(self): + from arr_ingestion.contracts import IngestionError + self.repository.assert_cancelled_scope_schema.side_effect = IngestionError( + "DATABASE_MIGRATION_MISSING", "ARR processing requires migration 020") + client = Mock() + with patch.object(processing_runtime, 'compose_object_store', return_value= + processing_runtime.ObjectStoreRuntime(client, self.store)), \ + patch.object(processing_runtime, 'PostgresIngestionRepository', return_value=self.repository), \ + self.assertRaises(IngestionError) as error: + processing_runtime.compose_programmatic_processing( + project_root=Path(__file__).resolve().parents[1], connect=Mock()) + self.assertEqual(error.exception.code, "DATABASE_MIGRATION_MISSING") + client.close.assert_called_once_with() + def readers(self, archive, hotel): self.factory_calls += 1 return (source.Reader(archive, hotel, self.transport, sleep=lambda _: None), diff --git a/tests/test_arr_ingestion_postgres.py b/tests/test_arr_ingestion_postgres.py index 1eb519f..04aa1af 100644 --- a/tests/test_arr_ingestion_postgres.py +++ b/tests/test_arr_ingestion_postgres.py @@ -533,7 +533,7 @@ class PostgresIngestionTests(unittest.TestCase): and "INSERT INTO finance.daily_versions" in value ) - self.assertEqual(version_insert.count("%s"), 20) + self.assertEqual(version_insert.count("%s"), 21) class Migration008ContractTests(unittest.TestCase): diff --git a/tests/test_arr_opera_daily_ingest.py b/tests/test_arr_opera_daily_ingest.py index db224c7..b9fd2cd 100644 --- a/tests/test_arr_opera_daily_ingest.py +++ b/tests/test_arr_opera_daily_ingest.py @@ -236,7 +236,7 @@ class ArrOperaDailyIngestTests(unittest.TestCase): self.assertNotIn(forbidden, scripts) self.assertEqual(core.RESULT_VERSION, "4.0") self.assertEqual(core.STRUCTURED_RESULT_SCHEMA_VERSION, "4.0") - self.assertEqual(core.PROCESSOR_VERSION, "4.2.0") + self.assertEqual(core.PROCESSOR_VERSION, "4.3.0") self.assertEqual(len(core.DAILY_HEADERS), 19) self.assertEqual(len(core.RATE_WHITELIST), 20) @@ -302,13 +302,14 @@ class ArrOperaDailyIngestTests(unittest.TestCase): ) self.assertEqual(payload["result_schema_version"], "4.0") - self.assertEqual(payload["processor_version"], "4.2.0") + self.assertEqual(payload["processor_version"], "4.3.0") self.assertEqual(payload["source_rows"], 5) self.assertEqual( payload["outcome_counts"], { "candidate": 0, "duplicate": 1, + "excluded_cancelled": 0, "excluded_rate_code": 1, "price_unmatched": 0, "retained": 3, @@ -643,9 +644,11 @@ class ArrOperaDailyIngestTests(unittest.TestCase): "monthly_report", structured_schema["properties"]["artifacts"]["properties"], ) - retained = structured_schema["$defs"]["record"]["allOf"][1]["then"][ - "properties" - ] + retained = next( + condition["then"]["properties"] + for condition in structured_schema["$defs"]["record"]["allOf"] + if "retained" in condition["if"]["properties"]["outcome"].get("enum", []) + ) self.assertEqual(retained["nights"]["minimum"], 0) def test_cli_has_no_legacy_monthly_surface(self): @@ -799,7 +802,7 @@ class ArrOperaDailyIngestTests(unittest.TestCase): }, } ) - self.assertEqual(delivery.processor_version, "4.2.0") + self.assertEqual(delivery.processor_version, "4.3.0") self.assertEqual(delivery.result_schema_version, "4.0") self.assertEqual(delivery.business_date, date(2026, 7, 27)) diff --git a/tests/test_ohip_reservation_status.py b/tests/test_ohip_reservation_status.py new file mode 100644 index 0000000..1a4d320 --- /dev/null +++ b/tests/test_ohip_reservation_status.py @@ -0,0 +1,310 @@ +"""Offline status evidence checks against the real synthetic ARR collector.""" +import copy +import hashlib +import json +from pathlib import Path +import tempfile +import unittest +from unittest.mock import patch + +from integrations.ohip import arr_data as data +from integrations.ohip import collect_arr_source as base +from integrations.ohip import reservation_status as status +from tests.test_ohip_arr_data import DAY, HOTEL, REQUEST, SimulatedOHIP + + +def digest(raw): + return hashlib.sha256(raw).hexdigest() + + +class SavedStatusEvidenceTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.service = SimulatedOHIP(2) + self.capture_number = 0 + self.collect() + self.snapshot = {file.name: file.read_bytes() for file in self.capture.iterdir()} + + def collect(self): + self.capture_number += 1 + source = data.ARRDataSource(self.root / f"source-{self.capture_number}", HOTEL, + transport_factory=lambda: self.service, sleep=lambda _: None, page_size=1) + summary = source.fetch(DAY, REQUEST) + self.assertTrue(summary["collection_complete"], summary) + self.capture = Path(summary["data_path"]).parent + self.original = (self.capture / "arr-data.json").read_bytes() + self.pin = summary["manifest_sha256"] + + def restore(self): + for file in self.capture.iterdir(): + file.unlink() + for name, raw in self.snapshot.items(): + file = self.capture / name + file.write_bytes(raw) + file.chmod(0o600) + self.original = self.snapshot["arr-data.json"] + self.pin = digest(self.snapshot["result.json"]) + self.capture.chmod(0o700) + + def document(self, name): + return json.loads((self.capture / name).read_bytes()) + + def write(self, name, value): + (self.capture / name).write_bytes(base.json_bytes(value)) + + def repin(self, *, original_changed=False): + manifest = self.document("result.json") + for entry in manifest["files"]: + raw = (self.capture / entry["name"]).read_bytes() + entry.update(bytes=len(raw), sha256=digest(raw)) + manifest["data_sha256"] = digest((self.capture / "arr-data.json").read_bytes()) + self.write("result.json", manifest) + self.pin = digest((self.capture / "result.json").read_bytes()) + if original_changed: + self.original = (self.capture / "arr-data.json").read_bytes() + + def requests(self, operation): + return sorted(file.name for file in self.capture.glob("data-request-*.json") + if not file.name.endswith(".meta.json") + and self.document(file.name)["operation_id"] == operation) + + def response(self, request): + return request.removesuffix(".json") + ".response.bin" + + def evidence(self): + return status.saved_status_evidence(self.capture, self.pin, self.original) + + def assert_rejected(self, code=None): + with self.assertRaises(base.CollectionError) as caught: + self.evidence() + if code: + self.assertEqual(str(caught.exception), code) + + def test_complete_paginated_capture_is_read_only_and_offline(self): + calls = len(self.service.calls) + with patch.object(base, "HTTPTransport", side_effect=AssertionError("network forbidden")), \ + patch.object(base, "load_key", side_effect=AssertionError("credentials forbidden")): + receipt = self.evidence() + self.assertEqual(receipt["version"], status.VERSION) + self.assertEqual(receipt["policy_id"], status.POLICY_ID) + self.assertEqual(receipt["original_sha256"], digest(self.original)) + self.assertEqual(receipt["source_manifest_sha256"], self.pin) + self.assertEqual((receipt["report_date"], receipt["hotel_id"]), (DAY, HOTEL)) + self.assertEqual([(r["source_sequence"], r["reservation_id"], r["reservation_status"]) + for r in receipt["records"]], [(1, "res0", "InHouse"), (2, "res1", "InHouse")]) + for row in receipt["records"]: + self.assertEqual(len(row["sources"]), 9) + self.assertEqual(len({item["file"] for item in row["sources"]}), 9) + for item in row["sources"]: + self.assertEqual(item["sha256"], digest((self.capture / item["file"]).read_bytes())) + self.assertEqual(len(self.service.calls), calls) + self.assertEqual({file.name: file.read_bytes() for file in self.capture.iterdir()}, self.snapshot) + + def test_legacy_original_without_status_is_supported(self): + payload = self.document("arr-data.json") + for row in payload["records"]: + row.pop("reservation_status", None) + self.write("arr-data.json", payload) + self.repin(original_changed=True) + self.assertEqual([r["reservation_status"] for r in self.evidence()["records"]], ["InHouse", "InHouse"]) + self.assertNotIn("reservation_status", self.document("arr-data.json")["records"][0]) + + def test_unknown_nonempty_status_is_preserved_without_interpretation(self): + for row, value in zip(self.service.rows, ("CA", "FutureOracleStatus")): + row["reservationStatus"] = value + self.collect() + self.assertEqual([r["reservation_status"] for r in self.evidence()["records"]], ["CA", "FutureOracleStatus"]) + + def test_retry_failure_is_never_selected_as_evidence(self): + self.service.calls.clear() + self.service.status = lambda operation, count: 503 if operation == base.SEARCH and count == 1 else 200 + self.collect() + failed = self.response(self.requests(base.SEARCH)[0]) + receipt = self.evidence() + self.assertTrue(all(failed not in {item["file"] for item in row["sources"]} for row in receipt["records"])) + # A source row cannot claim that failed response as its status evidence. + payload = self.document("arr-data.json") + payload["records"][0]["sources"].append(failed) + self.write("arr-data.json", payload) + self.repin(original_changed=True) + self.assert_rejected("status_evidence_source_binding_failed") + + def test_pins_detect_manifest_raw_data_and_source_format_changes(self): + for mutation, code in ( + (lambda: setattr(self, "pin", "0" * 64), "status_evidence_manifest_changed"), + (lambda: setattr(self, "original", self.original + b"\n"), "status_evidence_original_changed"), + (lambda: (self.capture / self.response(self.requests(base.DETAIL)[0])).write_bytes(b"{}"), + "status_evidence_archive_changed"), + ): + with self.subTest(code=code): + self.restore() + mutation() + self.assert_rejected(code) + + def test_inventory_rejects_unsafe_duplicate_unlisted_and_orphan_files(self): + for name in ("../escape", "/tmp/escape", "data-request-000001.json/child"): + with self.subTest(name=name): + self.restore() + manifest = self.document("result.json") + manifest["files"][0]["name"] = name + self.write("result.json", manifest) + self.pin = digest((self.capture / "result.json").read_bytes()) + self.assert_rejected("status_evidence_inventory_name_invalid") + self.restore() + manifest = self.document("result.json") + manifest["files"].append(copy.deepcopy(manifest["files"][0])) + self.write("result.json", manifest) + self.pin = digest((self.capture / "result.json").read_bytes()) + self.assert_rejected("status_evidence_inventory_name_invalid") + self.restore() + extra = self.capture / "data-request-999999.response.bin" + extra.write_bytes(b"{}") + extra.chmod(0o600) + self.assert_rejected("status_evidence_inventory_changed") + manifest = self.document("result.json") + manifest["files"].append({"name": extra.name, "bytes": 2, "sha256": digest(b"{}")}) + self.write("result.json", manifest) + self.pin = digest((self.capture / "result.json").read_bytes()) + self.assert_rejected("status_evidence_request_inventory_invalid") + + def test_incomplete_manifest_or_invalid_options_rejected(self): + for target, key, value in (("result.json", "collection_complete", False), + ("result.json", "error", "http_failure"), + ("result.json", "status", "failed"), + ("result.json", "finance_ready", True), + ("capture.json", "options", {"unexpected": 1})): + with self.subTest(target=target, key=key): + self.restore() + document = self.document(target) + document[key] = value + self.write(target, document) + self.repin() + self.assert_rejected() + + def test_request_metadata_and_envelope_fail_closed(self): + request_name = self.requests(base.DETAIL)[0] + response_name = self.response(request_name) + meta_name = request_name.removesuffix(".json") + ".meta.json" + for target, key, value in ((meta_name, "http_status", 500), (meta_name, "error", "transport_failure"), + (meta_name, "oversized", True), (response_name, "operation_id", base.SEARCH), + (response_name, "hotel_id", "OTHER_HOTEL"), + (response_name, "oracle_request_id", " "), + (response_name, "upstream_status", 500), + (response_name, "warnings", [{"message": "partial result"}]), + (request_name, "method", "POST"), + (request_name, "path", "/api/v1/reservations/res0"), + (request_name, "path", None)): + with self.subTest(target=target, key=key, value=value): + self.restore() + document = self.document(target) + document[key] = value + self.write(target, document) + self.repin() + self.assert_rejected() + + def test_typed_identity_hotel_date_and_detail_cardinality_must_match(self): + response_name = self.response(self.requests(base.DETAIL)[0]) + for key, value in (("reservationIdList", [{"type": "Reservation", "id": "res99"}]), + ("reservationIdList", [{"type": "Confirmation", "id": "res0"}]), + ("reservationIdList", [{"type": "Reservation", "id": "res0"}] * 2), + ("hotelId", "OTHER_HOTEL"), + ("roomStay", {"arrivalDate": "2026-09-16"})): + with self.subTest(key=key, value=value): + self.restore() + envelope = self.document(response_name) + envelope["data"]["reservations"]["reservation"][0][key] = value + self.write(response_name, envelope) + self.repin() + self.assert_rejected() + for count in (0, 2): + with self.subTest(count=count): + self.restore() + envelope = self.document(response_name) + envelope["data"]["reservations"]["reservation"] *= count + self.write(response_name, envelope) + self.repin() + self.assert_rejected("status_evidence_detail_ambiguous") + + def test_conflicting_missing_empty_and_nonstring_status_rejected(self): + response_name = self.response(self.requests(base.DETAIL)[0]) + for value in (None, "", " ", 0, False, {}, [], "Cancelled"): + with self.subTest(value=value): + self.restore() + envelope = self.document(response_name) + envelope["data"]["reservations"]["reservation"][0]["reservationStatus"] = value + self.write(response_name, envelope) + self.repin() + self.assert_rejected("status_evidence_status_conflict") + self.restore() + envelope = self.document(response_name) + envelope["data"]["reservations"]["reservation"][0].pop("reservationStatus") + self.write(response_name, envelope) + self.repin() + self.assert_rejected("status_evidence_status_conflict") + + def test_modified_timestamp_and_final_search_drift_rejected(self): + detail_name = self.response(self.requests(base.DETAIL)[0]) + envelope = self.document(detail_name) + envelope["data"]["reservations"]["reservation"][0]["lastModifyDateTime"] = "later" + self.write(detail_name, envelope) + self.repin() + self.assert_rejected("status_evidence_state_conflict") + self.restore() + final_name = self.response(self.requests(base.SEARCH)[-1]) + envelope = self.document(final_name) + envelope["data"]["reservations"]["reservationInfo"][0]["reservationStatus"] = "Cancelled" + self.write(final_name, envelope) + self.repin() + self.assert_rejected("status_evidence_search_changed") + + def test_original_order_existing_status_and_source_refs_are_bound(self): + for mutate in ( + lambda rows: rows.reverse(), + lambda rows: rows[0].update(source_sequence=2), + lambda rows: rows[0].update(reservation_id="res99"), + lambda rows: rows[0].update(reservation_status="Cancelled"), + lambda rows: rows[0].update(sources=rows[1]["sources"]), + lambda rows: rows[0]["sources"].append(self.response(self.requests(base.DETAIL)[1])), + lambda rows: rows[0]["sources"].append(rows[0]["sources"][0]), + ): + with self.subTest(mutation=mutate): + self.restore() + payload = self.document("arr-data.json") + mutate(payload["records"]) + self.write("arr-data.json", payload) + self.repin(original_changed=True) + self.assert_rejected() + + def test_search_body_cannot_change_date_status_scope_or_pagination(self): + request_name = self.requests(base.SEARCH)[0] + for key, value in (("arrivalStartDate", "2026-09-16"), ("reservationStatus", ["InHouse"]), + ("offset", 1), ("limit", 100)): + with self.subTest(key=key): + self.restore() + request = self.document(request_name) + request["body"][key] = value + self.write(request_name, request) + self.repin() + self.assert_rejected() + + def test_archive_private_permissions_and_no_symlink_files(self): + self.capture.chmod(0o755) + self.assert_rejected("status_evidence_directory_unsafe") + self.capture.chmod(0o700) + target = self.capture / "arr-data.json" + target.chmod(0o644) + self.assert_rejected("unsafe_archive_file") + target.chmod(0o600) + outside = self.root / "outside.json" + outside.write_bytes(target.read_bytes()) + outside.chmod(0o600) + target.unlink() + target.symlink_to(outside) + with self.assertRaises(OSError): + self.evidence() + + +if __name__ == "__main__": + unittest.main()