# BCP Core Data Model Reference

> This document describes the entities, relationships, and data flows of the InsureMO BCP (Billing/Collection/Payment) module for General Insurance.

---

## Entity Relationship Diagram

```
BcpBizTransaction ----> BcpFee --------------------------> GLIntegrationVoucherDetail
   (root)             (fee breakdown)                     (via GLMappingRule - Data Table)
   |
   +----> BcpArap <-----------------------------------------------+
   |      (AR/AP)                                                |
   |                                                             |
   |      +-----------------> BcpCollection -----> BcpCollectionDetail ----> BcpSuspense
   |      |                     (money in)        (detail)              (parked funds)
   |      |                                                                   |
   |      +--------------- BcpOffset <--------------------------------------------+
   |                     (AR/AP netting)                                         |
   |                                                                             |
   +----> BcpPayment ----> BcpPaymentDetail ----> BcpArap                       |
        (money out)        (detail)           (direct clear, NO Offset)           |
                                                                                 |
                                                                         (Suspense used in Offset)
```
---

## Core Entities

### BcpBizTransaction — Root Business Transaction

The root object for every financial activity in BCP. **Immutable once created.**

| Field | Description | Code Table |
|-------|-------------|------------|
| `BizTransId` | Unique identifier | — |
| `BizTransType` | Transaction type (NEWBIZ, ENDORSEMENT, CLAIM, etc.) | `BizTransactionType` |
| `PolicyNo` | Associated policy number | — |
| `ProductCode` | Associated product | `Products` |
| `BizModule` | Module origin (PA, Claim, Finance) | `BizModule` |
| `BizSource` | Direct, Fac In, Coinsurance | `BizSource` |
| `Amount` | Transaction amount | — |
| `CurrencyCode` | Transaction currency | — |
| `PaymentMethod` | Payment method (Cash, Cheque, etc.) | `BcpPaymentMethod` |
| `ChannelCode` | Producer/Agent ID | `SalesChannelAPI` |
| `RefTransId` | Reference transaction ID for lookups | — |
| `BizTransTime` | Timestamp of the transaction | — |

**API:** `loadBizTransaction`, `loadBizTransactionsByRefTransId`, `queryEsData`

---

### BcpFee — Fee Component

Raw data source for GL generation. Contains detailed fee breakdowns (Net Premium, Tax, Commission). The original Fee created during billing is **immutable once sent**, but **new Fee records** are generated during each subsequent transaction (Collection, Payment, Offset, Reverse) to record fee movements for GL.

| Field | Description | Code Table |
|-------|-------------|--------|
| `BcpFeeId` | Unique identifier | — |
| `BizTransId` | Parent BizTransaction | — |
| `BcpFeeType` | Fee type code | — |
| `Amount` | Fee amount | — |
| `AmountSign` | Sign (+/-) indicating debit/credit | — |
| `CurrencyCode` | Currency | — |

One BizTransaction can produce **multiple Fee records**. Each fee may:
- **Enter BcpArap** — trigger receivable/payable creation
- **Enter SalesCommission** — create commission records in orchestration layer
- **Send to GL** — map to General Ledger via GLMappingRule - Data Table

**API:** `syncDataToBCP`, `getFeeList`, `syncAdditionalFeeToBCP`

---

### BcpArap — Accounts Receivable / Payable

Tracks BCP transaction status (Outstanding, Paid, Cancelled) and balance (remaining amount to collect/pay).

| Field | Description | Code Table |
|-------|-------------|--------|
| `ArapId` | Unique identifier | — |
| `BizTransId` | Parent BizTransaction | — |
| `ArapType` | Premium, Claim Settlement etc. | BcpArapType |
| `Amount` | Original AR/AP amount | — |
| `Balance` | Remaining uncollected/unpaid amount | — |
| `ArapStatus` | 1=Outstanding, 2=Settled, 7=Cancelled | BcpArapStatus |
| `ArapCate` | 1=Receivable, 2=Payable | BcpArapCate |
| `CurrencyCode` | Currency | — |
| `DueDate` | Payment due date | — |

