# ListObject Layer

> The `src/<Module>/…` paths used throughout this document are drawn from a
> representative CG Mobile customer workspace. Your workspace may use different
> module names, but the folder structure (`DS/`, `BO/`, `LO/`, `PR/`, `UI/`)
> and naming conventions are identical.

## Overview

ListObjects (LO) represent collections of items and provide operations for managing lists, filtering, sorting, and aggregating data. Unlike Business Objects which represent single entities, ListObjects manage multiple items of the same type.

## ListObject Fundamentals

### Purpose

ListObjects (LO) serve as the collection management layer that:

-   Represent collections of items (e.g., list of account receivables, list of visits)
-   Define item structure through separate ListItem (Li) definitions
-   Provide collection-level methods (add, remove, filter, sort, count)
-   Implement custom aggregation and calculation logic
-   Support parent-child relationships with Business Objects
-   Enable batch operations on multiple items

### Directory Structure

```
src/
├── Call/
│   └── BO/
│       └── LoAccountReceivables/
│           ├── LoAccountReceivables.listobject.xml
│           ├── LiAccountReceivables.listitem.xml
│           └── Mv2/
│               ├── LoadAsync/
│               ├── SaveAsync/
│               └── [custom methods]/
└── Visit/
    └── BO/
        └── LoVisit/
            ├── LoVisit.listobject.xml
            ├── LiVisit.listitem.xml
            └── Mv2/
                └── [business logic]
```

**Note:** ListObjects are located in the `BO/` directory, not a separate `LO/` directory.

## ListObject vs Business Object

| Aspect          | Business Object (BO)                     | ListObject (LO)                                          |
| --------------- | ---------------------------------------- | -------------------------------------------------------- |
| **Represents**  | Single entity                            | Collection of items                                      |
| **Structure**   | SimpleProperties in BO definition        | Item properties in separate ListItem definition          |
| **Primary Key** | One ID property                          | List doesn't have ID; items do                           |
| **DataSource**  | Loads one record                         | Loads multiple records                                   |
| **Methods**     | Entity operations (load, save, validate) | Collection operations (getAllItems, addItem, removeItem) |
| **Example**     | BoAccount (one account)                  | LoAccountReceivables (list of receivables)               |
| **Lifecycle**   | Create, Load, Save, Validate             | Load, Save, Validate (no Create)                         |
| **Use Case**    | Detail screen, form editing              | List screens, tables, grids                              |

## ListObject XML Structure

### Root Element

```xml
<ListObject
  name="LoAccountReceivables"        <!-- LO identifier -->
  generateLoadMethod="true"          <!-- Auto-generate load method? -->
  schemaVersion="1.1"                <!-- Schema version -->
  paging="false"                     <!-- Enable pagination? -->
>
```

### Key Elements

| Element        | Description                                         | Required |
| -------------- | --------------------------------------------------- | -------- |
| `<DataSource>` | Reference to datasource for loading items           | Yes      |
| `<Item>`       | Reference to ListItem class defining item structure | Yes      |
| `<Methods>`    | Lifecycle and custom method declarations            | Yes      |

## Example 1: LoAccountReceivables (Account Receivables List)

**Location:** `src/Call/BO/LoAccountReceivables/LoAccountReceivables.listobject.xml`
**Purpose:** Manage a list of account receivable records for a customer
**Complexity:** Moderate (custom aggregation methods)

### Complete ListObject XML

```xml
<ListObject name="LoAccountReceivables" generateLoadMethod="true" schemaVersion="1.1">
  <DataSource name="DsLoAccountReceivables" />
  <Item objectClass="LiAccountReceivables" />
  <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" />
    <Method name="calculateAccountReceivablesForCard" />
    <Method name="prepareAccountReceivablesCardInfo" />
  </Methods>
</ListObject>
```

### Analysis

#### DataSource Reference

```xml
<DataSource name="DsLoAccountReceivables" />
```

References: `src/Call/DS/DsLoAccountReceivables_sf.datasource.xml`

This DS loads multiple Account_Receivable\_\_c records filtered by customer:

```xml
<DataSource name="DsLoAccountReceivables" backendSystem="sf" readOnly="true" ...>
  <QueryCondition><![CDATA[
    Account_Receivable__c.Account__c = #customerPKey#
  ]]></QueryCondition>
  <OrderCriteria>
    <OrderCriterion entity="Account_Receivable__c" attribute="Receipt_Date__c" direction="ASC" />
  </OrderCriteria>
</DataSource>
```

