# FUW02301SFLogic

## Purpose

`FUW02301SFLogic` is the central business logic controller for the **email address change application** workflow within the eoWEB web portal. It orchestrates the full user journey — from initial display of the email address change screen, through confirmation and submission, to the final completion notice. This class acts as a screen-level orchestrator that coordinates data retrieval from shared form beans, invokes multiple backend services, handles error routing based on the current step in the workflow, and sets up navigation targets for the next screen.

## Design

This class follows the **screen controller** (sometimes called a page logic) pattern common in Japanese enterprise web frameworks (specifically the JPC/epoc platform). It extends `JCCWebBusinessLogic`, which provides the base `invokeService()` method for calling backend web services and utilities for accessing form data beans.

The class implements a **three-phase wizard flow**:

1. **Initial display** (`init`) — Fetches contract data, calls the email address change display service (`FUSV0064`), validates the change lock period, and prepares all display fields.
2. **Confirmation** (`cfm`) — The user presses the confirmation button; the class invokes the submission display service (`FUSV0065`) with a suite of service choreography (SC) and data mapping (CC) steps to prepare and show the confirmation screen.
3. **Submission** (`mskm`) — The user presses the submit button; the class executes the full backend workflow including contract changes, email alias provisioning, error routing configuration, completion emails, and customer record refresh.

Each method is responsible for a single user interaction (page load, confirm button, submit button, back button). The class is **not thread-safe** — it relies on instance state inherited from `JCCWebBusinessLogic` and per-request form beans, consistent with typical Java EE screen controllers that are instantiated per request.

## Key Methods

### `init()` → boolean

The entry point invoked when the email address change screen first loads.

**What it does:**

1. Performs a generic device check via `JFUWebCommon.checkOrosi(this)`.
2. Extracts customer contract data from the shared form bean (`commoninfoBean`) — SSO info, service contract info, optional service contract info, and sub-optional service contract info — by calling `getCommoninfoBeanInfo()`.
3. Creates a `FUSV0064_FUSV0064OPDBMapper` and populates parameter data by calling a series of SC (Service Choreography) methods:
   - `setFUSV006401SC` — Contract change lock period days
   - `setFUSV006402SC` — Pre-change email address send/receive availability period
   - `setFUSV006403SC` — POP server name
   - `setFUSV006404SC` — SMTP server name
   - `setFUSV006405SC` — NG word list (front)
   - `setFUSV006401CC` — Initial fee display
   - `setFUSV006402CC` — Email address change count retrieval
4. Invokes the backend service via `invokeService(paramMap, dataMap, outputMap)`. Catches `JCCWebServiceException` and re-throws as a system error (`JCCBusinessException` with `ERROR_CODE_0002`).
5. Retrieves results back from the `outputMap` into the service form bean using corresponding `getFUSVxxxxSC()` / `getFUSVxxxxCC()` mapper methods.
6. Sets pricing info area via `JFUWebCommon.setPrcInfoArea()` for the initial fee display section.
7. **Validates the email address change lock period**: If the change count (`mladChgCnt`) is ≥ 3, it checks whether `mladChgeYmd + keiChgStpPrd` (change forbidden period days) has passed the current system date. If the window hasn't expired, it throws a contract status error (`ERROR_CODE_0102`).
8. Sets auxiliary display fields: free flag, mansion division, current POP ID, current email address, sub-domain, and contract agreement checkbox.
9. Sets navigation to the confirmation screen (`FUW02302`).

**Return value:** Always returns `true` (indicating successful processing; navigation is set on the bean).

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

---

### `cfm()` → boolean

Handles the **confirmation button** press. Displays a confirmation screen showing what will happen when the user submits the email address change.

**What it does:**

