# Rule: LO vs LI — Responsibilities and File Colocation

## Conceptual Split

A list in CG Mobile is always a **pair**: one `.listobject.xml` and one `.listitem.xml`.

| Contract                  | Responsibility                                        | Analogy                      |
| ------------------------- | ----------------------------------------------------- | ---------------------------- |
| `Lo<Name>.listobject.xml` | Owns the DS binding, methods, paging, load behavior   | The container / query driver |
| `Li<Name>.listitem.xml`   | Defines the shape of every row (its typed properties) | The row model                |

The LO _never_ declares `<SimpleProperties>` directly. All row fields live in the LI. The LO references the LI through its `<Item objectClass="Li<Name>"/>` element.

---

## File Colocation Rule

Both files **must** live in the same folder:

```
src/<Module>/BO/Lo<Name>/
├── Lo<Name>.listobject.xml    ← container
└── Li<Name>.listitem.xml      ← row model
```

The folder name starts with `Lo` and matches the LO name exactly. The LI file uses the same base name but with the `Li` prefix.

Real anchor:

-   `src/Visit/BO/LoVisit/LoVisit.listobject.xml`
-   `src/Visit/BO/LoVisit/LiVisit.listitem.xml`

---

## LO Root Element — Attribute Reference

```xml
<ListObject name="LoVisit" generateLoadMethod="true" schemaVersion="1.1" paging="false">
```

| Attribute            | Required | Notes                                                                                               |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------- |
| `name`               | Yes      | Must start with `Lo`                                                                                |
| `generateLoadMethod` | Yes      | `"true"` for most LOs; `"false"` if the LO uses a completely custom `loadAsync` BL method           |
| `schemaVersion`      | Yes      | Always `"1.1"` (not `"2.0"` — that is the DS version)                                               |
| `paging`             | Yes      | `"false"` for lists where all rows load at once; `"true"` for large tables (e.g., product catalogs) |
| `filter`             | No       | Omit for default; `"InDatabase"` for performance-critical filtered lists                            |

From real file `LoVisit.listobject.xml`:

```xml
<ListObject name="LoVisit" generateLoadMethod="true" schemaVersion="1.1" paging="false">
  <DataSource name="DsLoVisit" />
  <Item objectClass="LiVisit" />
  <Methods>
    <Method name="beforeSaveAsync" />
    <Method name="afterSaveAsync" />
    <Method name="afterLoadAsync" />
    <Method name="beforeLoadAsync" />
    <Method name="afterDoValidateAsync" />
    <Method name="beforeDoValidateAsync" />
    <Method name="loadAsync" />
    <Method name="saveAsync" />
    <!-- custom methods -->
    <Method name="getCalendarTitle" />
    <Method name="getVisitsByDate" />
  </Methods>
</ListObject>
```

---

## `generateLoadMethod` Decision

| Value     | When to use                                                                             | What happens                                              |
| --------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `"true"`  | Default for most LOs; the framework generates the standard `loadAsync` path             | Framework auto-calls DS and populates the list            |
| `"false"` | LO needs fully custom loading (e.g., compiling items from multiple sources, no DS call) | A `.bl.js` for `loadAsync` must exist and do all the work |

Real examples:

-   `LoVisit` → `generateLoadMethod="true"` (framework-driven, DS does the query)
-   `LoKPIContextMenu` → `generateLoadMethod="false"` (BL builds items from scratch, DS returns undefined)
-   `LoProductCategoryQuickFilter` → `generateLoadMethod="false"` (custom createAsync/loadAsync)

---

## `paging` Decision

| Value     | When to use                                                                                    |
| --------- | ---------------------------------------------------------------------------------------------- |
| `"false"` | Lists with bounded rows (visits for a day, tasks for a record, context menus)                  |
| `"true"`  | Open-ended catalogs where the user scrolls for more (product catalog, customer search results) |

A paged LO requires a DS that supports limit/offset, and the UI control must be a pageable list.

---

## LI Root Element — Shape Rules

```xml
<ListItem name="Li<Name>">
  <SimpleProperties>
    <SimpleProperty id="true" name="pKey" type="DomPKey" dataSourceProperty="pKey"/>
    <!-- ...more properties -->
  </SimpleProperties>
</ListItem>
```

Rules:

1. Exactly one `<SimpleProperty>` must carry `id="true"` — this is the row primary key.
2. `dataSourceProperty` links the property to a DS `<Attribute name="...">` — must match exactly.
3. Properties without `dataSourceProperty` are computed in `afterLoadAsync` BL.
4. `storable="false"` on read-only rows prevents accidental writes.
5. All valid `Dom*` types from BO also apply here: `DomPKey`, `DomText`, `DomDate`, `DomTime`, `DomDateTime`, `DomBool`, `DomDegree`, `DomString`, `DomRgbColor`, plus domain-specific types like `DomVisitStatus`.

From real file `LiVisit.listitem.xml`:

```xml
<ListItem name="LiVisit">
  <SimpleProperties>
    <SimpleProperty id="true" name="pKey" type="DomPKey" dataSourceProperty="pKey"/>
    <SimpleProperty name="visitStatus" type="DomVisitStatus" dataSourceProperty="visitStatus"/>
    <SimpleProperty name="plannedStartDateTime" type="DomDateTime" dataSourceProperty="plannedVisitStartTime"/>
    <SimpleProperty name="retailStoreName" type="DomText" dataSourceProperty="retailStoreName"/>
    <!-- visibleInMap — computed in afterLoadAsync, no dataSourceProperty -->
    <SimpleProperty name="visibleInMap" type="DomBool"/>
  </SimpleProperties>
</ListItem>
```

---

## The `<Item objectClass="Li<Name>"/>` Wiring

The LO's `<Item objectClass="..."/>` element is how the framework knows which LI class to instantiate for each row. If this value doesn't match the LI's `name` attribute, the build fails or list items come back as empty shells.

```xml
<!-- LO side -->
<Item objectClass="LiVisit"/>

<!-- Must match: -->
<ListItem name="LiVisit">
```

---

## Common Mistakes

| Mistake                                | Effect                                                |
| -------------------------------------- | ----------------------------------------------------- |
| LI in a different folder from LO       | Build error — framework can't resolve `objectClass`   |
| `<Item objectClass="LiWrong"/>`        | Build error or empty item instances                   |
| `id="true"` on zero or two properties  | Identity failures; pKey dedup breaks                  |
| `dataSourceProperty` spelling mismatch | Property reads as `undefined` at runtime              |
| Using `schemaVersion="2.0"` on LO      | Wrong — only DS uses `2.0`; LO/LI/BO always use `1.1` |