#### Item Reference

```xml
<Item objectClass="LiAccountReceivables" />
```

Points to: `src/Call/BO/LoAccountReceivables/LiAccountReceivables.listitem.xml`

Each item in the list is an instance of `LiAccountReceivables`.

#### Methods

**Standard Lifecycle:**

-   `loadAsync` - Load items from datasource
-   `saveAsync` - Save modified items to datasource
-   `beforeLoadAsync` / `afterLoadAsync` - Load hooks
-   `beforeSaveAsync` / `afterSaveAsync` - Save hooks
-   `beforeDoValidateAsync` / `afterDoValidateAsync` - Validation hooks

**Custom Methods:**

-   `calculateAccountReceivablesForCard` - Aggregate receivables for dashboard card
-   `prepareAccountReceivablesCardInfo` - Format receivables for UI display

## ListItem Definition: LiAccountReceivables

**Location:** `src/Call/BO/LoAccountReceivables/LiAccountReceivables.listitem.xml`
**Purpose:** Define the structure of each item in LoAccountReceivables

### Complete ListItem XML

```xml
<ListItem name="LiAccountReceivables">
  <SimpleProperties>
    <SimpleProperty name="pKey" type="DomPKey" dataSourceProperty="pKey" />
    <SimpleProperty name="externalId" type="DomText" dataSourceProperty="externalId" />
    <SimpleProperty name="documentType" type="DomBpaReceivableDocType" dataSourceProperty="documentType" />
    <SimpleProperty name="receiptDate" type="DomDate" dataSourceProperty="receiptDate" />
    <SimpleProperty name="dueDate" type="DomDate" dataSourceProperty="dueDate" />
    <SimpleProperty name="amount" type="DomMoney" dataSourceProperty="amount" />
    <SimpleProperty name="amountOpen" type="DomMoney" dataSourceProperty="amountOpen" />
    <SimpleProperty name="invoiceStatus" type="DomText" dataSourceProperty="invoiceStatus" />
    <SimpleProperty name="accountReceivableIcon" type="DomString" />
    <SimpleProperty name="externalIdInvoiceInfo" type="DomText" />
    <SimpleProperty name="dueDateText" type="DomText" />
    <SimpleProperty name="receiptDateText" type="DomText" />
  </SimpleProperties>
</ListItem>
```

### Analysis

#### Persisted Properties (from DataSource)

| Property      | Type                    | DS Property   | SF Field            | Purpose              |
| ------------- | ----------------------- | ------------- | ------------------- | -------------------- |
| pKey          | DomPKey                 | pKey          | Id                  | Item identifier      |
| externalId    | DomText                 | externalId    | External_Id\_\_c    | ERP system reference |
| documentType  | DomBpaReceivableDocType | documentType  | Document_Type\_\_c  | Invoice, Credit Note |
| receiptDate   | DomDate                 | receiptDate   | Receipt_Date\_\_c   | When received        |
| dueDate       | DomDate                 | dueDate       | Due_Date\_\_c       | Payment due date     |
| amount        | DomMoney                | amount        | Amount\_\_c         | Original amount      |
| amountOpen    | DomMoney                | amountOpen    | Amount_Open\_\_c    | Outstanding amount   |
| invoiceStatus | DomText                 | invoiceStatus | Invoice_Status\_\_c | Open, Paid, etc.     |

#### Computed Properties (no dataSourceProperty)

| Property              | Type      | Purpose                              |
| --------------------- | --------- | ------------------------------------ |
| accountReceivableIcon | DomString | Icon to display based on status/date |
| externalIdInvoiceInfo | DomText   | Combined display string              |
| dueDateText           | DomText   | Localized due date string            |
| receiptDateText       | DomText   | Localized receipt date string        |

These are calculated in business logic (see below).

## Example 2: LoVisit (Visit List)

**Location:** `src/Visit/BO/LoVisit/LoVisit.listobject.xml`
**Purpose:** Manage a list of visit records
**Complexity:** High (40+ properties per item, custom navigation methods)

### 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" />
    <Method name="getCalendarTitle" />
    <Method name="getVisitsByDate" />
    <Method name="getDailyViewTitle" />
    <Method name="getRetailStoreId" />
    <Method name="navigateToCustomer" />
    <Method name="prepareMapDetails" />
  </Methods>