1. Fetches shared form bean and service form bean data.
2. Calls `getDispInfo()` to set up all display fields (email alias, mailbox capacity, virus check status, etc.).
3. Creates a `FUSV0065_FUSV0065OPDBMapper` and invokes the submission display service. This involves a significantly larger set of SC and CC steps than `init()`:

   **Service Choreography (SC) steps — 15 total:**
   | # | Method | Purpose |
   |---|--------|---------|
   | 1 | `setFUSV006501SC` | Accept registration of submission content |
   | 2 | `setFUSV006502SC` | Sub-optional service contract cancellation |
   | 3 | `setFUSV006503SC` | Optional service contract (ISP) information change |
   | 4–6 | `setFUSV006504SC`–`06SC` | End of use for engineering targets (3 calls) |
   | 7–8 | `setFUSV006507SC`–`08SC` | Register engineering targets (2 calls) |
   | 9 | `setFUSV006509SC` | Progress registration |
   | 10 | `setFUSV006510SC` | Submission detail inquiry / follow-up business request |
   | 11 | `setFUSV006511SC` | Sub-optional service contract cancellation confirmation |
   | 12 | `setFUSV006512SC` | Sub-optional service contract cancel |
   | 13–14 | `setFUSV006513SC`–`14SC` | Optional service contract agreement (2 calls) |
   | 15 | `setFUSV006515SC` | Instruction document registration |

   **Content Check (CC) steps — 9 total:**
   | # | Method | Purpose |
   |---|--------|---------|
   | 1 | `setFUSV006501CC` | Optional service contract (ISP) agreement |
   | 2 | `setFUSV006502CC` | Service IF result data transfer |
   | 3–5 | `setFUSV006503CC`–`05CC` | Engineering list display (3 calls) |
   | 6 | `setFUSV006506CC` | Email address SOD matching |
   | 7 | `setFUSV006507CC` | Service order issuance |
   | 8 | `setFUSV006508CC` | Non-charges judgment |
   | 9 | `setFUSV006509CC` | Opt-in email reception setting registration |

4. If a sub-optional service contract exists, sets its status to preparation (`Rsv1`).
5. Invokes the backend service. Catches `JCCWebServiceException` and routes through `checkException(se, "cfm")` for specialized error handling.
6. Sets navigation to the submission confirmation screen (`FUW02303`).

**Return value:** Always returns `true`.

---

### `mskm()` → boolean

Handles the **submit button** press. This is the most complex method, executing the full email address change workflow against the backend.

**What it does:**

1. Calls `JFUCommonRelationCheck.checkCommonRelation(this, USECASE_ID_FUSV0065)` for common related checks.
2. Fetches shared form bean and service form bean data via `getCommoninfoBeanInfo()` and `super.getServiceFormBean()`.
3. Determines the function code based on member type: if the member subtype code is `"11"` or `"21"`, uses `FUNC_CD_4` instead of the default `FUNC_CD_2`.
4. Captures the current email address (`chgBfMlad`) before the change for the completion email.
5. Calls `getDispInfo()` to set up display fields.
6. Invokes the same suite of 15 SC and 9 CC steps as `cfm()`, but **with different function codes** — most use `FUNC_CD_1` (the actual execution code) instead of `FUNC_CD_2` (the display-only code used in `cfm()`). The key difference: this is real execution, not display preparation.
7. Invokes the backend service. Catches `JCCWebServiceException` and routes through `checkException(se, "mskm")`.
8. **Updates membership information** via the `CKSV9001` service: calls `setCKSV900101CC` to register the membership info update request. Errors here are swallowed (logged and ignored) to allow the flow to continue.
9. **Refreshes the current customer record**: Updates the POP ID field in the SSO info bean with the new email address (with `@` replaced by `%`), then calls `JFUWebCommon.refreshGenCustKei(this)`.
10. Re-fetches contract info after refresh and sets the POP password and confirmed email address on the bean.
11. **Sends the completion email** via `JFUWebCommon.sendMskmFinMail(this, MSKM_FIN_MAIL_FUW023_1, chgBfMlad, null, ignoreOpSvcKeiNo)`.
12. Sets navigation to the completion screen (`FUW02303`).

**Return value:** Always returns `true`.

**Important:** This method performs irreversible business operations (contract changes, email alias creation). The user must have already passed through `cfm()` (confirmation) before reaching this method.

---

### `back()` → boolean

Simple navigation handler. Sets the next screen ID back to the initial screen (`FUW02301`). This is called when the user presses the "Back" button on the confirmation screen.

---

### `getDispInfo(X31SDataBeanAccess commoninfoBean, X31SDataBeanAccess bean) → X31SDataBeanAccess`

Internal method that populates the service form bean with all display fields needed for the confirmation and submission screens.

**What it does:**

