# FUW01901SFLogic

## Purpose

`FUW01901SFLogic` is the central business-logic controller for a multi-step, web-based optional service subscription flow in a Japanese ISP/broadband provider system. It orchestrates the entire lifecycle of a customer requesting, confirming, and finalizing the addition of email addresses (and associated POP/IMAP credentials) as optional paid services. This class is the orchestrator that ties together data gathering, service invocations, pricing determination, error handling, and screen navigation.

## Design

This class follows the **WebActionLogic** pattern common in the X31/JP web framework. It extends `JCCWebBusinessLogic`, which provides base methods for accessing shared form beans, invoking remote web services, and managing cross-cutting concerns. `FUW01901SFLogic` acts as a **service-orchestrating controller** for a three-screen wizard:

1. **Screen FUW01901** — Display screen: loads pricing, service lists, and optional email-addition limits. The customer reviews and clicks "Confirm" (`cfm`).
2. **Screen FUW01902** — Confirmation screen: maps all input data to service calls, invokes the backend to register the subscription, and catches service errors for user-friendly handling. The customer clicks "Submit" (`mskm`).
3. **Screen FUW01903** — Completion screen: after final submission, sends a confirmation email and displays the success page. The customer can click "Back" (`back`).

The class is stateless per request — it reads from `X31SDataBeanAccess` beans, invokes services through `invokeService()`, and writes results back to beans and `HashMap` output maps.

## Key Methods

### `init() → boolean` [Lines 125–269]

**Purpose:** Initializes the display screen (FUW01901). This is the entry point when the customer first opens the optional service selection page.

**What it does:**

1. Performs a device-check (`checkOrosi`) to validate the requesting device.
2. Retrieves shared form bean data via `getCommoninfoBeanInfo()`, which extracts the SSO info, service contract info, and optional service info.
3. Creates a `FUSV0053_FUSV0053OPDBMapper` and populates the **up-mapping** (input) data by calling a chain of mapper methods:
   - `setFUSV005301SC` — Discounted optional service contract list (service contract specification).
   - `setFUSV005302SC` — Service cost / optional service consent list.
   - `setFUSV005303SC` — Fixed-price plan (optional submission cost) list.
   - `setFUSV005301CC` — Initial fee list component control.
   - `setFUSV005304SC` through `setFUSV005308SC` — NG word list, business parameter management, sub-option service list, security pack submission display control.
4. Invokes the backend service (`invokeService` with `USECASE_ID_FUSV0053`) wrapped in a try-catch.
5. Performs **down-mapping** (output retrieval): calls `getFUSV0053xxSC` / `getFUSV0053xxCC` methods to extract results from the service response into the bean.
6. Sets email address counts on the bean:
   - `MLAD_STKU_CNT` — currently acquired email count.
   - `MLAD_ADD_PSB_CNT` — max additional emails possible (max - current).
   - `MLAD_MRYO_CNT` — free email allowance remaining (free count - current).
7. Calls `chkMaxMladCnt()` to verify the customer hasn't hit the email addition limit; throws `ERROR_CODE_0102` if so.
8. Calls `setPayFlg()` to determine whether this is a paid service, and stores the result as `PAY_FLG`.
9. Calls `getMansionDiv()` to determine if this is a mansion-type contract with payments, storing as `MANSION_DIV`.
10. If certain conditions hold (free count > 0 AND mansion type, OR pack contract, OR not paid), sets the free price info map.
11. Calls `setPrcInfoArea()` to build the pricing display area, and `setPrcDspNowDay()` to show today's date.
12. Sets the next-screen ID to `FUW01902` on the commoninfo bean.

**Return:** Always `true`.

**Side effects:** Populates the service form bean and common info bean with display data.

---

### `cfm() → boolean` [Lines 277–396]

**Purpose:** Handles the **Confirm button** click on the display screen (FUW01901). Prepares and invokes the backend service registration for the optional service subscription.