</ListObject>
```

### ListItem: LiVisit (Excerpt)

```xml
<ListItem name="LiVisit">
  <SimpleProperties>
    <SimpleProperty id="true" name="pKey" type="DomPKey" dataSourceProperty="pKey" />
    <SimpleProperty name="instructionDescription" type="DomText" dataSourceProperty="instructionDescription" />
    <SimpleProperty name="name" type="DomText" dataSourceProperty="name" />

    <!-- Retail Store Info (8 properties) -->
    <SimpleProperty name="retailStoreName" type="DomText" dataSourceProperty="retailStoreName" />
    <SimpleProperty name="retailStoreCity" type="DomText" dataSourceProperty="retailStoreCity" />
    <SimpleProperty name="retailStoreLongitude" type="DomDegree" dataSourceProperty="retailStoreLongitude" />
    <SimpleProperty name="retailStoreLatitude" type="DomDegree" dataSourceProperty="retailStoreLatitude" />
    <!-- ... more store properties ... -->

    <!-- Date/Time Properties -->
    <SimpleProperty name="plannedStartDateTime" type="DomDateTime" dataSourceProperty="plannedVisitStartTime" />
    <SimpleProperty name="plannedEndDateTime" type="DomDateTime" dataSourceProperty="plannedVisitEndTime" />
    <SimpleProperty name="plannedStartDate" type="DomDate" />  <!-- Computed -->
    <SimpleProperty name="plannedStartTime" type="DomTime" />  <!-- Computed -->

    <!-- Status -->
    <SimpleProperty name="visitStatus" type="DomVisitStatus" dataSourceProperty="visitStatus" />

    <!-- Address (3 properties) -->
    <SimpleProperty name="city" type="DomText" dataSourceProperty="city" />
    <SimpleProperty name="street" type="DomText" dataSourceProperty="street" />
    <SimpleProperty name="postalCode" type="DomText" dataSourceProperty="postalCode" />
    <SimpleProperty name="combinedAddress" type="DomText" />  <!-- Computed -->

    <!-- Map Display Properties -->
    <SimpleProperty name="mapPinId" type="DomString" storable="false" dataSourceProperty="mapPinId" />
    <SimpleProperty name="mapPinImage" type="DomString" storable="false" dataSourceProperty="mapPinImage" />
    <SimpleProperty name="latitude" type="DomDegree" dataSourceProperty="latitude" />
    <SimpleProperty name="longitude" type="DomDegree" dataSourceProperty="longitude" />
    <SimpleProperty name="visibleInMap" type="DomBool" />  <!-- Computed -->

    <!-- 40+ properties total ... -->
  </SimpleProperties>
</ListItem>
```

**Key Observations:**

-   **Rich item structure:** 40+ properties per visit
-   **Computed properties:** combinedAddress, plannedStartDate/Time split from DateTime
-   **Map integration:** latitude, longitude, mapPinImage for map display
-   **Storable flag:** `storable="false"` means not persisted to datasource

## Business Logic Implementation

### Example: Calculate Account Receivables for Card

**File:** `src/Call/BO/LoAccountReceivables/Mv2/LoAccountReceivables.CalculateAccountReceivablesForCard.bl.js`

```javascript
/**
 * @function calculateAccountReceivablesForCard
 * @this LoAccountReceivables
 * @kind listobject
 * @async
 * @namespace CORE
 * @param {String} customerPKey
 * @returns promise
 */
