# Rate Code 识别规则

本文档整理当前已确认的 Rate Code 判断逻辑。判断时先识别渠道，再套用对应渠道规则；不管哪个渠道，只要无法匹配到可选 Rate Code，最终都改为手工录入具体结算价格。

## 总原则

1. 先判断订单渠道或市场类别：目前已整理 `QBD`、`LianTai`、其他市场代理、特殊类别、AI 代理、批发商、导游房等规则。
2. 按渠道套用对应规则，不混用不同渠道的判断逻辑。
3. Rate Code 判断必须保留原始房型关键线索，例如 `Sup`、`高级房`、`U-`。不得只根据映射后的 Opera Room Type Code（如 `RM2` / `RM3`）判断 Rate Code。
4. 同样映射到 `RM2` / `RM3`，不代表 Rate Code 相同。例如 LianTai 中 `Sup 高级房TWN` 与 `U-TWN` 都可映射到 `RM3`，但 Rate Code 不同。
5. `pms_room_type_code` 必须来自 `room_type_mapping_rules.md` 的唯一命中；本文可引用该代码作为判断依据，但不得作为房型映射的唯一来源。
6. 对于 New Booking 中的新建控房 / 配额 / Allotment / Control Block，如果邮件没有明示 Rate Code，但房型可命中本文的控房默认补全规则，应按房型自动补全房型代码和结算价格，不得仅因缺少 Rate Code 输出 manual_review。
7. LianTai 复合单价表达中的附加费用组件不参与 Rate Code 判断；不得把 `2000+500` 合并为 `2500` 判断 Rate Code。
8. 如果订单数据无法匹配任何 Rate Code，或者系统内无法选择对应 Rate Code，则不强行选择代码，改为手工录入具体结算价格。

## New Booking 控房 / 配额 / Control Block 缺 Rate Code 自动补充规则

本节适用于 `new_booking_skill` 中的新建控房 / 配额 / Allotment / Control Block 类任务。

当 current 邮件明确是新建控房、配额或 Control Block，且邮件没有直接写出 Rate Code 时，先按房型自动补全以下字段：

| 原始房型 / 表达 | Opera Room Type Code | 结算价格 |
|---|---|---:|
| `U-DBL` | `RM2` | 850 |
| `U-TWN` | `RM3` | 850 |
| `U-TRP` / `U-TRP=FAM (6+4)` / `FAM (6+4)` | `RM4` | 1275 |

输出要求：

- 不要因为邮件未明示 Rate Code 就直接输出 `manual_review`。
- 在 `extracted_fields` 中保留原始房型、归一化房型、`pms_room_type_code`、结算价格、命中规则，以及 `rate_code_missing_in_email=true`。
- 如统一输出契约有 `rate_code_result`，应标记为 `auto_completed_from_control_block_room_type_default`，并写入对应房型代码、价格和已确认的 Rate Code。
- 对于 `U-DBL` / `U-TWN`，如 LianTai 原始房型线索明确，可按 LianTai 原始房型关键线索规则写入 `WHO1-850UP`。
- 如果一条控房任务包含多个房型，按房型逐项补全。
- 如果房型不在本表，或 `U-TRP` / `FAM` / `6+4` 关系无法确认，输出结构化 `manual_review`，并在 `manual_review.evidence_to_check` 中提示人工核对房型和 Rate/价格规则。
- 当前只对本文明确覆盖的房型写入 Rate Code；例如 `U-TRP` / `FAM (6+4)` 当前只确认房型代码和结算价格，若信息系统要求具体 Opera Rate Code 字符串，不得由 AI 编造。

## QBD 渠道规则

QBD 渠道主要看房型是否带有 `U` 标号。

核心口径：

- 带 `U` 标号的房型，按有 `UP` 的 Rate Code 处理。
- 不带 `U` 标号的普通房型，按没有 `UP` 的 Rate Code 处理。
- `U` 标号指 `U-DBL`、`U-TWN`、`U-TRP`、`U-TWN=850` 等显式升级标识；`Sup` 单词中的字母 `u` 不算 `U` 标号。