**What it does:**

1. Retrieves shared form bean data and extracts SSO SYSID, plus all bean info (optional service contract, billing contract, SSO info, service contract info).
2. Sets email count info on the bean (same logic as `init()`).
3. Creates a `FUSV0054_FUSV0054OPDBMapper` and populates a large set of **up-mapping** calls (approximately 19 mapper methods):
   - `setFUSV005401SC` — Submission content approval registration.
   - `setFUSV005402SC` — Optional service contract <ISP> registration.
   - `setFUSV005409SC` — Optional service contract <ISP> inquiry.
   - `setFUSV005403SC` — Optional service contract <ISP> inquiry result binding.
   - `setFUSV005404SC` — Optional service contract <ISP> start.
   - `setFUSV005405SC` / `setFUSV005406SC` — Aging target registration.
   - `setFUSV005407SC` — Progress registration.
   - `setFUSV005408SC` — Submission detail inquiry / follow-up work request.
   - `setFUSV005410SC` / `setFUSV005411SC` — Optional service contract consent inquiry.
   - `setFUSV005412SC` — Instruction book registration.
   - Various `CC` (component control) methods for data transfer, audit issuance conditions, optional email reception setup registration, email addition submission (aging check), initial POPID password acquisition, and family pack guidance necessity determination.
4. Sets the service contract status via `JFUWebCommon.setSvcKeiStat(this, dataMap)`.
5. Invokes the backend service (`invokeService` with `USECASE_ID_FUSV0054`) wrapped in a try-catch that calls `chkRelationServiceError()` instead of re-throwing.
6. Checks the email addition aging result (`getFUSV005405CC`). If the result equals `"NG"`, sets a user-facing error message ("This email address cannot be used. Please use a different email address") and returns early.
7. Refreshes the current customer info via `JFUWebCommon.refreshGenCustKei(this)`.
8. Sets the next-screen ID to `FUW01903` (the completion screen).

**Return:** Always `true`.

**Side effects:** Invokes backend service for optional service subscription; may throw `JCCBusinessException` for specific error conditions (email address validity, etc.).

---

### `mskm() → boolean` [Lines 404–555]

**Purpose:** Handles the **Submit button** click on the confirmation screen (FUW01902). This is the critical method that finalizes the optional service subscription and sends the completion email.

**What it does:**

1. Performs a common relation check (`checkCommonRelation`) to prevent concurrent modifications.
2. Retrieves shared form bean data and all bean info (same pattern as `cfm()`).
3. Creates a `FUSV0054_FUSV0054OPDBMapper` and populates up-mapping data (similar to `cfm()` but with different function codes — `FUNC_CD_1` instead of `FUNC_CD_2`, indicating a different service operation mode for final submission).
4. Sets the service contract status.
5. Invokes the backend service (`invokeService` with `USECASE_ID_FUSV0054`) with error handling through `chkRelationServiceError()`.
6. Invokes a **second service** (`USECASE_ID_CKSV9001`) for member information update. This call is wrapped in a broader `try-catch (Throwable)` — errors here are **ignored and logged** at debug level; processing continues regardless. This appears to be a non-critical post-processing step.
7. Performs down-mapping to extract results from both service calls.
8. Refreshes current customer info.
9. Retrieves the fixed email address (`FIX_MLAD`) from the bean.
10. Re-fetches commoninfo bean data with `fixMlad` to get the confirmed POP password (`POP_PWD`) and stores it on the bean.
11. Retrieves the ignore optional service contract number.
12. **Sends the completion confirmation email** via `JFUWebCommon.sendMskmFinMail(this, MSKM_COMP_MAIL_ID, null, null, ignoreOpSvcKeiNo)`.
13. Sets the next-screen ID to `FUW01903` (the completion screen).

**Return:** Always `true`.

**Side effects:** Finalizes the subscription, updates member info, stores POP password, sends confirmation email, navigates to completion screen.

---

