
# Unified DependsOn Format & Conditional Config Requirements

This document describes the requirements for standardizing the `dependsOn` config format and introducing a new `conditional_config` column for advanced program/field-based filtering. It also details the expected behavior during template-to-program cloning.


## 1. Existing `config` Column (DependsOn Format)

- **No new column for UI logic.**
- Only update the **format** of `dependsOn` inside the existing `config` column:
  - All dependency logic lives in a single array of objects under the `dependsOn` key.
  - No change to other config keys/values.
  - The old top-level `prefill` object is no longer used.

### 1.1 Value Field — Always an Array

Every `dependsOn` entry's `value` field must be a JSON array, even for a single value:

```json
// ✅ Correct
{ "questionBindingKey": "paymentMode", "value": ["Card swipe"] }

// ❌ Wrong — scalar string
{ "questionBindingKey": "paymentMode", "value": "Card swipe" }
```

### 1.2 Duplicate Entries — Merged by questionId + questionBindingKey

If multiple `dependsOn` entries reference the same question, they are merged into a single entry with a combined `value` array:

```json
// Before (two entries for same question)
[
  { "questionBindingKey": "travelMode", "value": ["Flight"] },
  { "questionBindingKey": "travelMode", "value": ["Train"] }
]

// After (single merged entry)
[
  { "questionBindingKey": "travelMode", "value": ["Flight", "Train"] }
]
```

### 1.3 No Runtime Type Injection

- `normalizeQuestionConfig` does **not** inject `type: "show"` into entries that lack a `type` field.
- Config is passed through as-is from the database.
- What is stored in DB is what the backend uses — no silent mutations at runtime.

### 1.4 Unified Format Example

```json
"dependsOn": [
  {
    "value": ["Register for Myself", "Register for Other"],
    "questionId": 123,
    "questionBindingKey": "registrationForWhom"
  }
]
```

The `type` field is optional. If present (e.g. `"disable"`), it is used for business logic. If absent, the entry is treated as a visibility condition.


## 2. New `conditional_config` Column (Program/Field-Based Filtering)

- Add a new column `conditional_config` (JSONB, nullable) to all relevant tables (questions and sections).
- This column is for advanced program/field-based filtering only.
- **No validation or business logic changes required for this column at this time.**
- **When cloning (template → program, etc.), always clone the `conditional_config` column as-is.**

#### Format Example:
```json
[
  {
    "field": "programType",
    "operator": "in",
    "values": ["HDB", "MSD"]
  }
]
```


## 3. Validation Service

- **No changes needed** for `conditional_config`.
- `getValidationDependsOn(config)` returns **all** `dependsOn` entries from config — no filtering by type.
- `isDependencySatisfied` handles `condition.value` as an array — checks if the user's answer matches **any** value in the array.
- `normalizeQuestionConfig` passes config through unchanged — no type injection, no prefill migration.


## 4. Migration & Script Order

Run in this order:

1. `03_5_add_conditional_config_and_registration_for_whom.sql`
   - Adds `conditional_config` column to `master_question`, `template_question`, `hdb_question`
   - Adds `registration_mode` column to `hdb_program_registration`
   - Inserts `registrationForWhom` master question if missing
   - Inserts `travelPlanDependReturn` and `travelPlanDependOnward` questions
   - Updates related questions' `dependsOn` for travel flight dependency

2. `03_6_migrate_config_to_unified_depends_on_format.sql`
   - Wraps any scalar `value` fields in `dependsOn` to arrays (e.g. `"Card swipe"` → `["Card swipe"]`)
   - Merges duplicate `dependsOn` entries for the same question into a single entry with combined values

3. `04_*_template.sql` scripts — run after the above


## 5. Update/Cloning Logic

When updating or cloning templates/programs:

- Use the unified format for `dependsOn` in `config` (values always as arrays).
- Clone `conditional_config` column as-is.

## 6. Conditional Filtering When Cloning Template → Program Form

When cloning from a template to a program form (both `cloneFromTemplate` and `buildProgramForm` paths):

- The caller must supply `programMeta` — a key/value map matching the `field` key in `conditional_config` conditions (e.g. `{ "programType": "HDB" }`).
- Evaluate `conditional_config` for both sections and questions:
  - `template_form_section.conditional_config`
  - `template_question.conditional_config`
- Section-level rule:
  - If a section fails `conditional_config`, exclude the section and its full subtree (all descendant sections and their questions).
- For each question being cloned, evaluate its `conditional_config` against the `programMeta`:
  - If **all** conditions are met (or no conditions exist), include the question.
  - If **any** condition fails, exclude the question from the form entirely.
- After collecting all excluded binding keys across all sections (**two-pass approach**):
  - For every included question, strip any `dependsOn` entries whose `questionBindingKey` matches an excluded question's binding key.
- This ensures referential integrity in `dependsOn` — no surviving question has a dependency on a question that was excluded from the form.

### Evaluation Logic

- `conditional_config` format: `[{ field: string, operator: "in"|"not_in"|"equals"|"not_equals", values: any[] }]`
- Conditions within the array are AND-ed together.
- Null/empty `conditional_config` → always include.

### Implementation

- `evaluateConditionalConfig(conditionalConfig, programMeta)` — `src/common/utils/conditional-config.util.ts`
- `stripExcludedDependsOn(config, excludedBindingKeys)` — `src/common/utils/question-config.util.ts`
- `getValidationDependsOn(config)` — `src/common/utils/question-config.util.ts`
- `cloneQuestionsForSections` in `form-section.service.ts` — full two-pass cross-section approach
- `processSectionRecursivelyForProgram` in `form-section.service.ts` — per-question conditional check
- Section clone/build paths (`cloneSectionHierarchy`, section recursion) — section-level conditional checks

---

**Summary:**

- `dependsOn` values are always arrays — no scalar strings.
- Duplicate entries for the same question are merged into one.
- No runtime type injection or prefill migration — DB is the source of truth.
- Add `conditional_config` column for program/field-based filtering.
- Always clone/copy `conditional_config` during template/program cloning.
- When cloning template → program form, exclude questions whose `conditional_config` is not satisfied by `programMeta`, and strip references to excluded questions from all surviving `dependsOn` entries.