function calculateAccountReceivablesForCard(customerPKey) {
    var me = this;

    // Build query parameters
    var jsonParams = [];
    jsonParams.push({
        field: 'customerPKey',
        operator: 'EQ',
        value: customerPKey,
    });

    var jsonQuery = {};
    jsonQuery.params = jsonParams;
    var relevantAccountReceivables = [];
    var currentDate = Utils.createAnsiDateToday();

    // Load account receivables for this customer
    var promise = BoFactory.loadObjectByParamsAsync('LoAccountReceivables', jsonQuery).then(function (
        loAccountReceivables
    ) {
        var receivablesRelevantAccount = loAccountReceivables.getAllItems();

        // Process each receivable item
        receivablesRelevantAccount.forEach(function (accountRecievables) {
            // Set icon based on due date and status
            if (accountRecievables.getDueDate() < currentDate && accountRecievables.getInvoiceStatus() === 'UnPaid') {
                accountRecievables.setAccountReceivableIcon('WarningTriangle_IC');
            } else if (
                accountRecievables.getDueDate() < currentDate &&
                accountRecievables.getInvoiceStatus() === 'PartiallyPaid'
            ) {
                accountRecievables.setAccountReceivableIcon('WarningCircle_IC');
            } else if (
                accountRecievables.getDueDate() >= currentDate ||
                accountRecievables.getDueDate() === Utils.getMinDate()
            ) {
                accountRecievables.setAccountReceivableIcon(' ');
            }

            // Localize dates
            var accountReceivableDueDate = Localization.localize(accountRecievables.getDueDate(), 'date');
            var accountReceivableReceiptDate = Localization.localize(accountRecievables.getReceiptDate(), 'date');

            accountRecievables.setDueDateText(accountReceivableDueDate);
            accountRecievables.setReceiptDateText(accountReceivableReceiptDate);

            // Get document type display text
            var documentTypeText = Utils.getToggleText('DomBpaReceivableDocType', accountRecievables.getDocumentType());

            // Combine external ID and document type for display
            if (
                !Utils.isEmptyString(accountRecievables.getDocumentType()) &&
                !Utils.isEmptyString(accountRecievables.getExternalId())
            ) {
                accountRecievables.setExternalIdInvoiceInfo(
                    accountRecievables.getExternalId() + ' - ' + documentTypeText
                );
            } else if (Utils.isEmptyString(accountRecievables.getDocumentType())) {
                accountRecievables.setExternalIdInvoiceInfo(accountRecievables.getExternalId());
            } else if (Utils.isEmptyString(accountRecievables.getExternalId())) {
                accountRecievables.setExternalIdInvoiceInfo(documentTypeText);
            }

            relevantAccountReceivables.push(accountRecievables);
        });

        // Update list with processed items
        me.cardItemCount = relevantAccountReceivables.length;
        me.removeAllItems();
        me.addItems(relevantAccountReceivables);
    });

    return promise;
}
```

### Business Logic Patterns Demonstrated

1. **Dynamic Loading**

    ```javascript
    BoFactory.loadObjectByParamsAsync('LoAccountReceivables', jsonQuery);
    ```

    - Load LO with query parameters
    - Returns promise with populated list

2. **Item Iteration**

    ```javascript
    loAccountReceivables.getAllItems().forEach(function (item) {
        // Process each item
    });
    ```

    - Get all items in the list
    - Iterate and process individually

3. **Computed Property Population**

    ```javascript
    item.setAccountReceivableIcon('WarningTriangle_IC');
    item.setDueDateText(localizedDate);
    item.setExternalIdInvoiceInfo(combinedString);
    ```

    - Set computed properties based on business rules

4. **Conditional Logic**

    - Icon assignment based on due date vs current date
    - Status-dependent processing (UnPaid, PartiallyPaid)

5. **Localization**

    ```javascript
    Localization.localize(date, 'date');
    ```

    - Format dates for user's locale

6. **List Manipulation**

    ```javascript
    me.removeAllItems();
    me.addItems(relevantAccountReceivables);
    ```

    - Clear existing items
    - Add new item collection

7. **Collection Aggregation**
    ```javascript
    me.cardItemCount = relevantAccountReceivables.length;
    ```
    - Track list-level metadata

## ListObject Collection Operations

### Core Collection Methods

| Method                | Description             | Example                                  |
| --------------------- | ----------------------- | ---------------------------------------- |
| `getAllItems()`       | Get array of all items  | `var items = lo.getAllItems();`          |
| `getItemByPKey(pKey)` | Get specific item by ID | `var item = lo.getItemByPKey("001xxx");` |
| `addItem(item)`       | Add single item to list | `lo.addItem(newItem);`                   |
| `addItems(items)`     | Add array of items      | `lo.addItems([item1, item2]);`           |
| `removeItem(item)`    | Remove item from list   | `lo.removeItem(item);`                   |
| `removeAllItems()`    | Clear all items         | `lo.removeAllItems();`                   |
| `getItemCount()`      | Get number of items     | `var count = lo.getItemCount();`         |
| `getItemAt(index)`    | Get item by index       | `var item = lo.getItemAt(0);`            |

### Filtering and Searching

```javascript
// Filter items by condition
var unpaidItems = lo.getAllItems().filter(function (item) {
    return item.getInvoiceStatus() === 'UnPaid';
});