1. Walks the shared form bean tree: `WEB change info → SSO info → Current customer info → Service contract info → Service contract details info → Optional service contract info`.
2. Iterates through the optional service contract array to find the one matching the user's POP ID with service code `"B001:E-Mail"`.
3. **Mailbox capacity calculation**: Reads the capacity in MB from the optional service contract. If ≥ 1024 MB, converts to GB (divided by 1024, rounded up to 1 decimal). Otherwise shows MB.
4. Iterates through the sub-optional service contract array to find:
   - **Mail alias**: Sub-optional code `"D01:Mail Alias"` matching the parent optional service contract number → stores the alias address.
   - **Mail virus check**: Sub-optional code `"D03:Mail Virus Check"` matching the parent → sets contract status flag.
5. Sets display strings based on user choices:
   - `ML_ALIAS` — Current email alias (or "Not available" / "No inheritance")
   - `MWKML_SETTE` — Inheritance setting text for confusion mail rejection settings
   - `OLD_MAIL_TENSO_SETTE` — Forwarding from old mail setting ("Forward" / "Don't forward")
   - `MLVIRUSCHK` — Virus check status ("Under contract" / "Not available")
   - `ML_PWD` — Email password display placeholder text

**Return value:** The populated `bean` instance.

---

### `getCommoninfoBeanInfo(X31SDataBeanAccess commoninfoBean) → Map<String, Object>`

Internal utility that extracts and validates contract-related data from the shared form bean into a structured map.

**What it does:**

1. Traverses the form bean tree to extract: SSO info, current customer info, service contract info, service contract details info, and billing contract info.
2. **Finds the matching optional service contract**: Iterates through the optional service contract array, filtering for contracts with status `"020:Under review"`, `"030:Completed"`, or `"100:Service provision"`, matching the user's POP ID, with service code `"B001:E-Mail"`. If no match is found, throws `ERROR_CODE_0102` (contract status error).
3. **Finds the matching sub-optional service contract**: Iterates through the sub-optional service contracts of the matched optional contract, skipping those with status `"910:Canceled"` or `"920:Cancel completed"`. For contracts matching the parent optional service contract number with code `"D01:Mail Alias"`:
   - Status `"020/030/100"` → Sets as the matched sub-optional contract.
   - Status `"010:Received"` → Skips.
   - Status `"210:Suspended`" or `"220:Stopping"` → Throws `ERROR_CODE_0102`.
4. Returns a map containing all extracted beans keyed by `CommonInfoCFConst` constants (SSO_INFO, SVC_KEI_INFO, OP_SVC_KEI_INFO, SBOP_SVC_KEI_INFO, etc.).

**Return value:** A `Map<String, Object>` containing all contract-related beans and identifiers.

**Error handling:** Throws `JCCBusinessException(ERROR_CODE_0002)` if the optional service contract number is null. Throws `ERROR_CODE_0102` if no valid contract is found or if the sub-optional contract is in a suspended state.

---

### `checkSvcData(Map<String, Object> resultMap) → void`

Simple validation that the optional service contract data exists. If `OP_SVC_KEI_INFO` is null in the result map, throws a contract status error (`ERROR_CODE_0102`). Called during `init()` after extracting the common info bean data.

---

### `checkException(JCCWebServiceException se, String callMethod) → void`

Sophisticated error routing that classifies backend exceptions and decides which error to surface to the user based on the current call context (`"cfm"` vs `"mskm"`).

**Error classification logic:**

1. Extracts error details from the exception's message list — status, template ID, item ID, and error flag.
2. **When status is `RELATION_ERR`**:
   - If `callMethod` is `"cfm"` (confirmation) and the error relates to the expected email account or the current email address with error flag `"EB"` (business error) → re-throws the original exception, allowing the frontend to display specific error messages.
   - If `callMethod` is `"mskm"` (submission) with the same conditions → throws a contract status error (`ERROR_CODE_0102`), treating it as a fatal state issue since the submission has already been confirmed.
   - If the error relates to the change lock period (`KIBO_ML_ACCOUNT_1`) with error flag `"EA"` (system error) → throws `ERROR_CODE_0102`.
   - If the error relates to the update timestamp (`UPD_DTM_BF`) → throws `ERROR_CODE_0204` (update impossibility error), indicating a concurrency conflict.