| 判断条件 | Rate Code |
|---|---|
| 房型不带 `U` 标号，例如 `DBL`、`TWN` | `GRPA1-900` |
| 房型带 `U` 标号，例如 `U-TWN`、`U-DBL`、`U-TWN=850` | `GRPA2-850UP` |
| `ST+K:2卧1厅=1200` + `FAMILY 1`，且价格 1200 来自 current | `rate_code=null`，手工录入结算价 1200 |
| 无法根据房型判断，或无法选择对应 Rate Code | 手工录入具体结算价格 |

### QBD 示例

| 订单信息 | 判断 | Rate Code |
|---|---|---|
| `【DBL】`、`【TWN】2` | 普通房型，不带 `U` 标号 | `GRPA1-900` |
| `【U-TWN=850】` | 房型带 `U` 标号 | `GRPA2-850UP` |
| `ST+K:2卧1厅=1200` + `FAMILY 1` | QBD 没有已确认可选 Rate Code；价格 1200 可靠时走手工结算价 | `rate_code=null`，`manual_settlement_price_required=true` |

## LianTai 渠道规则

LianTai 渠道应先保留原始房型关键线索，再结合备注中的价格数字判断 Rate Code。`Sup` / 高级房和 `U-` 是关键 Rate Code 线索，不得在房型映射后丢弃。

在 parent-to-child 配合房 / Allotment 创建场景中，header 级价格线索可以继承到 child group line：

- 例如 current header 写 `AMEND +FINAL BOOKING U-1200`，后续 child line 只列 `TWN` / `DBL` / `SUITE` 和房量时，`U-1200` 可作为该批 child allocation 的价格 evidence。
- 提取 `1200` 后按 LianTai 价格代码表判断；能唯一命中时可写入 `WHO3-1200`，并在 `rate_code_result` 中记录 `price_source="parent_allocation_header"`、`raw_price_indicator="U-1200"`。
- 不得把 header 价格线索当作房量，也不得覆盖 child line 自己更具体的价格 / 复合单价表达。
- 如果 header 价格与 child line 自己的明确价格或复合单价冲突，按 child line 证据优先；仍冲突则输出结构化 `manual_review`。

### 原始房型关键线索规则

LianTai 渠道中，Rate Code 不能只根据 Opera Room Type Code 判断，必须先看原始房型关键线索。

| 原始房型关键线索 | Opera Room Type Code | Rate Code | 结算价格线索 |
|---|---|---|---|
| `Sup 高级房TWN` / `Sup【高级房TWN】` | `RM3` | `GRP1-900` | 900，或备注价格 1800 折半为 900 |
| `Sup 高级房DBL` / `Sup【高级房DBL】` | `RM2` | `GRP1-900` | 900，或备注价格 1800 折半为 900 |
| `U-TWN` | `RM3` | `WHO1-850UP` | 850 |
| `U-DBL` | `RM2` | `WHO1-850UP` | 850 |
| 普通 `TWN`，不带 `U` 标号 | `RM3` | `GRP1-900` | 900 |
| 普通 `DBL`，不带 `U` 标号 | `RM2` | `GRP1-900` | 900 |
| `ST+K:2卧1厅=1200` + `FAMILY 1` | `SU3` | `WHO3-1200` | 1200 |

关键约束：