**Key Rule:** One business data transmission with multiple fee components generates **one AR or one AP per installment**.

---

### BcpCollection — Collection Record

Money received from customers (premium payments, endorsements, prepayments).

| Field | Description | Code Table |
|-------|-------------|------------|
| `CollectionId` | Unique identifier  | — |
| `CollectionNo` | Business reference number | — |
| `CollectionType` | 1=Prepay, 2=collection, 3=Policy Premium, etc. | `BcpCollectionType` |
| `CollectionStatus` | 1=Collected, 2=Confirmed, 3=Reversed, 4=Refund | `BcpCollectionStatus` |
| `PaymentMethod` | Method of payout (Cheque, Credit Card, etc.) | `BcpPaymentMethod` |
| `Amount` | Total collected amount | — |
| `ReceiptNo` | Manual or system-generated receipt number | — |
| `PayerPayeeName` | Name of the payer | — |
| `ConfirmTime` | When the collection was finalized | — |
| `BankAccountNo` | Source bank account | — |
| `OrderIds` | Links to BcpPrepayOrder (for prepayments) | — |

**CollectionDetail sub-records:**
| Field | Description |
|-------|-------------|
| `Amount` | Detail amount |
| `CurrencyCode` | Currency |
| `RefId` | Reference ID (links to BcpSuspense) |
| `RefType` | Always **2** (Suspense) for CollectionDetail |

#### Timestamp Fields

BcpCollection carries **three categories** of timestamps. Each business-event timestamp has a paired **System** variant that records the actual system-clock time at processing (useful for audit when the business time was back-dated or forward-dated).

**Business-event timestamps** — when the business event nominally occurred (the `*Time` fields; set only if not already provided, so they may be back-dated by the caller):

| Field | When set |
|-------|----------|
| `ReceiveTime` | When the receipt/payment was received. Auto-filled only if null — the caller may pass a back-dated value (e.g. Prepay `ReceiptDate`, or manual receipt input). |
| `ConfirmTime` | When the collection was confirmed/finalized (normally triggered by a 3rd-party payment-integration callback). |
| `ReverseTime` | When the collection was reversed. |
| `LastOperateTime` | Refreshed by **every** business operation that touches the collection (collect, apply/offset, confirm, reverse, payment). Paired with `LastOperateBy` (who operated). |

**System timestamps** — the real system clock at processing; unconditionally overwritten and not editable by the caller:

| Field | When set |
|-------|----------|
| `SystemReceiveTime` | Set at collection creation, always overwritten regardless of caller input. |
| `SystemConfirmTime` | Set when confirmed. |
| `SystemReverseTime` | Set when reversed. |

**Audit timestamps** — inherited from the base domain model (set via `@PrePersist` / `@PreUpdate`), not Collection-specific:

| Field | Description |
|-------|-------------|
| `InsertTime` | DB row creation time. Set once on first persist. |
| `UpdateTime` | DB row last-modified time. Refreshed on every `insertOrUpdate`, including non-business field changes. |

<div class="docs-note"><span class="docs-admonitions-text">note</span>

**How to distinguish the three categories in practice:**

- Use **`ReceiveTime`** for **business / reporting** cut-offs and reconciliation — it can be back-dated.
- Compare **`ReceiveTime`** vs **`SystemReceiveTime`** to detect back-dated or forward-dated receipts (any divergence means the business time was supplied rather than auto-filled).
- Use **`LastOperateTime`** + **`LastOperateBy`** to see *when business activity last touched* the receipt (business-level activity) — **not** `UpdateTime`.
- Use **`InsertTime`** / **`UpdateTime`** for **DB-level** audit and incremental data sync only; they have no direct business meaning.

All timestamps use `DateContext.getLocalDateTimeForCurrentUser()` — they are recorded in the **current user's timezone**; apply timezone conversion when displaying across regions.

</div>

**API:** `collectionApplication`, `confirmCollection`, `policyCollectionAndOffset`, `producerCollect`

---

### BcpSuspense — Suspense Record