3. **Fallback**: Any unrecognized error is treated as a system error (`ERROR_CODE_0002`).

This method is critical for providing appropriate error messages — the confirmation screen gets business-level errors (user-correctable), while the submission screen gets system-level errors (potentially indicating state corruption).

---

### `getMansionDiv(X31SDataBeanAccess commoninfoBean, HashMap<String, Object> outputMap) → boolean`

Determines whether the customer's contract is an **eo光 Net Mansion Type** (eo Hikari Net apartment building type). Returns `true` if all of the following are true:
- Price group code is `"04"` (eo Hikari Net Mansion Type)
- Enrollment contract payment method code is `"003"` (All-account unified)
- Initial fees exist (delegates to `chkPayInitialCost()`)

This flag is used on the initial screen to determine display formatting.

---

### `setFreeFlg(X31SDataBeanAccess bean, HashMap<String, Object> outputMap) → boolean`

Sets the free flag for the service. Returns `false` (paid) if initial fees exist, `true` (free) otherwise. Delegates to `chkPayInitialCost()`.

---

### `chkPayInitialCost(HashMap outputMap) → boolean`

Internal helper that checks whether initial fees are present in the output map. Looks for the key `FUSV006401CC` (initial fee display CC section), then for the child list `EKK0721A010CBSMsg1List`. If the list is non-empty, returns `true` (fees exist).

## Relationships

### Dependency Diagram

```mermaid
flowchart TD
    subgraph Dependents["Dependent Screens"]
        PJP02["WEBGAMEN_FUW023010PJP
(FUW02301 Screen)"]
        PJP02b["WEBGAMEN_FUW023020PJP
(FUW02302 Screen)"]
        PJP03["WEBGAMEN_FUW023030PJP
(FUW02303 Screen)"]
        BL["x31business_logic_FUW02301SF
(Business Logic Wrapper)"]
    end

    subgraph FUW02301SFLogic["FUW02301SFLogic"]
        INIT["init() - Screen Initialization
- Fetch customer contract data
- Invoke FUSV0064 service
- Check email address change lock period
- Set display info"]
        CFM["cfm() - Confirmation Button
- Validate and submit email change
- Invoke FUSV0065 service
- 15 SC steps, 9 CC steps"]
        MSKM["mskm() - Submit Button
- Execute full email change workflow
- Update membership info via CKSV9001
- Refresh customer data
- Send completion email"]
        BACK["back() - Back Button
- Navigate back to init screen"]
        HELP["Helper Methods
- getDispInfo: Set display fields
- getCommoninfoBeanInfo: Parse beans
- checkSvcData: Validate service data
- checkException: Error handling
- getMansionDiv: Mansion type check
- setFreeFlg: Free flag check
- chkPayInitialCost: Initial fee check"]
    end

    subgraph Parent["Parent Class"]
        JCC["JCCWebBusinessLogic
- invokeService() base method
- getCommonInfoBean / getServiceFormBean"]
    end

    PJP02 -->|calls| INIT
    PJP02b -->|receives| CFM
    PJP03 -->|receives| MSKM
    BL -->|delegates| INIT

    INIT --> CFM
    CFM --> MSKM
    MSKM --> PJP03

    JCC -->|extended by| FUW02301SFLogic
```

### Inbound Dependencies (4 classes)

| Class | Purpose |
|-------|---------|
| `WEBGAMEN_FUW023010PJP.xml` | Initial display screen — calls `init()` to load the email address change screen |
| `WEBGAMEN_FUW023020PJP.xml` | Confirmation screen — calls `cfm()` on confirm button press |
| `WEBGAMEN_FUW023030PJP.xml` | Completion screen — receives result of `mskm()` after submission |
| `x31business_logic_FUW02301SF.xml` | Business logic wrapper/registry — delegates to this class for screen processing |

### Outbound Dependencies

| Class | Relationship |
|-------|-------------|
| `JCCWebBusinessLogic` | Parent class — provides `invokeService()`, `getCommonInfoBean()`, `getServiceFormBean()` |
| `FUSV0064_FUSV0064OPDBMapper` | Created in `init()` — handles service choreography for display |
| `FUSV0065_FUSV0065OPDBMapper` | Created in `cfm()` and `mskm()` — handles service choreography for submission |

## Usage Example