// Find specific item
var foundItem = lo.getAllItems().find(function (item) {
    return item.getExternalId() === 'INV-12345';
});

// Check if any item matches condition
var hasOverdue = lo.getAllItems().some(function (item) {
    return item.getDueDate() < currentDate;
});
```

### Sorting

```javascript
// Sort items by date
var sortedItems = lo.getAllItems().sort(function (a, b) {
    return a.getDueDate() - b.getDueDate();
});

// Multi-level sort
var sortedItems = lo.getAllItems().sort(function (a, b) {
    var dateCompare = a.getDueDate() - b.getDueDate();
    if (dateCompare !== 0) return dateCompare;
    return a.getAmount() - b.getAmount();
});
```

### Aggregation

```javascript
// Sum amounts
var totalAmount = lo.getAllItems().reduce(function (sum, item) {
    return sum + item.getAmount();
}, 0);

// Count by status
var statusCounts = {};
lo.getAllItems().forEach(function (item) {
    var status = item.getInvoiceStatus();
    statusCounts[status] = (statusCounts[status] || 0) + 1;
});

// Get max/min
var maxAmount = Math.max.apply(
    null,
    lo.getAllItems().map(function (item) {
        return item.getAmountOpen();
    })
);
```

## ListObject Lifecycle

### Load Flow

```
Process requests LO load (e.g., "Load Receivables for Customer")
    |
1. LO.loadAsync({customerPKey: "001xxx"}) called
    |
2. beforeLoadAsync(context)
    - Modify load parameters
    - Set up filtering
    |
3. DataSource query executes
    - Query with parameters
    - Returns multiple records
    |
4. For each record:
    - Create LiAccountReceivables instance
    - Map DS attributes to Li properties
    - Add item to list
    |
5. afterLoadAsync(result, context)
    - Calculate computed properties for each item
    - Perform list-level aggregations
    - Sort/filter items
    |
6. LO ready with items
```

### Save Flow

```
User modifies list items
    |
1. LO.saveAsync() called
    |
2. beforeSaveAsync(context)
    - Validate list-level rules
    - Prepare items for save
    |
3. For each modified item:
    - Validate item
    - Transform data
    - Write to datasource
    |
4. afterSaveAsync(result, context)
    - Post-save processing
    - Refresh calculated values
    |
5. LO persisted
```

## Complete Data Flow: Salesforce to ListObject

### LoAccountReceivables Example

```
Salesforce: Account_Receivable__c Object
    Fields: Id, External_Id__c, Amount__c, Due_Date__c, ...
    |
DsLoAccountReceivables: DataSource
    <QueryCondition>Account_Receivable__c.Account__c = #customerPKey#</QueryCondition>
    <Attribute name="amount" column="Amount__c" />
    |
LiAccountReceivables: ListItem Definition
    <SimpleProperty name="amount" type="DomMoney" dataSourceProperty="amount" />
    <SimpleProperty name="accountReceivableIcon" type="DomString" />  <!-- Computed -->
    |
LoAccountReceivables: ListObject
    - Contains collection of LiAccountReceivables items
    - Methods: loadAsync, calculateAccountReceivablesForCard
    |
Business Logic: calculateAccountReceivablesForCard
    - Loads items by customerPKey
    - Iterates through items
    - Sets computed properties (icon, localizedDates)
    - Updates list
    |
Process Variable: loAccountReceivables
    - Referenced in Process flow
    - Passed to UI
    |
UI List Component
    - Displays list items
    - Binds to item properties (amount, icon, dueDateText)
    |