### `back() → boolean` [Lines 563–573]

**Purpose:** Handles the **Back button** click on the confirmation screen (FUW01902). Simply navigates the user back to the display screen (FUW01901).

**What it does:** Sets the next-screen ID and name to `FUW01901` / `SCREEN_NAME_FUW01901` on the commoninfo bean.

**Return:** Always `true`.

---

### `getCommoninfoBeanInfo(X31SDataBeanAccess commoninfoBean, String fixMlad) → Map<String, Object>` [Lines 601–700]

**Purpose:** Extracts and organizes customer information from the hierarchical shared form bean structure. This is a critical helper method called by `init()`, `cfm()`, and `mskm()`.

**What it does:**

1. Navigates the bean hierarchy: `commoninfoBean` → `WEB_CHG_INFO` → `GEN_CUST_KEI_INFO` → `SVC_KEI_INFO` → `SVC_KEI_UCWK_INFO` → `OP_SVC_KEI_INFO`.
2. Iterates through all optional service contract records (`opSvcKeiInfoArray`), looking for email-address-related services (identified by `opSvcCd == "Eメール"` and `pcrsCd == "Basic"`).
3. Counts the number of active (non-terminated/cancelled) email service contracts to compute `mladCnt`.
4. If `fixMlad` is provided (indicating a recently-added email), retrieves the associated initial POP password (`fixPopidPwd`).
5. Extracts billing contract info and SSO SYSID.
6. Returns a `Map<String, Object>` containing: `SYSID_21`, `POP_PWD`, `MLAD_STKU_CNT`, `SSO_INFO`, `SVC_KEI_INFO`, `SVC_KEI_UCWK_INFO`, `OP_SVC_KEI_INFO`, `SEIKY_KEI_INFO`.

**Return:** A map of extracted customer information.

---

### `setPayFlg(X31SDataBeanAccess bean, HashMap<String, Object> outputMap) → boolean` [Lines 787–825]

**Purpose:** Determines whether the optional service subscription is **paid** (`true`) or **free** (`false`).

**What it does:**

1. Checks if there is a fixed unit price (`chkPayKoteiTanka()`).
2. If fixed price exists:
   - No free count → **paid**.
   - Free count exists AND pack contract → **paid** (family plan overrides).
   - Free count exists AND no pack contract → **free**.
3. If no fixed price → **free**.

**Return:** `true` if paid, `false` if free.

---

### `chkPayKoteiTanka(HashMap outputMap) → boolean` [Lines 833–873]

**Purpose:** Checks whether the service has a **fixed unit price** greater than zero. Used by `setPayFlg()`.

**What it does:** Extracts the `FUSV005303SC` pricing data from the output map, iterates the `EKK0601B001CBSMsg1List`, and checks if any entry's `pplan_kotei_amnt` (fixed plan amount) is greater than 0.

**Return:** `true` if a fixed price exists and is > 0, `false` otherwise.

---

### `chkPayInitialCost(HashMap outputMap) → boolean` [Lines 881–908]

**Purpose:** Checks whether the service has any **initial fees** (setup costs). Used by `getMansionDiv()` to determine mansion-type pricing.

**What it does:** Extracts the `FUSV005301CC` initial fee data from the output map and checks if `EKK0721A010CBSMsg1List` contains any entries.

**Return:** `true` if initial fee entries exist, `false` otherwise.

---

### `getMansionDiv(X31SDataBeanAccess bean, X31SDataBeanAccess commoninfoBean, HashMap<String, Object> outputMap) → boolean` [Lines 710–734]

**Purpose:** Determines whether the customer is a **mansion-type contract with payments** (`true`) or not. This flag controls UI behavior and pricing display.

**What it does:** Checks if `prcGrpCd == "04"` (mansion type), payment method code indicates payments exist, AND either `payFlg` is true OR there are initial costs.

**Return:** `true` if mansion-type with payments, `false` otherwise.

---