Temporarily parked funds awaiting allocation or reconciliation.

| Field | Description | Code Table |
|-------|-------------|------------|
| `SuspenseId` | Unique identifier | — |
| `SuspenseNo` | Business reference number | — |
| `SuspenseType` | Quotation, Producer, Collection, etc. | `BcpSuspenseType` |
| `SuspenseStatus` | Inactive (0), Active (1) | `BcpSuspenseStatus` |
| `Balance` | Remaining unallocated amount | — |
| `CurrencyCode` | Currency | — |
| `ReferenceNo` | Related document reference | — |

**SuspenseDetail sub-records:**
| RefType | Meaning |
|---------|---------|
| 3 | Collection |
| 4 | Premium Offset |
| 5 | Reverse |

---

### BcpOffset — Offset / Settlement Record

AR/AP netting engine. Matches AR and AP entries to reduce balances to zero. **Does NOT involve Payment.**

| Field | Description | Code Table |
|-------|-------------|------------|
| `OffsetId` | Unique identifier  | — |
| `OffsetNo` | Business reference number (prefix: OFS) | — |
| `OffsetType` | 1=ARAP Offset, 2=Application, 3=Tolerance, 4=Write-off | `BcpOffsetType` |
| `OffsetStatus` | 1=Active, 2=Reversed | `BcpOffsetStatus` |
| `Amount` | Amount matched/reconciled | — |
| `OffsetTime` | Date/time of reconciliation | — |
| `StatementNo` | Associated statement (if applicable) | — |
| `ToleranceOffsetReason` | Reason for small balance write-offs | `BcpToleranceOffsetReason` |

**OffsetDetail sub-records — links Offset to source records:**
| RefType | Meaning |
|---------|---------|
| 1 | Links to **BcpFee** (Commission Offset) |
| 2 | Links to **BcpSuspense** |
| 3 | Links to **BcpArap** |

**API:** `arapOffset`, `toleranceOffset`, `reverseOffset`

---

### BcpPayment — Payment (Disbursement) Record

Money going out: claim payouts, premium refunds (endorsement/cancellation). **Does NOT go through Offset.** Directly clears BcpArap balance.

| Field | Description | Code Table |
|-------|-------------|------------|
| `PaymentId` | Unique identifier  | — |
| `PaymentNo` | Business reference number (prefix: PYMT) | — |
| `PaymentStatus` | 2=Approved, 3=Paid, 5=Reversed | `BcpPaymentStatus` |
| `PaymentType` | 1=Direct Billing, 2=Agent Billing | `BcpPaymentType` |
| `Amount` | Total payout amount | — |
| `PayerPayeeCode` | ID of the payee (claimant/agent) | — |
| `PayerPayeeName` | Name of the payee | — |
| `ApproveBy` | User who authorized the payment | `UserInfo` |
| `ApproveTime` | When payment was approved | — |
| `CompleteTime` | When payment was settled | — |
| `PaymentMethod` | Method of payout (Cheque, Credit Card, etc.) | `BcpPaymentMethod` |
| `ArapIds` | List of source BcpArap IDs | — |
| `BizTransId` | Parent BizTransaction | — |

**PaymentDetail sub-records:**
| Field | Description |
|-------|-------------|
| `Amount` | Detail amount |
| `RefId` | Reference ID (links to BcpArap) |
| `RefType` | Always **3** (ARAP) for PaymentDetail |

**Important**: PaymentDetail.RefType is **always 3** (ARAP), unlike OffsetDetail which uses both RefType 2 (Suspense) and RefType 3 (ARAP).

**PaymentStatus lifecycle:**
```
[Created] → Status 2 (Approved) → Status 3 (Paid) → Status 5 (Reversed)
                           │
                           └──► singlePayment API
                                sets ArapStatus=2 (Settled)
                                sets Arap.Balance=0
                                updates SalesCommission.CommissionStatus=2
```

**API:** `approvePaymentWithEs`, `approvePaymentByArapIds`, `approvePaymentAdvanced`, `approveStatementPayment`, `singlePayment`, `massivePayment`, `receiptRefund`, `reversePayment`