User sees formatted, aggregated list of account receivables
```

## ListObject Best Practices

### 1. Separate Concerns

**ListObject:** List-level operations (load all, count, aggregate)

```javascript
lo.getItemCount();
lo.calculateTotalAmount();
```

**ListItem:** Individual item properties and operations

```javascript
item.getAmount();
item.setAccountReceivableIcon('WarningTriangle_IC');
```

### 2. Computed Properties in Items

Put computed properties in ListItem, calculate them in LO business logic:

```javascript
// After loading items
me.getAllItems().forEach(function (item) {
    // Calculate computed properties for each item
    item.setDueDateText(Localization.localize(item.getDueDate(), 'date'));
});
```

### 3. Efficient Filtering

Load only what you need using DS query conditions:

```xml
<QueryCondition><![CDATA[
    Account_Receivable__c.Account__c = #customerPKey#
    AND Account_Receivable__c.Invoice_Status__c != 'Paid'
]]></QueryCondition>
```

Avoid loading everything and filtering in JavaScript.

### 4. List-Level Metadata

Store aggregations and counts as LO properties:

```javascript
me.totalAmount = items.reduce((sum, i) => sum + i.getAmount(), 0);
me.overdueCount = items.filter((i) => i.getDueDate() < today).length;
me.cardItemCount = items.length;
```

### 5. Promise Chaining for Dependencies

```javascript
return me
    .loadAsync({ customerPKey: pKey })
    .then(function () {
        return me.calculateAccountReceivablesForCard(pKey);
    })
    .then(function () {
        return me.prepareAccountReceivablesCardInfo();
    });
```

## Comparison: BO vs LO vs Li

| Aspect                  | Business Object       | ListObject                 | ListItem               |
| ----------------------- | --------------------- | -------------------------- | ---------------------- |
| **File Extension**      | `.businessobject.xml` | `.listobject.xml`          | `.listitem.xml`        |
| **Represents**          | Single entity         | Collection                 | Item in collection     |
| **Properties Location** | In BO definition      | In separate Li definition  | In Li definition       |
| **DataSource**          | One record            | Multiple records           | N/A (uses LO's DS)     |
| **Has pKey**            | Yes (one ID)          | No (list itself has no ID) | Yes (each item has ID) |
| **Create Method**       | Yes                   | No                         | No                     |
| **Load Method**         | Loads one             | Loads many                 | N/A                    |
| **Access Pattern**      | `bo.getName()`        | `lo.getAllItems()`         | `item.getName()`       |
| **Used For**            | Detail screens        | List screens               | List item rendering    |

## Integration with Business Objects

### Parent-Child Relationship

```xml
<!-- In BoVisit (parent) -->
<ListObjects>
  <ListObject name="loAssessmentTasks"
              objectClass="LoVisitAssessmentTask"
              dataSourceProperty="pKey"
              listProperty="visitId"/>
</ListObjects>
```

**Usage:**

```javascript
// In BoVisit business logic
var assessmentTasks = me.getLoAssessmentTasks();
return assessmentTasks.loadAsync({ visitId: me.getPKey() }).then(function () {
    var taskCount = assessmentTasks.getItemCount();
    me.setTaskCount(taskCount);
});
```

**Relationship:**

-   Parent BO (BoVisit) has child LO (LoAssessmentTasks)
-   Parent's `pKey` maps to child's `visitId` filter
-   Access via getter: `getLoAssessmentTasks()`

## Key Takeaways

1. **ListObjects manage collections** of items, not single entities
2. **Separate ListItem definition** specifies structure of each item
3. **DataSource loads multiple records** filtered by parameters
4. **Collection operations** (getAllItems, addItems, removeAllItems) manage the list
5. **Computed properties** calculated in business logic for each item
6. **Custom aggregation methods** (calculateAccountReceivablesForCard) perform list-level operations
7. **No Create lifecycle** - ListObjects load existing items
8. **Item access** via getAllItems(), getItemByPKey(), getItemAt()
9. **Promise-based async** operations for loading and processing
10. **Parent-child relationships** enable nested collections (BO to LO)

## Files Referenced

| File Path                                                                                          | Purpose                      |
| -------------------------------------------------------------------------------------------------- | ---------------------------- |
| src/Call/BO/LoAccountReceivables/LoAccountReceivables.listobject.xml                               | ListObject definition        |
| src/Call/BO/LoAccountReceivables/LiAccountReceivables.listitem.xml                                 | ListItem structure           |
| src/Call/DS/DsLoAccountReceivables_sf.datasource.xml                                               | DataSource for loading items |
| src/Call/BO/LoAccountReceivables/Mv2/LoAccountReceivables.CalculateAccountReceivablesForCard.bl.js | Custom aggregation logic     |
| src/Visit/BO/LoVisit/LoVisit.listobject.xml                                                        | Complex ListObject example   |
| src/Visit/BO/LoVisit/LiVisit.listitem.xml                                                          | Complex ListItem example     |

---

_This documentation is maintained by the Modeler CLI plugin and refreshed on workspace upgrade._