### `chkMaxMladCnt(X31SDataBeanAccess bean) → boolean` [Lines 769–778]

**Purpose:** Checks if the customer has reached the **maximum email address limit**.

**What it does:** Compares `MLAD_STKU_CNT` (current count) against `MLAD_MAX_CNT` (max allowed). Returns `true` if current >= max.

**Return:** `true` if limit reached (addition not allowed).

---

### `chkRelationServiceError(JCCWebServiceException se) → void` [Lines 915–996]

**Purpose:** Interprets service-layer exceptions and maps them to specific business-level error codes. This is a **critical error-dispatch method** that translates low-level service errors into user-facing business errors.

**What it does:** Extracts error metadata (template ID, item ID, status, error flag) from the exception's `MessageMoreInfo` list and checks against known error signatures:

| Service Template | Error Flag | Item | Business Result |
|---|---|---|---|
| `EKK0361D010` | `RELATION_CHECK_ERR_ED` | `op_svc_cd` | Contract error (ERROR_CODE_0102) |
| `EKK0361D010` | `RELATION_CHECK_ERR_EB` | `kibo_ml_account_1` | Re-throw original exception (invalid email) |
| `EKK0361D010` | `RELATION_CHECK_ERR_EB` | `mskm_dtl_no` | Usage limit error (ERROR_CODE_0101) |
| `EKK0361D010` | `RELATION_CHECK_ERR_EA` | `upd_dtm_bf` | Update-not-allowed error (ERROR_CODE_0204) |
| `EKK0361C030` | `RELATION_CHECK_ERR_ED` | `op_svc_kei_no` | Usage limit error (ERROR_CODE_0101) |
| `EKK0361C010` | `RELATION_CHECK_ERR_EE` | `op_svc_kei_no` | Usage limit error (ERROR_CODE_0101) |
| `EZM0111D010` | `RELATION_CHECK_ERR_EA` | `aging_tg_value` | Usage limit error (ERROR_CODE_0101) |

Any unmatched error falls through to a generic system error (ERROR_CODE_0002).

**Return:** Never (throws exceptions).

---

### `getX31SDataBeanAccess(X31SDataBeanAccess bean, String componentID) → X31SDataBeanAccess` [Lines 582–592]

**Purpose:** Safely retrieves or creates the first child DataBean under a given component ID. If no child exists, it creates one first.

**Return:** The first (index 0) child `X31SDataBeanAccess`.

---

### `getPrcGrpCd(X31SDataBeanAccess commoninfoBean) → String` [Lines 742–761]

**Purpose:** Extracts the **price group code** from the customer's service contract info.

**Return:** Price group code string (e.g., `"04"` for mansion type).

## Relationships

```mermaid
flowchart TD
    A["WEBGAMEN_FUW019010PJP"] --> B["FUW01901SFLogic"]
    C["WEBGAMEN_FUW019020PJP"] --> B
    D["WEBGAMEN_FUW019030PJP"] --> B
    E["x31business_logic_FUW01901SF"] --> B

    B --> F["JCCWebBusinessLogic"]
    B --> G["FUSV0053_FUSV0053OPDBMapper"]
    B --> H["FUSV0054_FUSV0054OPDBMapper"]
    B --> I["JFUWebCommon"]
    B --> J["JFUCommonRelationCheck"]
```

**Inbound (4 dependents):**

- **WEBGAMEN_FUW019010PJP.xml** — The presentation-layer controller for the display screen (FUW01901). Instantiates and calls `init()`.
- **WEBGAMEN_FUW019020PJP.xml** — The presentation-layer controller for the confirmation screen (FUW01902). Instantiates and calls `cfm()` and `back()`.
- **WEBGAMEN_FUW019030PJP.xml** — The presentation-layer controller for the completion screen (FUW01903). Instantiates and calls `mskm()`.
- **x31business_logic_FUW01901SF.xml** — The business logic wiring/definition file that registers this class in the framework's dependency injection or factory.