- 带 `U` 标号的房型，按有 `UP` 的 Rate Code 处理；不带 `U` 标号的普通房型，按没有 `UP` 的 Rate Code 处理。
- `U` 标号指 `U-DBL`、`U-TWN`、`U-TRP`、`U-TWN=850` 等显式升级标识；`Sup` 单词中的字母 `u` 不算 `U` 标号。
- `Sup 高级房TWN` 与 `U-TWN` 都可能映射到 `RM3`，但前者 Rate Code 为 `GRP1-900`，后者为 `WHO1-850UP`。
- `Sup 高级房DBL` 与 `U-DBL` 都可能映射到 `RM2`，但前者 Rate Code 为 `GRP1-900`，后者为 `WHO1-850UP`。
- `ST+K:2卧1厅=1200` + `FAMILY 1` 只有在原始表达同时包含 `ST+K`、`2卧1厅`、`1200`、`FAMILY` 时才命中；其中 `1` 是房量，`1200` 是价格线索。LianTai 渠道输出 `WHO3-1200`；QBD 渠道不得编造 Rate Code，价格可靠时走 `rate_code=null` + 手工结算价 1200。
- LianTai 复合单价表达优先于普通 `TWN` / `DBL` 的默认 Rate Code 判断；例如 `5DBL ... 2000THB+500` 应先拆分复合单价，使用 `2000 / 2 = 1000 -> WHO2-1000`，而不是只因 `DBL` 命中 `GRP1-900`。
- `rate_code_result` 必须记录 `original_room_type`、`normalized_room_type`、`pms_room_type_code`、`rate_code_basis` 和命中的原始关键线索。
- 如果只剩 `RM2` / `RM3`，但原始 `Sup` / `U-` 线索丢失，不得仅凭 Room Type Code 反推 Rate Code；可用备注价格继续判断，仍无法唯一判断时输出结构化 `manual_review`。
- 如果原始房型关键线索与备注价格明显冲突，输出结构化 `manual_review`，并在 `manual_review.conflicting_points` 中说明冲突。

### 判断顺序

1. 保留并读取原始房型关键线索，例如 `Sup` / `高级房` / `U-` / 普通 `TWN` / 普通 `DBL` / `ST+K` / `2卧1厅`。
2. 如果原始价格表达可拆分为 LianTai 复合单价，先按复合单价规则拆分房费单价组件和 Fix Charge 单价组件。
3. 如果没有复合单价，再判断原始房型关键线索是否命中上表。
4. 从备注中提取价格数字，例如 `850`、`900`、`1000`、`1100`、`1200`、`1800`。
5. 如果原始房型关键线索没有命中，再用备注价格直接匹配 LianTai 价格代码表。
6. 如果备注价格无法直接匹配，则用 `备注价格 ÷ 2` 得到结算价，再匹配 LianTai 价格代码表。
7. 如果除以 2 后仍然无法匹配 Rate Code，则手工录入具体结算价格。

### LianTai 价格代码表

| 可匹配价格 | Rate Code |
|---:|---|
| 850 | `WHO1-850UP` |
| 900 | `GRP1-900` |
| 1000 | `WHO2-1000` |
| 1200 | `WHO3-1200` |

### LianTai 复合单价 / Fix Charge 规则

本节用于 LianTai 表格酒店说明列或 current 事件价格区中的复合单价表达。复合单价表示“房费单价 + Fix Charge 单价”，不是总价。

典型表达：

- `2000THB+500`
- `2000+500THB`
- `1800THB+500`
- `1800THB+500THB`
- `2000THB*2 +附加500*2`
- `2000THB*2` 换行后 `+附加500*2`
- `春节附加费` / `ตรุษจีนเซอร์ชาร์จ` / `surcharge` 可作为辅助证据，但不是必要条件。

解析规则：

1. 只有出现可拆分的 `房费单价组件 + 附加费用单价组件` 时，才触发 Fix Charge。
2. `TWN` / `DBL` / 普通房型本身不触发 Fix Charge。
3. 普通单一价格如 `2000THB`、`1800THB` 不触发 Fix Charge，继续按既有 LianTai Rate Code 规则处理。
4. 不得把 `2000THB+500` 合并成 `2500` 判断 Rate Code。
5. `+` 前的数字是 `base_room_price_component`，用于 Rate Code 判断。
6. `+` 后的数字是 `additional_charge_component`，写入 `fix_charge_items.amount`。
7. Fix Charge 金额不固定，不得把 `500` 作为唯一触发金额。
8. AI 不负责计算 Fix Charge 数量或总额；若原文有 `*数量`，可保留为原文证据。

