# ERPFlow — Approval Workflow Flow (বাংলা)

**Phase:** 7 — Approval Workflow Engine  
**সংস্করণ:** 1.0  
**ভাষা:** বাংলা  
**সম্পর্কিত:** [IMPLEMENTATION_PHASES.md](../IMPLEMENTATION_PHASES.md) (Phase 7, 8)

---

## সারাংশ

ERPFlow-এ এখন **configurable approval system** আছে। যেকোনো module + action (যেমন `leave.create`, `purchase.delete`) এর জন্য admin workflow বানাতে পারে, company-wise ON/OFF করতে পারে, আর user যখন সেই action করতে চায় — তখন engine ঠিক করবে কাজ **সরাসরি execute** হবে নাকি **approval-এর জন্য pending** থাকবে।

**মূল অংশ তিনটি:**

| অংশ | দায়িত্ব |
|-----|----------|
| **Config Layer** | Workflow, steps, settings, approver resolver registry |
| **Runtime Layer** | Request, steps, approvers, payload, comments, audit |
| **ApprovalGateway** | Business module-এর entry point — approval লাগবে কিনা check করে |

**UI এখনো নেই** (Phase 8) — backend API সম্পূর্ণ ready।

---

## আর্কিটেকচার

```
Business Module (Layer 3)
        │
        ▼
  ApprovalGateway          ← "approval লাগবে?"
        │
   ┌────┴────┐
   │         │
  OFF       ON
   │         │
   ▼         ▼
তৎক্ষণাৎ    ApprovalEngine
execute         │
                ▼
         pending request
                │
                ▼
         approver actions
                │
                ▼
         executor → actual DB action
```

### মূল Service

| Service | ফাইল | কাজ |
|---------|------|-----|
| `ApprovalGateway` | `app/Platform/Services/ApprovalGateway.php` | Plugin entry — ON/OFF check, immediate vs pending |
| `ApprovalEngine` | `app/Platform/Services/ApprovalEngine.php` | Workflow CRUD, request lifecycle, step state machine |
| `ApproverResolverRegistry` | `app/Platform/Services/ApproverResolverRegistry.php` | Approver কে হবে — resolve করে |
| `ApprovalExecutorRegistry` | `app/Platform/Services/ApprovalExecutorRegistry.php` | Final approval-এর পর actual action execute |

---

## ডাটাবেস টেবিল

### Config (সেটআপ)

| টেবিল | উদ্দেশ্য |
|-------|----------|
| `approval_workflows` | Company + module-action অনুযায়ী workflow definition |
| `approval_workflow_versions` | Immutable version snapshot (পরিবর্তন হলে নতুন version) |
| `approval_steps` | প্রতিটি version-এর step template |
| `approval_settings` | Company-wise module-action-এ approval ON/OFF |
| `approver_resolvers` | Resolver strategy registry (`role`, `specific_user`, ইত্যাদি) |

### Runtime (চলমান request)

| টেবিল | উদ্দেশ্য |
|-------|----------|
| `approval_requests` | Request header — status, requester, workflow version snapshot |
| `approval_request_steps` | Runtime copy of workflow steps |
| `approval_request_approvers` | প্রতিটি step-এ resolved approver |
| `approval_payloads` | Immutable operation payload (create/update/delete data) |
| `approval_comments` | Requester/approver মন্তব্য |
| `approval_audits` | Event trail (`request.started`, `step.approved`, ইত্যাদি) |

### গুরুত্বপূর্ণ নিয়ম: Version Snapshot

Request শুরু হওয়ার সময় workflow-এর **বর্তমান version** (যেমন v3) runtime-এ copy হয় এবং `workflow_version_id` হিসেবে lock হয়। Admin পরে workflow v4-এ update করলেও **চলমান পুরনো request v3 অনুযায়ী** চলতে থাকবে।

---

## ভাগ ১ — Admin Setup Flow (একবার configure)