**Outbound dependencies:**

- **JCCWebBusinessLogic** — Base class providing `getCommonInfoBean()`, `getServiceFormBean()`, `invokeService()`, and `DEBUG_LOG`.
- **FUSV0053_FUSV0053OPDBMapper** — Used in `init()` for input/output mapping of the service display query.
- **FUSV0054_FUSV0054OPDBMapper** — Used in `cfm()` and `mskm()` for input/output mapping of the subscription registration service.
- **JFUWebCommon** / **JFUCommonRelationCheck** — Shared utilities for web operations, pricing display, mail sending, and concurrent request protection.

## Usage Example

The typical calling pattern flows through the presentation layer (JP XML controllers):

```
User visits FUW01901 page
  → WEBGAMEN_FUW019010PJP.xml instantiates FUW01901SFLogic
  → Calls init()
  → init() loads pricing, service lists, email limits into beans
  → Renders display screen to user

User clicks "Confirm" button
  → WEBGAMEN_FUW019020PJP.xml instantiates FUW01901SFLogic
  → Calls cfm()
  → cfm() maps input data, invokes FUSV0054 service, handles errors
  → Renders confirmation screen

User clicks "Submit" button
  → WEBGAMEN_FUW019030PJP.xml instantiates FUW01901SFLogic
  → Calls mskm()
  → mskm() finalizes subscription, updates member info, sends email
  → Renders completion screen

User clicks "Back" button
  → Calls back()
  → Returns to display screen
```

Each call creates a new `FUW01901SFLogic` instance, so the class must be **thread-safe per-request** (which it is — it has no instance fields, only local variables and static constants).

## Notes for Developers

- **Stateless design:** The class holds no instance fields (all fields are `static final` constants). Each method invocation is independent. This means the same instance can safely handle concurrent requests, though in practice the framework likely creates a new instance per request.

- **Three-screen wizard:** The class manages a strict sequential flow: `init()` → `cfm()` → `mskm()`. The `back()` method only goes from `cfm()` back to `init()`'s screen. There is no bypass mechanism.

- **Dual service invocations in `mskm()`:** The `mskm()` method calls the primary service (`FUSV0054`) and a secondary member-info-update service (`CKSV9001`). The secondary call is intentionally **fire-and-forget** — any error is logged and ignored. This means the subscription proceeds even if the member info update fails, which could lead to inconsistency if the member update is critical.

- **Error classification:** The `chkRelationServiceError()` method is the single point of service error interpretation. Adding new error conditions requires updating this method. The mapping uses template ID, status, item ID, and error flag as a composite key — errors that don't match any case fall through to a generic system error.

- **Email address limits:** The class enforces a hard limit on the number of email addresses per customer (`chkMaxMladCnt`). When the limit is reached during `init()`, it throws `ERROR_CODE_0102` (contract error). During `cfm()`/`mskm()`, invalid email addresses are caught by the service layer and re-thrown via `chkRelationServiceError()`.

- **Pricing logic complexity:** The `setPayFlg()` method implements tiered pricing rules that depend on fixed prices, free allowances, pack contracts, mansion types, and initial costs. The conditions are nested and somewhat hard to follow — consider extracting into a dedicated pricing engine if rules change frequently.

- **Japanese constants:** Many constant strings and comments are in Japanese. Key terminology:
  - `mlad` (メーアド) = mail address
  - `mskm` (申請) = application/submission
  - `cfm` (確認) = confirm
  - `muryo` (無料) = free
  - `payFlg` = paid flag
  - `pplan_kotei_amnt` = planned fixed amount

- **Concurrent modification protection:** `mskm()` calls `JFUCommonRelationCheck.checkCommonRelation()` to detect and prevent race conditions where the same contract is being modified simultaneously by multiple requests. The `init()` and `cfm()` methods do not use this protection — they rely on `checkOrosi()` for device validation only.