The typical calling pattern follows a three-screen wizard:

```
User request → WEBGAMEN_FUW023010PJP.xml
  → FUW02301SFLogic.init()
    → Fetches contract info via getCommoninfoBeanInfo()
    → Invokes FUSV0064 service (display-only)
    → Validates change lock period (if >= 3 changes)
    → Sets display fields (alias, capacity, virus check, etc.)
    → Sets next screen = FUW02302
  → Renders initial screen with email change form

User presses "Confirm" → WEBGAMEN_FUW023020PJP.xml
  → FUW02301SFLogic.cfm()
    → Calls getDispInfo() to populate display fields
    → Invokes FUSV0065 service with FUNC_CD_2 (display mode)
    → 15 SC + 9 CC steps prepare confirmation data
    → Routes errors via checkException("cfm") — business errors pass through
    → Sets next screen = FUW02303
  → Renders confirmation screen

User presses "Submit" → FUW02301SFLogic.mskm()
    → Calls getCommonRelationCheck() for pre-checks
    → Captures old email for completion email
    → Invokes FUSV0065 service with FUNC_CD_1 (execution mode)
    → Calls CKSV9001 service for membership info update (errors tolerated)
    → Refreshes current customer record
    → Sets POP password and confirmed email on bean
    → Sends completion email via JFUWebCommon.sendMskmFinMail()
    → Routes errors via checkException("mskm") — system errors only
    → Sets next screen = FUW02303
  → Renders completion screen
```

## Notes for Developers

1. **Change lock period enforcement**: If the user has changed their email address 3 or more times, they must wait for the change forbidden period (`keiChgStpPrd` days) to elapse after the last change (`mladChgeYmd`). This check occurs in both `init()` (display) and is enforced at the service layer during submission.

2. **Error handling divergence**: The `checkException()` method treats the same backend error differently depending on context. During confirmation (`cfm`), business errors are surfaced to the user for correction. During submission (`mskm`), the same errors become system-level errors because the user has already confirmed. This is a deliberate design choice — the submission phase should not present user-correctable errors if they were visible during confirmation.

3. **CKSV9001 membership update**: After the email change submission, the class calls the membership info update service (`CKSV9001`). Errors here are **intentionally swallowed** — logged but not propagated. This suggests the membership update is non-critical to the core flow, or will be handled asynchronously.

4. **Thread safety**: This class is **not thread-safe**. It inherits per-request state from `JCCWebBusinessLogic` and uses instance-level data bean access. It should be instantiated per HTTP request.

5. **Form bean tree depth**: The shared form bean has a deeply nested structure: `WEB change info → SSO info / Current customer info → Service contract info → Service contract details info → Optional service contract info → Sub-optional service contract info`. Traversal errors (e.g., null pointer on a missing intermediate node) would cause unexpected failures. The `getCommoninfoBeanInfo()` method does have some null checks (e.g., for `opInfoOpSvcKeiNo`), but not all paths are guarded.

6. **Capacity unit conversion**: Mailbox capacity is returned from the backend in MB. The class converts to GB for capacities ≥ 1024 MB, using `setScale(1, BigDecimal.ROUND_UP)` (always rounds up to 1 decimal place). This means a 1536 MB mailbox becomes "1.5 GB", and a 2048 MB mailbox becomes "2.0 GB".

7. **Service invocation is transactional**: The `invokeService()` call bundles multiple SC and CC steps into a single web service invocation. If the service call fails with `JCCWebServiceException`, none of the SC/CC steps are persisted. This is unlike the `mskm()` method where the CKSV9001 call is separate and its errors are tolerated.

8. **Email address normalization**: After submission, the new email address stored in the customer record replaces `@` with `%`. This is likely a database encoding convention. Developers should be aware when reading or displaying email addresses from this record.

9. **Magic constants**: The class contains many Japanese string literals (e.g., `契約中`, `ご利用なし`, `引き継がない`) that serve as display text values. These are defined as `private static final String` constants but are not localized — they are hardcoded Japanese strings.

10. **Mapper instances**: New mapper instances (`FUSV0064_FUSV0064OPDBMapper`, `FUSV0065_FUSV0065OPDBMapper`) are created fresh in each method call. They are not cached or shared, which is consistent with the per-request lifecycle.
