# Rule: Common DS Patterns — Joins, Derived Attributes, Ordering, and Save Mapping

This file covers the structural patterns that appear in nearly every non-trivial DataSource. Each pattern
has a real-file citation from ``.

---

## Joins — Connecting Related Entities

### Inner Join (required row in both tables)

```xml
<Entities>
  <Entity name="Visit" alias="" idAttribute="Id" />
  <Entity name="User" alias="">
    <Join Type="inner">
      <SimpleJoin>
        <Condition leftSideValue="Visit.VisitorId"
                   comparator="eq"
                   rightSideType="Attribute"
                   rightSideValue="User.Id" />
      </SimpleJoin>
    </Join>
  </Entity>
</Entities>
```

### Left Outer Join (optional row in the joined table)

```xml
<Entity name="RetailStore" alias="">
  <Join Type="left">
    <SimpleJoin>
      <Condition leftSideValue="Visit.PlaceId"
                 comparator="eq"
                 rightSideType="Attribute"
                 rightSideValue="RetailStore.Id" />
    </SimpleJoin>
  </Join>
</Entity>
```

### Aliased Entities (same table joined twice)

```xml
<Entity name="User" alias="Responsible">
  <Join Type="inner">
    <SimpleJoin>
      <Condition leftSideValue="Task.OwnerId"
                 comparator="eq"
                 rightSideType="Attribute"
                 rightSideValue="Responsible.Id" />
    </SimpleJoin>
  </Join>
</Entity>
<Entity name="User" alias="Initiator">
  <Join Type="inner">
    <SimpleJoin>
      <Condition leftSideValue="Task.CreatedById"
                 comparator="eq"
                 rightSideType="Attribute"
                 rightSideValue="Initiator.Id" />
    </SimpleJoin>
  </Join>
</Entity>
```

When an alias is set, all `<Attribute>` rows and QueryCondition references must use the alias, not the table name.

Real-file anchor: `src/Visit/DS/DsBoVisit_sf.datasource.xml`
(Visit + User inner join on `Visit.VisitorId = User.Id`)

---

## DerivedAttribute — Computed Columns

A `<DerivedAttribute>` adds a computed value to the result set. It does not correspond to a real column.

### Hardcoded literal

```xml
<DerivedAttribute name="duration" value="'90'" />
<DerivedAttribute name="allDay"   value="'0'" />
```

### CASE expression for icon/image names

```xml
<DerivedAttribute name="priorityImage"
                  value="'TaskPriority_' || CASE WHEN Task.Priority = 'High' THEN 'A'
                                                  WHEN Task.Priority = 'Normal' THEN 'B'
                                                  ELSE 'C' END" />
```

### Concatenation of joined columns

```xml
<DerivedAttribute name="responsibleName"
                  value="Responsible.LastName || ', ' || Responsible.FirstName" />
<DerivedAttribute name="visitName"
                  value="RetailStore.Name || ' - ' || Visit.Name" />
```

### Referencing a system macro

```xml
<DerivedAttribute name="salesOrg" value="'#SalesOrg#'" />
```

Real-file anchor: `src/Visit/DS/DsBoVisit_sf.datasource.xml`
(`DerivedAttribute name="duration" value="'90'"`)
Also: `src/Visit/DS/DsLoVisit_sf.datasource.xml`
(multiple `DerivedAttribute` rows for `allDay`, `color`, `visitName`, `mapPinId`, etc.)

---

## OrderCriterion — Required Three Attributes

Every `<OrderCriterion>` must have all three attributes. Missing any one silently breaks ordering or
causes a build warning.

```xml
<OrderCriteria>
  <OrderCriterion entity="Visit" attribute="PlannedVisitStartTime" direction="ASC" />
</OrderCriteria>
```

| Attribute   | Required | Valid values                                       |
| ----------- | -------- | -------------------------------------------------- |
| `entity`    | Yes      | Must match an `<Entity name="...">` in the same DS |
| `attribute` | Yes      | Column name to sort by                             |
| `direction` | Yes      | `ASC` or `DESC` — NOT `sortOrder`                  |

Empty criteria block is valid when no sort is needed:

```xml
<OrderCriteria />
```

---

## Save Mapping — Writing Back to the Database

To make a DS writable, two things must be set correctly:

1. `editableEntity="<EntityName>"` on the root `<DataSource>` element
2. Every column that will be written needs a plain `<Attribute>` mapping (not just `<DateTimeAttribute>`)

### The DateTimeAttribute save-mapping trap

`<DateTimeAttribute>` splits one DateTime column into two DS attributes (date + time) for the UI.
But it is **NOT included in the save mapping** — `Facade.saveObjectAsync` only reads plain `<Attribute>` rows.

You need both:

```xml
<!-- For UI split (read) -->
<DateTimeAttribute dateName="plannedStartDate" timeName="plannedStartTime"
                   table="Visit" column="PlannedVisitStartTime" />

<!-- For save mapping (write) — ALSO required -->
<Attribute name="plannedVisitStartTime" table="Visit" column="PlannedVisitStartTime" />
```

The BO must hold a combined `DomDateTime` property and `beforeSaveAsync` must recombine:

```javascript
me.setPlannedStartDateTime(me.getPlannedStartDate() + ' ' + me.getPlannedStartTime() + ':00');
```

Real-file anchor: `src/Visit/DS/DsBoVisit_sf.datasource.xml`
(has both `DateTimeAttribute` pairs AND plain `<Attribute>` rows for `plannedVisitStartTime` / `plannedVisitEndTime`)

---

## QuickSearch Patterns

`<QuickSearchParameters>` enables text search across multiple fields in a list screen.

```xml
<QuickSearchParameters>
  <QuickSearchParameter name="Visit.Name" />
  <QuickSearchParameter name="RetailStore.Name" />
  <QuickSearchParameter name="responsibleName" />
</QuickSearchParameters>
```

Note: parameter names reference either `Table.Column` (real columns) or the `name` of a `DerivedAttribute`.
The framework wraps each in a LIKE condition automatically.

---

## ConditionalParameters — Optional Search Filters

For LOs that support user-driven search. Each condition is applied **only when the parameter is provided**.

```xml
<ConditionalParameters>
  <ConditionalParameter name="text">
    <SimpleConditions>
      <Condition leftSideValue="Visit.Name"
                 comparator="#textComp#"
                 rightSideType="Attribute"
                 rightSideValue="'#text#'" />
    </SimpleConditions>
  </ConditionalParameter>
</ConditionalParameters>
```

Pattern: every `ConditionalParameter` pairs with a companion `*Comp` parameter for the comparator operator
(`eq`, `startsWith`, `contains`, etc.).

---

## DS Naming Quick Reference

| Prefix             | Kind                 | File suffix          | Example                                          |
| ------------------ | -------------------- | -------------------- | ------------------------------------------------ |
| `DsBo*`            | Single-record BO     | `_sf.datasource.xml` | `DsBoVisit_sf.datasource.xml`                    |
| `DsLo*`            | List / collection    | `_sf.datasource.xml` | `DsLoVisit_sf.datasource.xml`                    |
| `DsLu*`            | Lookup / reference   | `_sf.datasource.xml` | `DsLuVisitCountByDate_sf.datasource.xml`         |
| `DsLo*ContextMenu` | Context menu (empty) | `_sf.datasource.xml` | `DsLoVisitOverviewContextMenu_sf.datasource.xml` |

File location: `src/<Module>/DS/<DsName>_sf.datasource.xml`
