# Registration System Specification

## 1. Overview

This system supports:

- Self registration
- Registration for others (proxy)
- Admin-created registrations (with explicit owner assignment)

The model separates:

- **Actor** (`createdBy`) — who performs the action
- **Owner** (`ownerUserId`) — who controls the registration (visible in their account)
- **Beneficiary** (`userId`) — who the registration belongs to

---

## 2. Core Model

### 2.1 Fields

| Field              | Description                                         |
| ------------------ | --------------------------------------------------- |
| `createdBy`        | Actor — who created the record (immutable)          |
| `ownerUserId`      | Current controller of the registration              |
| `userId`           | Beneficiary — final owner, nullable                 |
| `registrationMode` | `SELF` or `OTHER` — derived at creation, stored     |

---

## 3. Registration Types

| Type    | Condition                                                                       |
| ------- | ------------------------------------------------------------------------------- |
| `SELF`  | `userId === createdBy`                                                          |
| `OTHER` | `userId !== null && userId !== createdBy`                                       |
| `OTHER` | `userId === null` (unassigned proxy)                                            |
| `OTHER` | Admin creates: `createdBy = admin.id`, `ownerUserId = user.id`, `userId = null` |

---

## 4. Ownership Rules

### 4.1 Definitions

- **Actor (`createdBy`)** → immutable audit field, set once at creation
- **Owner (`ownerUserId`)** → controls access and updates, can change via transfer API
- **Beneficiary (`userId`)** → final user tied to the registration, set via assign API

### 4.2 Ownership Principle

```
ownerUserId = current controller of the registration
```

---

## 5. Creation Rules

### 5.1 Self Registration

```
createdBy    = user.id
ownerUserId  = user.id
userId       = user.id
registrationMode = SELF
```

### 5.2 Register for Others (User)

```
createdBy    = user.id
ownerUserId  = user.id
userId       = null OR targetUserId
registrationMode = OTHER
```

### 5.3 Admin Creates Registration

```
createdBy    = admin.id
ownerUserId  = user.id   // account where registration appears
userId       = null
registrationMode = OTHER
```

---

## 6. Ownership Transitions

### 6.1 Assign User

**PUT /registrations/:id/assign-user**

Request:

```json
{ "userId": "target-user-id" }
```

Rules:

- Allowed for admin OR current owner (`ownerUserId`)
- Reassignment blocked if `userId` is already set (unless admin)

Behavior:

```
registration.userId      = targetUserId
registration.ownerUserId = targetUserId
```

### 6.2 Transfer Ownership

**PUT /registrations/:id/transfer-ownership**

```
registration.ownerUserId = newOwnerId
```

---

## 7. Access Control

### 7.1 Central Rule

```
canAccess(registration, user) {
  return (
    registration.ownerUserId === user.id ||
    registration.userId === user.id ||
    user.role !== 'VIEWER'
  );
}
```

### 7.2 Enforcement

Apply to:

- Registration updates
- Profile updates
- Travel info and travel plans
- Payments
- Document uploads

---

## 8. API Design

### 8.1 Create Registration

**POST /registrations**

Rules:

- `createdBy` derived from token
- `ownerUserId` = actor (default) or explicit (admin)
- `userId` optional
- `registrationMode` derived and stored at creation

### 8.2 Update Registration

**PUT /registrations/:id**

Rules:

- Must pass `canAccess`
- Cannot modify: `createdBy`, `ownerUserId` (use transfer API), `userId` (use assign API)

---

## 9. registrationForWhom Question

A `registrationForWhom` question (`radio` type) is shown on the form when the program's `allowsProxyRegistration = true` (controlled via `conditional_config`).

Options:

- `Register for Myself`
- `Register for Other`

The answer is stored in `registration.registration_mode`.

Questions like `name`, `dob`, `mobileNumber`, `countryName` etc. have a `dependsOn` entry pointing to `registrationForWhom` with `value: ["Register for Myself", "Register for Other"]` — they are shown for both options and prefilled only for self-registration.

---

## 10. Profile Handling

```
if (registration.userId) {
  syncWithUserProfile();
}
```

No sync if `userId = null`.

---

## 11. Travel Module Rules

- Remove `createdBy` and `updatedBy` from DTOs — always derive from `req.user.id`
- Enforce `canAccess` on all travel endpoints
- Travel records always linked to `registrationId`, not `userId`

---

## 12. Query Rules

### 12.1 Ownership-Based Queries

```sql
WHERE ownerUserId = :userId
   OR userId = :userId
```

### 12.2 Avoid

```sql
-- ❌ incomplete — misses proxy registrations
WHERE userId = :userId
```

---

## 13. Payments / Invoice / History

- Always link to `registrationId`
- Use ownership fallback: `ownerUserId = :userId OR userId = :userId`

---

## 14. Analytics

Include:

- `userId`
- `ownerUserId` when `userId IS NULL`

---

## 15. Security Rules

### 15.1 Never Accept from Client

- `createdBy`
- `ownerUserId`
- `updatedBy`

### 15.2 Always Derive

```
actorUserId = req.user.id
```

---

## 16. Migration Strategy

1. Add column `owner_user_id` to `program_registration`
2. Backfill: `owner_user_id = created_by`
3. Add column `registration_mode` (enum: `SELF`, `OTHER`)
4. Existing rows with `userId = null` → treated as unassigned

---

## 17. Validation Rules

### Self

```
userId = loggedInUser.id
registrationMode = SELF
```

### On-Behalf

```
userId = null OR valid userId
registrationMode = OTHER
```

---

## 18. Test Scenarios

- Self registration — `userId = createdBy`
- Register for known user — `userId` set at creation
- Register for unknown user — `userId = null`, assigned later
- Admin creates registration with explicit `ownerUserId`
- Assign user via assign API
- Transfer ownership via transfer API
- Unauthorized update blocked by `canAccess`
- Travel access validation
- Profile sync when `userId` is set
- Query correctness — both `ownerUserId` and `userId` covered
- `registrationForWhom` question shown only when `allowsProxyRegistration = true`

---

## 19. Non-Goals

- No new beneficiary field beyond `userId`
- No forced user creation
- No breaking schema changes beyond `ownerUserId`, `registrationMode`, `registrationForWhom`