Rate Code 判断：

| 复合单价表达 | base_room_price_component | Rate Code 判断 | Fix Charge 单价 |
|---|---:|---|---:|
| `2000THB+500` | 2000 | 2000 ÷ 2 = 1000 -> `WHO2-1000` | 500 |
| `1800THB+500` | 1800 | 1800 ÷ 2 = 900 -> `GRP1-900` | 500 |
| `2000THB*2 +附加500*2` | 2000 | 2000 ÷ 2 = 1000 -> `WHO2-1000` | 500 |

输出要求：

- 在 `rate_code_result` 中记录 `raw_price_expression`、`composite_unit_price_detected=true`、`base_room_price_component`、`additional_charge_component`、`settlement_price`、`rate_code` 和 `rate_code_basis`。
- 在 `fix_charge_items` 中记录 `charge_type=additional_charge`、`amount`、`currency=THB`、`pricing_mode=unit_price`、`unit_basis`、`quantity=null`、`total_amount=null`、`raw_text`、`evidence_source=current_table_row`、`requires_followup_tool=true`、`followup_tool_name=create_fix_charge / TBD`。
- 在 `additional_operations` 中记录 `operation_type=create_fix_charge` 和 `status=pending_booking_id`。
- 没有复合单价或附加费组件时，可记录 `fix_charge_required=false`、`fix_charge_items=[]`，并继续正常 Rate Code 判断。
- 如果出现 `2500THB` 这类单一价格，不得自动拆成 `2000+500`。
- 如果 `+` 前后无法判断房费单价和附加费用单价，或同一行多个价格组件无法归属，输出结构化 `manual_review`。
- 如果原始房型线索与复合单价判断明显冲突，输出结构化 `manual_review`，并在 `manual_review.conflicting_points` 中说明冲突。

### LianTai 示例

| 备注价格 | 判断过程 | Rate Code / 操作 |
|---:|---|---|
| `Sup 高级房TWN`，备注价格 1800 | 原始房型命中 Sup 高级房；1800 ÷ 2 = 900 作为支持证据 | `GRP1-900` |
| `Sup 高级房DBL`，备注价格 1800 | 原始房型命中 Sup 高级房；1800 ÷ 2 = 900 作为支持证据 | `GRP1-900` |
| `U-TWN`，备注价格 850 | 原始房型命中 U-TWN | `WHO1-850UP` |
| `U-DBL`，备注价格 850 | 原始房型命中 U-DBL | `WHO1-850UP` |
| 普通 `TWN`，不带 `U` 标号 | 原始房型不带 U 标号，按没有 UP 的 Rate Code | `GRP1-900` |
| 普通 `DBL`，不带 `U` 标号 | 原始房型不带 U 标号，按没有 UP 的 Rate Code | `GRP1-900` |
| `(【ST+K:2卧1厅=1200】FAMILY 1)` | 原始表达同时命中 `ST+K`、`2卧1厅`、`1200`、`FAMILY`；`1` 是房量 | `WHO3-1200` |
| `2000THB+500` | 复合单价；2000 为房费单价组件，500 为 Fix Charge 单价组件；2000 ÷ 2 = 1000 | `WHO2-1000`，并写入 Fix Charge amount 500 |
| `1800THB+500` | 复合单价；1800 为房费单价组件，500 为 Fix Charge 单价组件；1800 ÷ 2 = 900 | `GRP1-900`，并写入 Fix Charge amount 500 |
| 850 | 850 可直接匹配 | `WHO1-850UP` |
| 900 | 900 可直接匹配 | `GRP1-900` |
| 1000 | 1000 可直接匹配 | `WHO2-1000` |
| 1200 | 1200 可直接匹配 | `WHO3-1200` |
| 1800 | 1800 无法直接匹配；1800 ÷ 2 = 900，可匹配 | `GRP1-900` |
| 1100 | 1100 无法直接匹配；1100 ÷ 2 = 550，仍无法匹配 | 手工录入具体结算价格 |