---

### BcpPrepayOrder — Prepayment Order

Prepayment orders created when `IsPrepay="Y"` (Cash Before Cover). Not tracked as ARAP or Fee — settled via `Balance` and `OrderStatus` fields.

---

### BcpReverse — Reversal Record

Records reversal operations for Collection, Payment, and Offset transactions.

| Field | Description | Code Table |
|-------|-------------|---------|
| `ReverseId` | Unique identifier | — |
| `RefId` | Reference to the reversed entity | — |
| `ReverseType` | 1=Collection, 2=Payment, 3=Offset | BcpReverseType |
| `ReverseReason` | Reason for reversal | — |
| `ReverseBy` | User who performed the reversal | — |
| `ReverseTime` | When the reversal occurred | — |

---

## Currency & Exchange Rate

Every BCP financial record (BcpBizTransaction, BcpFee, BcpArap, BcpCollection, BcpPayment, BcpOffset, and their detail lines) carries **four currency codes** and a set of **direct exchange-rate (ER) fields** to support cross-currency billing, booking, and collection.

### Currency Codes

| Field | Description |
|-------|-------------|
| `CurrencyCode` | Transaction currency — the currency actually received or paid. On a Collection this is the collected currency; on an Offset this is the currency used in the current operation |
| `BizCurrencyCode` | Business currency — the policy/product currency (header-level only: BcpCollection, BcpSuspense) |
| `BookingCurrencyCode` | Booking (ledger) currency — derived from branch via `CurrencySdkClient.currencySettingApi().getBookingCurrencyCode(branch)` |
| `LocalCurrencyCode` | Local (base) currency — derived from branch via `CurrencySdkClient.currencySettingApi().getLocalCurrencyCode(branch)` |

### Direct Exchange-Rate Fields

Each `Direct*Er` field converts **from `CurrencyCode`** to a target currency:

| Field | Converts `CurrencyCode` → | Present on |
|-------|---------------------------|------------|
| `DirectEr` | `LocalCurrencyCode` (local) | All financial objects |
| `DirectBookingEr` | `BookingCurrencyCode` (booking) | All financial objects |
| `DirectBusiEr` | `BizCurrencyCode` (business) | `BcpCollection` header & `BcpSuspense` only |
| `OffsetDirectEr` | `OffsetCurrencyCode` (AR/AP original currency) | `BcpOffset` & `BcpOffsetDetail` only |

<div class="docs-caution"><span class="docs-admonitions-text">caution</span>

`DirectBusiEr` ("Busi") is a **different field** from `DirectBizEr` ("Biz") — do not conflate them.

</div>

### Direction Convention

<div class="docs-note"><span class="docs-admonitions-text">note</span>

**Target-currency amount = `CurrencyCode` amount × `Direct*Er`**

Each rate is expressed as *"target-currency units per 1 unit of `CurrencyCode`"*. When `CurrencyCode` equals the target currency, the rate is `1` (no conversion).

</div>

Rates are fetched at runtime from the Currency service (`CurrencySdkClient.currencyExchangeApi().getExchangeRate(currency=<CurrencyCode>)`, returning `sourceCurrencyCode` plus a `Map<targetCurrency, rate>`). Daily rates are maintained under **Global Configuration > Exchange Rate**.

### Examples

| Scenario | `CurrencyCode` | Target currency | Field | Value |
|----------|----------------|-----------------|-------|-------|
| Same currency (USD books to USD) | USD | Local USD | `DirectEr` | 1 |
| Collect USD, books in CNY | USD | Local CNY | `DirectEr` | 7.1 |
| Collect CNY, business USD | CNY | Business USD | `DirectBusiEr` | ≈0.14 |

Worked example: `Amount = 100 USD`, `DirectEr = 7.1` → local amount = 100 × 7.1 = **710 CNY**.

### Offset: Additional Currency Dimension

`BcpOffset` and `BcpOffsetDetail` carry `OffsetCurrencyCode` and `OffsetDirectEr` in addition to the standard currency/ER fields, because the AR/AP being offset may have been recorded in a different currency than the current offset operation.

