# Rule: Facade.getListAsync — How Callers Consume an LO

## Overview

An LO is consumed in two ways:

1. **Framework-driven load** — the framework auto-loads the LO when a Process action or BO child-list binding triggers it. Code never calls `getListAsync` directly.
2. **Explicit BL load** — a `.bl.js` method (card loader, custom query, refresh) calls `Facade.getListAsync("Lo<Name>", jsonQuery)` directly.

Most custom LO BL methods use the explicit load pattern. This page documents that pattern.

---

## Real Example

File: `src/Visit/BO/LoVisit/Mv2/LoVisit.GetVisitsByDate.bl.js`, line 113:

```javascript
return Facade.getListAsync('LoVisit', jsonQuery);
```

Full context (lines 95–125):

```javascript
var jsonQuery = {};
var jsonParams = [];
jsonParams.push({
    field: 'plannedStartDate',
    operator: 'EQ',
    value: plannedStartDate,
});
jsonParams.push({
    field: 'plannedEndDate',
    operator: 'EQ',
    value: plannedEndDate,
});
jsonQuery.params = jsonParams;

return Facade.getListAsync('LoVisit', jsonQuery);
```

The `getListAsync` call resolves with a raw array of LI instances — not an LO. The caller then calls `me.addItems(list, ...)` to populate the LO.

---

## Signature

```javascript
Facade.getListAsync(loName, jsonQuery);
// Returns: Promise<Array<Li<Name>>>
```

| Argument    | Type     | Notes                                                                                   |
| ----------- | -------- | --------------------------------------------------------------------------------------- |
| `loName`    | `string` | Exact LO name: `"LoVisit"`, `"LoMyTask"`, etc. Case-sensitive.                          |
| `jsonQuery` | `Object` | Contains `params` array and optional `cond` string. Pass `{}` for a parameterless call. |

---

## jsonQuery Structure

```javascript
var jsonQuery = {
    params: [
        { field: 'ownerId', value: Facade.getSystemProperty('UserPKey') },
        { field: 'startDate', value: Utils.convertForDBParam(startDate, 'DomDate') },
    ],
    cond: " AND Visit.Status IN ('Planned','InProgress') ", // only if DS has a #cond# param
};
```

-   `field` must match a `<Parameter name="...">` declared in the LO's companion DS.
-   `value` must be pre-converted with `Utils.convertForDBParam(value, domType)` for all non-string types.
-   `cond` is a raw SQL snippet — only safe when the DS declares `<Parameter name="cond" treatAs="sqlSnippet"/>`.

---

## Typical BL Pattern After getListAsync

```javascript
function getVisitsForCard(cardDate) {
    var me = this;
    var jsonQuery = {
        params: [{ field: 'plannedStartDate', value: Utils.convertForDBParam(cardDate, 'DomDate') }],
    };

    me.removeAllItems();

    return Facade.getListAsync('LoVisit', jsonQuery).then(function (list) {
        me.addItems(list, jsonQuery.params);
        me.orderBy({ plannedStartDateTime: 'ASC' });
        return me;
    });
}
```

Key steps:

1. `me.removeAllItems()` — always clear before repopulating.
2. `Facade.getListAsync(...)` — returns a Promise resolving to an LI array.
3. `me.addItems(list, jsonQuery.params)` — the `params` second argument passes filter context to the LO.
4. `me.orderBy(...)` — sort the populated LO in memory.

---

## Facade.getListAsync vs me.loadAsync

| Aspect          | `Facade.getListAsync(loName, query)`  | `me.loadAsync(query)`    |
| --------------- | ------------------------------------- | ------------------------ |
| Returns         | Raw `Array<LiInstance>`               | Populated LO instance    |
| Lifecycle hooks | No `beforeLoadAsync`/`afterLoadAsync` | Full lifecycle fires     |
| Used in         | Card BL methods, custom queries       | Framework initialization |
| Control         | You call `addItems` yourself          | Framework manages items  |

Use `getListAsync` when you need to control item trimming, ordering, or multiple loads into one LO. Use `me.loadAsync` when the standard framework initialization is sufficient.

---

## Binding an LO to a UI List Control

To display an LO in a screen, the Process must load it first and bind it in the UI:

```xml
<!-- Process: create the LO empty, then load -->
<Action name="CreateList" actionType="CREATE" type="LoVisit">
  <Return name="ProcessContext::VisitList"/>
</Action>

<Action name="LoadList" actionType="LOAD" type="LoVisit">
  <Parameters>
    <Input name="plannedStartDate" value="ProcessContext::SelectedDate"/>
  </Parameters>
  <Return name="ProcessContext::VisitList"/>
</Action>
```

```xml
<!-- UI: bind to list control -->
<Control name="VisitListControl" type="ListControl"
         binding="ProcessContext::VisitList"/>
```

For cockpit-card LOs, see the `add-cockpit-card` skill — the pattern uses `LOGIC` call to a card method instead of `LOAD`.

---

## Parameter Flow Summary

```
UI / Process action
        ↓
  jsonQuery.params
        ↓
  Facade.getListAsync("LoVisit", jsonQuery)
        ↓
  DS <Parameter name="plannedStartDate"> ← field name must match
        ↓
  <QueryCondition> WHERE Visit.PlannedVisitStartTime = #plannedStartDate# </QueryCondition>
        ↓
  Array<LiVisit> returned
```

If any name in the chain mismatches, the parameter is silently ignored and the query runs without that filter — returning unexpected rows or all rows.