## 其他市场与特殊类别规则

本节适用于非 `QBD`、非 `LianTai` 的订单。判断时优先看市场/代理类别、用餐餐厅、房型、是否含早餐，以及备注中的价格。

### 中国市场代理（其他公司）

用餐安排：在 Bualuang 餐厅用餐。

| 价格 | Rate Code |
|---:|---|
| 2000 | `GL2000B` |
| 2200 | `GL2200B` |
| 2300 | `GL2300B` |

### 台湾市场代理

用餐安排：在 Leelawadee（丽拉）餐厅用餐。

| 价格 | Rate Code | 备注 |
|---:|---|---|
| 2300 | `GL2300KR` | 与韩国市场共用 |
| 2400 | `GL2400TW` | 台湾市场专用 |

### 韩国市场代理

用餐安排：在 Leelawadee 餐厅用餐。

| 价格 | Rate Code | 备注 |
|---:|---|---|
| 2100 | `GL2100KR` | 韩国市场 |
| 2200 | `GL2200KR` | 韩国市场 |
| 2300 | `GL2300KR` | 与台湾市场共用 |

### 特殊类别

用餐安排：在 Leelawadee 餐厅享用早餐。以下类别有 Rate Code，但无固定预设房价，需要在系统中手工输入价格。

| 类别 | Rate Code | 操作 |
|---|---|---|
| 企业客（Corporate） | `GLSPCB` | 选择代码后手工输入价格 |
| 政府客（Government） | `GGV` | 选择代码后手工输入价格 |
| 预订部直接预订（Direct to Reservation Department） | `GDISPC` | 选择代码后手工输入价格 |

### 散客预订（FIT Booking）

当前未提供固定 Rate Code 规则。若订单无法匹配已有价格代码，按总原则手工录入具体结算价格。

### AI 代理预订（Rainbow 公司和社交媒体渠道）

根据是否含早餐选择 `LBSM` 或 `LBMS`，再按房型对应价格录入。

| 房型 | `LBSM` 不含早餐 | `LBMS` 含早餐 |
|---|---:|---:|
| SUP（高级房） | 900 | 1100 |
| DLX（豪华房） | 1000 | 1200 |
| 6+4（特定房型） | 不可售 | 1300 |
| ONE BED（单卧房/套房） | 1800 | 2400 |
| TWO BED（双卧房/套房） | 3800 | 4000 |

### 中国市场代理（其他）

| 价格 | Rate Code | 备注 |
|---:|---|---|
| 2500 | `LDCH25` | 如果实际不是 2500，请直接在系统中手工修改价格 |

### 韩国市场代理（WHKR）

| 价格 | Rate Code |
|---:|---|
| 2100 | `WHKR2100B` |
| 2200 | `WHKR2200B` |

### 俄罗斯市场代理

| Rate Code | 操作 |
|---|---|
| `LBAJ` | 无预设固定房价，请在系统中手工输入或修改价格 |

### 其他批发商代理（Other Wholesale）

| Rate Code | 操作 |
|---|---|
| `LDWT` | 无预设固定房价，请在系统中手工输入或修改价格 |

### 导游房（Guide Rooms）

专门用于带团导游安排的房间及用餐标准。

| Rate Code | 价格 | 用餐安排 |
|---|---:|---|
| `WHOG1` | 1200 | Bualuang 餐厅 |
| `WHOG2` | 800 | Bualuang 餐厅 |
| `WHOG3` | 1200 | Leela 餐厅 |
| `WHOG4` | 800 | Leel 餐厅 |