### ধাপ ১: Workflow তৈরি

Admin API: `POST /api/v1/approval-workflows`

উদাহরণ: Leave module-এর `create` action-এর জন্য `"Leave Create Approval"` workflow।

প্রয়োজনীয় তথ্য:
- `module_id`, `module_action_id`
- `name`
- `steps[]` — প্রতিটি step-এর config

### ধাপ ২: Steps define করা

প্রতিটি step-এ:

| ফিল্ড | মান | অর্থ |
|-------|-----|------|
| `step_order` | 1, 2, 3... | কোন step আগে, কোনটা পরে |
| `name` | "Manager Approval" | Step-এর নাম |
| `resolver_type` | `role`, `specific_user`, `custom` | কে approve করবে |
| `resolver_config` | `{ "role_id": 2 }` | Resolver-এর config |
| `mode` | `parallel` / `sequential` | Approver-দের মধ্যে কীভাবে act করবে |
| `completion_rule` | `any` / `all` | Step complete হওয়ার শর্ত |

**Step completion rule:**

| mode | completion_rule | অর্থ |
|------|-----------------|------|
| `parallel` | `any` | যেকোনো একজন approve করলেই step complete |
| `parallel` | `all` | সব assigned approver approve করতে হবে |
| `sequential` | `any` বা `all` | এক সময়ে শুধু প্রথম pending approver act করতে পারে |

**Built-in Resolver:**

| slug | কাজ | অবস্থা |
|------|-----|--------|
| `specific_user` | নির্দিষ্ট একজন user | ✅ কাজ করে |
| `role` | একটি role-এর সব member | ✅ কাজ করে |
| `custom` | `user_ids` array থেকে | ✅ কাজ করে |
| `reporting_manager` | Requester-এর manager | ⏳ stub (HRMS পরে implement করবে) |
| `department_head` | Department head | ⏳ stub (HRMS পরে implement করবে) |

Plugin নিজের resolver register করতে পারে:
```php
Platform::registerApproverResolver('my_module.custom', MyResolver::class);
```

### ধাপ ৩: Workflow update (নতুন version)

`PUT /api/v1/approval-workflows/{uuid}` — steps পরিবর্তন করলে:
- `current_version` 1 বেড়ে যায় (v1 → v2)
- নতুন `approval_workflow_versions` row তৈরি হয়
- পুরনো version **অপরিবর্তিত** থাকে
- চলমান request-গুলো পুরনো version-এ চলতে থাকে

### ধাপ ৪: Approval Setting ON করা

API: `PUT /api/v1/approval-settings`

```json
{
  "module_action_id": 5,
  "approval_enabled": true,
  "workflow_id": 1
}
```

Matrix দেখতে: `GET /api/v1/approval-settings/matrix`

**মনে রাখুন:**
- `module_actions.requires_approval` = manifest default (hint)
- `approval_settings.approval_enabled` = **runtime toggle** (এটাই Gateway দেখে)

### ধাপ ৫: Executor register (Plugin)

Final approval-এর পর actual business action চালাতে plugin executor register করে:

```php
Platform::registerApprovalExecutor('leave.requests', CreateLeaveExecutor::class);
```

Executor অবশ্যই `ApprovalExecutorInterface` implement করবে।

---

## ভাগ ২ — Runtime Flow (User action করলে)

### উদাহরণ: Employee Leave Create করতে চায়

```
Employee "Leave Create" button click করে
              │
              ▼
    LeaveService → ApprovalGateway::submit()
              │
              ▼
    approval_settings check
              │
       ┌──────┴──────┐
       │             │
    OFF (false)    ON (true)
       │             │
       ▼             ▼
  onApproved()    startRequest()
  তৎক্ষণাৎ         pending request
  execute              │
                       ▼
                 approver inbox
                       │
                       ▼
                 approve / reject
                       │
                       ▼
                 সব step OK?
                       │
                       ▼
                 executor → DB save
                       │
                       ▼
                 status = executed
```