| Field | Description |
|-------|-------------|
| `OffsetCurrencyCode` | Original currency of the AR/AP being offset (the currency the receivable/payable was originally recorded in) |
| `CurrencyCode` | Currency used in THIS offset operation |
| `OffsetDirectEr` | Exchange rate from `CurrencyCode` (this operation) to `OffsetCurrencyCode` (AR/AP original) |

Per the direction convention (source = `CurrencyCode`):

`OffsetCurrencyCode (original) amount = CurrencyCode (this operation) amount × OffsetDirectEr`

Example: `OffsetCurrencyCode` = CNY (AR/AP original), `CurrencyCode` = USD (this offset operation) → `OffsetDirectEr` = 7.1 (1 USD = 7.1 CNY; a 100 USD operation = 710 CNY in the AR/AP's original currency).

---

## GL Accounting Entities

### GLIntegrationVoucherDetail — GL Voucher Detail

Accounting representation of BCP Fee events. Created from BcpFee via **GLIntegrationMappingRule** double-entry transformation.

| Field | Description |
|-------|-------------|
| `VoucherDetailId` | Unique identifier (PK) |
| `VoucherHeaderKey` | Link to Voucher Header |
| `BcpFeeId` | Source BCP Fee |
| `BizTransType` | Inherited from BizTransaction (COLLECTION, PAYMENT, etc.) |
| `DuePostDate` | Scheduled date for accounting entry |
| `VoucherProcessStatus` | Status of posting to GL |
| `GlPostBatchId` | ID of the batch job for posting (→ `GLIntegrationPostBatch`) |
| `IsGlProcessFailed` | Y/N flag for integration errors |
| `GlProcessErrorMsg` | Error message from GL system |

**Dynamic segments** (defined in Data Dictionary `BCP_GL_INT_DD`):
- Custom fields: `GL_Account_Code`, `Department_ID`, `BranchCode`, etc.
- Filled by Groovy expressions from GLMappingRule

**API:** `queryGLByConditionEx`

**Physical Tables:**

| Table | Description |
|-------|-------------|
| t_bcp_gl_int_voucher_dtl | Current voucher records (before GL posting) |
| t_bcp_gl_int_voucher_dtl_his | Archived voucher records (after `queryVoucherDetailsOrderByIdForPostGL` completed) |

Both tables share the same DD model (GLIntegrationVoucherDetail). The distinction is whether `queryVoucherDetailsOrderByIdForPostGL` has been called — once called and completed, records are moved to the `_his` table.

---

### GLIntegrationMappingRule — GL Mapping Rule Configuration

DataTable configuration (dataTableName: `GLIntegrationMappingRule`) that defines how **BcpFee** transforms into **GL Voucher Details** (single-entry BCP Fee → double-entry GL).

**Condition Types:**
- **FeeType Mapping** — Match specific BCP fee type (e.g., `100501` for Direct Premium)
- **FilterExpression** — Groovy script for complex logic

**FilterExpression Context Objects:**
| Object | DSL Variable | Example |
|--------|--------------|---------|
| BCP Fee | `Fee` | `${Fee.BranchCode}` |
| BizTransaction | `transaction` | `${transaction.BizSource}` |
| Collection | `collection` | `${collection.AccountType}` |
| Payment | `payment` | `${payment.BankAccountNo}` |
| Offset | `offset` | — |
| Reverse | `reverse` | — |

---

### GLIntegrationPostBatch — GL Post Batch

Groups vouchers for a specific accounting period before transmitting to the external GL system.

| Field | Description |
|-------|-------------|
| `BatchId` | Unique identifier |
| `DueDate` | Accounting period due date |
| `Status` | Batch status |

**Posting Workflow:**
1. `createGLPostBatchRecord` — Create batch with due date
2. Transmit to external GL system
3. `completePostBatch` — Mark vouchers as "Completed"

---

## Data Flows

### Billing Flow
```
BizTransaction -> BcpFee
                   |
                   +-> (EnterBcpArap) -> BcpArap
                   +-> (SendToGL)     -> GLIntegrationVoucherDetail
                                           via GLMappingRule
```

### Collection Flow (Money In)
```
BcpCollection -> BcpCollectionDetail -> BcpSuspense
                                         |
                                         v
                               BcpOffset (RefType=2 -> Suspense)
                                         |
                                         v
                               BcpOffset (RefType=3 -> ARAP)
                                         |
                                         v
                                  BcpArap.Balance = 0
```

### Payment Flow (Money Out) — No Offset Involved
```
BcpPayment (Status=2, Approved)
        |
        v singlePayment API
BcpPayment (Status=3, Paid)
        |
        +-> BcpPaymentDetail (RefType=3) -> BcpArap
        |                                    |
        |                               ArapStatus = 2 (Settled)
        |                               Arap.Balance = 0
        |
        +-> SalesCommission.CommissionStatus = 2
```

### Receipt Refund Flow (Suspense -> Payment)
```
BcpSuspense (balance > 0)
        |
        v receiptRefund API
BcpPayment (Status=2)
        |
        +-> BcpPaymentDetail (RefType=2) -> BcpSuspense
                                              |
                                         Suspense.Balance = 0
```

### GL Accounting Flow
```
BcpFee + GLIntegrationMappingRule
         |
         +-> GLIntegrationVoucherDetail (row 1: Debit)
         +-> GLIntegrationVoucherDetail (row 2: Credit)
                   |
                   v
         GLIntegrationPostBatch -> External GL System
```

---

## RefType Reference

> **RefType values are context-dependent** — the same RefType number means different things in different entities.

| Entity | RefType=1 | RefType=2 | RefType=3 | RefType=4 | RefType=5 |
|--------|-----------|-----------|-----------|-----------|-----------|
| `BcpOffsetDetail` | FEE (Commission) | Suspense | ARAP | — | — |
| `BcpCollectionDetail` | — | Suspense | — | — | — |
| `BcpPaymentDetail` | — | Suspense (receipt refund) | ARAP (standard payment) | — | — |
| `BcpSuspenseDetail` | — | — | Collection | Premium Offset | Reverse |

---

## Entity ID Signing

Some APIs accept signed entity IDs (`numeric,hash` format). Use `sign.py` to generate signed values before calling those APIs.

---



## DB Table Reference

**Billing Related:**

| Table | Description |
|-------|-------------|
| t_bcp_biz_transaction | Root level. It stores main business transaction information. |
| t_bcp_prepay_order | isPrepay=="Y", if prepayment data will be in neither ARAP nor fee. |
| t_bcp_fee | feeList, detailed list of fee split. It can be the preparatory stage for GL generation. |
| t_bcp_arap | feeList, detailed list of AR and AP for the purpose of BCP transaction. |
| t_bcp_commission | Deprecated |

**Transaction Related:**

| Table | Description |
|-------|-------------|
| t_bcp_collection | Collection information |
| t_bcp_collection_detail | Collection transaction relationship. Now only suspense relation (2) is supported. |
| t_bcp_suspense | Suspense account information |
| t_bcp_suspense_detail | Suspense account movement and transaction relationship. Now collection (3), premium offset (4) and reverse (5) are supported. |
| t_bcp_offset | Offset information between AR and AP |
| t_bcp_offset_detail | Offset transaction relationship. Now suspense (2) and AR (3) are supported. |
| t_bcp_payment | Payment information |
| t_bcp_payment_detail | Payment transaction relationship. Now suspense (2) and AP (3) are supported. |
| t_bcp_reverse | Reversal information |
| t_bcp_reverse_detail | Detailed reversal information |

**GL Integration Related:**

| Table | Description |
|-------|-------------|
| t_bcp_gl_int_voucher_dtl | Current GL voucher detail records (before GL posting, `queryVoucherDetailsOrderByIdForPostGL` not yet called) |
| t_bcp_gl_int_voucher_dtl_his | Archived GL voucher detail records (after GL posting completed) |

