# Edit vs. Read-Only — Variant Decisions

This document covers the two main detail-screen variants (read-only and editable) and the
key contract differences between them.

---

## The Deciding Question

> Does the user need to change data on this screen?

-   **No** → read-only variant: simpler; no save action; no ExitHandler
-   **Yes** → editable variant: TWO_WAY bindings; save action; ExitHandler validation

---

## 1. Read-Only Variant

### DS — no editableEntity or empty string

For a read-only screen the DS does not need `editableEntity` set to a real entity.
In practice, read-only loads still use a BO DS that has `editableEntity="<Entity>"` — the
attribute controls whether writes are allowed back to the backend, not what the UI shows.
The runtime enforces read-only via the UI's `disabled="true"` attributes on form controls.

```xml
<DataSource name="DsBoVisitInfo" backendSystem="sf"
            businessObjectClass="BoVisit"
            external="false"
            editableEntity="Visit"
            schemaVersion="2.0">
```

A context-menu or helper LO DS that should never write back uses `editableEntity=""`:

```xml
<DataSource name="DsLoAttachmentsContextMenu" backendSystem="sf"
            external="true"
            editableEntity=""
            schemaVersion="2.0">
```

Real anchor: `src/Visit/DS/DsBoVisit_sf.datasource.xml`

### UI — ONE_WAY bindings, disabled controls

All `<InputArea>` elements have `disabled="true"`. All `<Binding>` elements use
`bindingMode="ONE_WAY"`. No save `MenuItem` in `PageHeader`.

```xml
<InputArea name="Status" disabled="true">
  <Bindings>
    <Resource target="Label" type="Label" id="statusId" defaultLabel="Status" />
    <Binding target="Value" type="Text"
             binding="ProcessContext::VisitBo.status.text"
             bindingMode="ONE_WAY" />
  </Bindings>
</InputArea>
```

The page attribute `onBackDiscard="true"` discards any accidental BO mutations on navigation
back (belt-and-suspenders for truly read-only screens).

Real anchor: `src/Visit/PR/Visit_Info/Visit_InfoUI.userinterface.xml`

### Process — no save action, no ExitHandler

```xml
<Body>
  <Actions>
    <Action actionType="VIEW" name="showVisitInfo">
      <UIDescription>Visit::InfoUI</UIDescription>
    </Action>
  </Actions>
</Body>
<!-- no <ExitHandlers> block needed -->
```

Real anchor: `src/Visit/PR/Visit_Info/Visit_InfoProcess.processflow.xml`

---

## 2. Editable Variant

### DS — editableEntity must name the backend entity

For the BO to persist changes, the DS must have `editableEntity` set to the Salesforce
object API name (or CAS entity name for non-SF backends). This is the **most common
save-related misconfiguration**.

```xml
<DataSource name="DsBoVisit" backendSystem="sf"
            businessObjectClass="BoVisit"
            external="false"
            editableEntity="Visit"
            schemaVersion="2.0">
```

If `editableEntity` is missing or empty, the save call silently succeeds on the client
but does not sync the record to the backend.

Real anchor: `src/Visit/DS/DsBoVisit_sf.datasource.xml`

### UI — TWO_WAY bindings, save button

Editable fields use `bindingMode="TWO_WAY"`. Controls that must not be edited (computed or
FK fields) keep `disabled="true"` with `bindingMode="TWO_WAY"` (or ONE_WAY — depends on
whether the field should be written back via another mechanism).

```xml
<InputArea name="Description">
  <Bindings>
    <Resource target="Label" type="Label" id="descriptionId" defaultLabel="Description" />
    <Binding target="Value" type="Text"
             binding="ProcessContext::BoVisit.instructionDescription"
             bindingMode="TWO_WAY" />
  </Bindings>
</InputArea>
```

Save button in `PageHeader`:

```xml
<MenuItem directlyVisible="true" itemId="Save">
  <Bindings>
    <Resource target="Text" type="Label" id="saveId" defaultLabel="Save" />
    <Resource target="Icon" type="Image" id="Save_White" />
  </Bindings>
  <Events>
    <ButtonPressedEvent event="savePressed" />
  </Events>
</MenuItem>
```

Real anchor: `src/Visit/PR/visit_Details/visit_DetailsUI.userinterface.xml`

### Process — save action + ExitHandler validation

The save flow uses an ExitHandler of type `Validate` that runs BO-level validation before
the process exits. If validation fails, it transitions back to the VIEW action.

```xml
<Body>
  <Actions>
    <Action actionType="VIEW" name="showDetails">
      <UIDescription><Module>::<Entity>DetailsUI</UIDescription>
      <Events>
        <Event name="savePressed" action="SaveRecord" />
      </Events>
    </Action>
    <Action actionType="SAVE" name="SaveRecord" type="<BoName>">
      <Parameters>
        <Input name="object" value="ProcessContext::<BODeclaration>" />
      </Parameters>
      <TransitionTo action="showDetails" />
    </Action>
  </Actions>
</Body>
<ExitHandlers>
  <ExitHandler handlerName="validateBeforeExit"
               type="Validate"
               name="ProcessContext::<BODeclaration>">
    <OnValidationError transitionTo="showDetails" />
  </ExitHandler>
</ExitHandlers>
```

Real anchor: `src/Visit/PR/visit_Details/visit_DetailsProcess.processflow.xml`

---

## 3. Mixed Variant (Mostly Read-Only, a Few Editable Fields)

Some screens are primarily informational but allow editing specific fields (e.g., notes on
an otherwise locked Visit record, or priority on a Customer Issue).

Pattern: use `TWO_WAY` only on the fields that must be writable. Use `ONE_WAY` + `disabled="true"`
on all other fields. The ExitHandler and `editableEntity` on the DS are still required — because
any TWO_WAY field triggers dirty-tracking.

```xml
<!-- Read-only field -->
<InputArea name="Customer" disabled="true">
  <Bindings>
    <Binding target="Value" type="Text"
             binding="ProcessContext::BoVisit.customerName"
             bindingMode="ONE_WAY" />
  </Bindings>
</InputArea>

<!-- Editable field (notes only) -->
<InputAreaMultiLine name="Notes" noOfRows="3">
  <Bindings>
    <Resource target="Label" type="Label" id="notesId" defaultLabel="Notes" />
    <Binding target="Value" type="Text"
             binding="ProcessContext::BoVisit.statusRemarks"
             bindingMode="TWO_WAY" />
  </Bindings>
</InputAreaMultiLine>
```

---

## 4. Quick Decision Guide

| Scenario                   | DS editableEntity               | Binding mode                              | ExitHandler | Save button             |
| -------------------------- | ------------------------------- | ----------------------------------------- | ----------- | ----------------------- |
| Pure read-only info screen | not required (can still be set) | ONE_WAY everywhere                        | no          | no                      |
| Fully editable form        | required — set to entity name   | TWO_WAY on editable fields                | yes         | yes                     |
| Mixed (notes field only)   | required                        | ONE_WAY most + TWO_WAY on editable fields | yes         | yes                     |
| Multi-tab editable         | required                        | TWO_WAY per editable tab                  | yes         | yes (per tab or global) |

---

## 5. Validation Hooks in BL

For editable screens that need field-level or cross-field validation, add a
`validate.bl.js` method on the BO:

```js
// @namespace CUSTOM
// BoOrderDetail.validate.bl.js
this.validate = async function () {
    const errors = [];
    if (!this.status) {
        errors.push({ field: 'status', message: 'Status is required.' });
    }
    return errors;
};
```

The framework calls `validate()` automatically when the ExitHandler fires before the process exits.
If the array is non-empty the ExitHandler's `<OnValidationError transitionTo="showDetails" />` fires.

Invoke `create-business-logic` with `Method name: validate` and `Namespace: CUSTOM` to generate this.