---

### ধাপ ১: Business Module Gateway call করে

```php
$result = $this->approvalGateway->submit(new ApprovalSubmissionData(
    companyId: $companyId,
    requesterId: $user->id,
    moduleSlug: 'leave',
    actionSlug: 'create',
    operation: ApprovalOperation::Create,
    entityType: 'leave.requests',
    entityId: null,
    payloadBefore: null,
    payloadAfter: $validatedData,
    title: 'Leave Request — 3 days',
    correlationId: $idempotencyKey,  // optional
    onApproved: fn () => $this->createLeaveDirectly($validatedData),
));
```

**Payload strategy:**

| Operation | payload_before | payload_after | entity_id |
|-----------|----------------|---------------|-----------|
| `create` | `null` | নতুন data | `null` |
| `update` | পুরনো data | নতুন data | existing id |
| `delete` | current data | `{ "_deleted": true }` | existing id |

Payload **কখনো update হয় না** — পরিবর্তন চাইলে নতুন request লাগবে।

---

### ধাপ ২: Gateway — Approval লাগবে কিনা check

`ApprovalGateway::isApprovalRequired()` দেখে:
- `approval_settings.approval_enabled = true` **এবং**
- `workflow_id` set আছে

**যদি OFF:**
- `onApproved` callback সঙ্গে সঙ্গে চলে
- কোনো request তৈরি হয় না
- Return: `ApprovalGatewayResult::immediate()`

**যদি ON:**
- `ApprovalEngine::startRequest()` call হয়
- Return: `ApprovalGatewayResult::pending($request)`

---

### ধাপ ৩: Engine — Request তৈরি (Snapshot)

`startRequest()` transaction-এ যা করে:

1. `correlation_id` duplicate check (যদি দেওয়া থাকে)
2. Active workflow + current version load
3. `approval_requests` row — status: **`pending`**
4. Workflow steps → `approval_request_steps`-এ **copy**
5. Payload → `approval_payloads`-এ **immutable save**
6. `workflow_version_id` lock (যেমন v3)
7. Audit: `request.started`

এখনো **কোনো business action execute হয়নি**।

---

### ধাপ ৪: প্রথম Step Activate

1. Step 1-এর `started_at` set হয়
2. Template step-এর `resolver_type` অনুযায়ী approver resolve হয়
   - যেমন `role` + `role_id: 2` → Manager role-এর সব user
3. Resolved user-দের `approval_request_approvers`-এ assign
4. **Requester নিজেকে approver হিসেবে skip** করা হয়
5. কোনো approver resolve না হলে error

Approver-রা inbox দেখতে পারে: `GET /api/v1/approval-requests?scope=inbox`

---

### ধাপ ৫: Approver Action

| Action | API | কী হয় |
|--------|-----|--------|
| Approve | `POST .../approve` | Approver row `approved` → step completion check |
| Reject | `POST .../reject` | Step + request `rejected` → বাকি steps skip → **execute হয় না** |
| Cancel | `POST .../cancel` | শুধু **requester** cancel করতে পারে → `cancelled` |
| Delegate | `POST .../delegate` | নিজের slot অন্য user-এ transfer |
| Comment | `POST .../comments` | মন্তব্য যোগ |

---

### ধাপ ৬: Step Completion Logic

একজন approve করলে engine check করে:

```
completion_rule = "any"  → ১ জন approve = step complete
completion_rule = "all"  → সবাই approve না হওয়া পর্যন্ত pending
mode = "sequential"      → এক সময়ে শুধু প্রথম pending approver act করতে পারে
```

Step complete হলে:
- সেই step status → `approved`
- **পরের step** activate → নতুন approver assign
- শেষ step না হলে request এখনো `pending`

---

### ধাপ ৭: Final Execution

সব step approved হলে:

1. Request status → `approved`
2. `ApprovalExecutorRegistry` registered executor call করে
3. Payload অনুযায়ী actual business action (যেমন leave DB-তে insert)
4. **Success** → status `executed`, `executed_at` set
5. **Fail** → status `failed`, audit: `request.execution_failed`

---

## Request Status Lifecycle

```
pending
   │
   ├── approve (সব step) ──→ approved ──→ executed ✅
   │                              └──→ failed ❌ (executor error)
   │
   ├── reject ──────────────→ rejected ❌
   │
   └── cancel (requester) ──→ cancelled 🚫
```

---

## API Reference (সংক্ষিপ্ত)

| Method | Endpoint | কাজ |
|--------|----------|-----|
| GET | `/api/v1/approval-workflows` | Workflow list |
| POST | `/api/v1/approval-workflows` | Workflow create |
| GET | `/api/v1/approval-workflows/{uuid}` | Workflow detail |
| PUT | `/api/v1/approval-workflows/{uuid}` | Workflow update (নতুন version) |
| DELETE | `/api/v1/approval-workflows/{uuid}` | Workflow delete |
| GET | `/api/v1/approval-settings/matrix` | Settings grid |
| PUT | `/api/v1/approval-settings` | Setting ON/OFF |
| GET | `/api/v1/approval-requests` | Request list |
| GET | `/api/v1/approval-requests?scope=inbox` | Approver inbox |
| POST | `/api/v1/approval-requests/{uuid}/approve` | Approve |
| POST | `/api/v1/approval-requests/{uuid}/reject` | Reject |
| POST | `/api/v1/approval-requests/{uuid}/cancel` | Cancel |
| POST | `/api/v1/approval-requests/{uuid}/delegate` | Delegate |
| POST | `/api/v1/approval-requests/{uuid}/comments` | Comment |
| GET | `/api/v1/approver-resolvers` | Resolver dropdown list |

সব API-তে JWT auth + `X-Company-Id` header প্রয়োজন।

---

## Reminder ও Escalation (Skeleton)

Phase 7-এ job skeleton আছে — notification Phase 10-এ আসবে:

| Job | কাজ |
|-----|-----|
| `ProcessApprovalRemindersJob` | `timeout_hours`-এর আগে reminder due হলে audit log |
| `ProcessApprovalEscalationsJob` | timeout পেরিয়ে গেলে escalation audit log |

Scheduler: `routes/console.php` — hourly।

---

## সম্পূর্ণ উদাহরণ (Leave Create)

```
১. Admin workflow বানায়:
   Step 1: Manager (role resolver, any)
   Step 2: HR (specific_user, any)

২. Admin setting ON করে: leave.create → workflow link

৩. Plugin executor register করে: leave.requests

৪. Employee leave submit করে
   → Gateway: approval ON → request pending

৫. Manager inbox-এ দেখে → approve (Step 1 complete)

৬. HR inbox-এ দেখে → approve (Step 2 complete)

৭. Engine executor চালায় → leave DB-তে save

৮. Request status: executed ✅
```

---

## গুরুত্বপূর্ণ নিয়ম (চেকলিস্ট)

- [ ] Approval **OFF** → কোনো request নেই, সরাসরি execute
- [ ] Approval **ON** → business action তখনই হয় না, সব step approve হলে হয়
- [ ] Workflow পরে change → চলমান request-এ প্রভাব নেই (version snapshot)
- [ ] Payload **immutable** — পরিবর্তন = নতুন request
- [ ] Requester নিজে নিজের request approve করতে পারে না (self-skip)
- [ ] `reporting_manager` / `department_head` HRMS আসার পর কাজ করবে
- [ ] UI Phase 8-এ আসবে — এখন API/Postman দিয়ে test করা যায়

---

## Migration ও Seed চালানো

```bash
docker compose up -d
docker compose exec backend php artisan migrate --force
docker compose exec backend php artisan db:seed --class=ApproverResolverSeeder
```

---

*শেষ আপডেট: Phase 7 complete*