## 手工录入规则

以下情况需要手工录入具体结算价格：

1. 渠道或市场类别不在当前已确认规则范围内。
2. QBD 渠道无法根据房型判断 Rate Code。
3. LianTai 渠道只有 `RM2` / `RM3` 等映射后房型代码，但原始 `Sup` / `U-` 线索缺失，且备注价格也无法唯一判断。
4. LianTai 渠道备注价格无法直接匹配，且 `备注价格 ÷ 2` 后仍无法匹配。
5. 其他市场或特殊类别无法根据价格、房型、是否含早餐、用餐餐厅匹配 Rate Code。
6. Rate Code 规则上能匹配，但系统内没有对应代码可选。
7. 规则要求手工输入或修改价格，例如 `GLSPCB`、`GGV`、`GDISPC`、`LBAJ`、`LDWT` 等无固定预设房价代码。

手工录入时，以订单或酒店确认的实际结算价格为准；如果 LianTai 备注价格需要折半后才是结算价，则按折半后的结算价录入。

### 手工价格仍可 normal_task 的条件

只有同时满足以下条件，才允许输出 `normal_task` 并使用手工结算价格维护：

1. 当前业务动作明确来自 `body_current` 或当前表格行。
2. `routing_context` 未发现阻断自动处理的既有有效订单、pending/open task、active workflow 或终止冲突。
3. 房型已由 `room_type_mapping_rules.md` 唯一命中，并能填入 `pms_room_type_code`。
4. 日期、晚数、房量、对象类型等创建任务所需核心字段可靠。
5. 实际结算价格来自 `body_current` 或当前表格行，且可可靠提取。
6. 本 reference 明确允许该场景手工维护价格。

输出必须包含：`rate_code=null`、`settlement_price`、`manual_settlement_price_required=true`、`manual_price_reason_code`、`price_evidence`。

任一条件不满足时，输出 structured `manual_review`，建议使用 `MANUAL_PRICE_UNCONFIRMED`。

## 快速判断流程

```text
先识别渠道

IF 渠道 = QBD:
  IF 原始表达同时包含 ST+K、2卧1厅、1200、FAMILY:
    SET rate_code = null
    SET settlement_price = 1200
    SET manual_settlement_price_required = true
    SET manual_price_reason_code = QBD_STK_FAMILY_MANUAL_PRICE
  ELSE IF 房型带 U 标号:
    SELECT GRPA2-850UP
  ELSE IF 房型为普通 DBL / TWN 等且不带 U 标号:
    SELECT GRPA1-900
  ELSE:
    手工录入具体结算价格

ELSE IF 渠道 = LianTai:
  IF 原始价格表达可拆分为 房费单价组件 + 附加费用单价组件:
    SELECT Rate Code from 房费单价组件 only
    ADD 附加费用单价组件 to fix_charge_items
  ELSE IF 原始房型带 U 标号:
    SELECT WHO1-850UP
  ELSE IF 原始表达同时包含 ST+K、2卧1厅、1200、FAMILY:
    SELECT WHO3-1200
  ELSE IF 原始房型为普通 TWN/DBL，或包含 SUP/高级房 + TWN/DBL:
    SELECT GRP1-900
  ELSE:
    P = 备注价格

    IF P 可匹配 LianTai 价格代码表:
      SELECT 对应 Rate Code
    ELSE:
      P2 = P / 2
      IF P2 可匹配 LianTai 价格代码表:
        SELECT 对应 Rate Code
      ELSE:
        手工录入具体结算价格

ELSE IF 渠道/市场类别可匹配其他市场与特殊类别规则:
  按对应市场、用餐餐厅、房型、是否含早餐、价格选择 Rate Code
  IF 规则要求手工输入价格:
    选择对应 Rate Code 后手工输入或修改价格
  ELSE IF 无法匹配:
    手工录入具体结算价格

ELSE:
  手工录入具体结算价格
```
