# Official Business — Backend Contract (ms-timekeeping)

> This document specifies the backend API endpoints for Official Business filing and approval.
> The Official Business module follows the same **frontend-orchestrated approval flow** as Travel Order and DTR Correction:
> the frontend manages approval workflow with ms-general, then sends the final result to ms-timekeeping.

---

## Module Code

| Field | Value |
|-------|-------|
| `module_code` (ms-general) | `official_business` |
| `route_prefix` (ms-timekeeping) | `official_business` |

---

## Database Columns

| Column | Type | Maps to Form Field |
|--------|------|-------------------|
| `employee_id` | string | Employee filing the OB |
| `date_filed` | date | Auto-set to today |
| `date_ob` | date | **Date of Official Business** |
| `time_start` | time | **Time From** |
| `time_end` | time | **Time To** |
| `total_hours` | decimal (5,2) | Computed total hours |
| `reason` | text | **Purpose** |
| `destination` | text (nullable) | **Destination/Office Visited** |
| `remarks` | text (nullable) | **Remarks** |
| `personal` | boolean | `false` for OB (scoped for future use) |
| `status` | string | `pending`, `approved`, `rejected` |
| `is_filing` | boolean | Always `true` for the filing flow |
| `approval_request_id` | string (nullable, indexed) | FK reference from ms-general approval request |
| `created_at` | datetime | Timestamp |
| `updated_at` | datetime | Timestamp |

---

## API Endpoints

### 1. Create Filing

```
POST /api/v1/filing/official_business
```

**Content-Type:** `application/json`

**Request Fields:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `is_filing` | boolean | yes | Always `true` for approval flow |
| `employee_id` | string | yes | Filing employee's ID |
| `date_ob` | string (date) | yes | OB date in `Y-m-d` |
| `time_start` | string (time) | yes | Start time in `H:i:s` |
| `time_end` | string (time) | yes | End time in `H:i:s` (must be after `time_start`) |
| `reason` | string | yes | Purpose of the official business |
| `destination` | string | yes | Office/destination visited |
| `remarks` | string | no | Optional remarks |
| `personal` | boolean | no | Default `false` |
| `created_by` | string | yes | Employee ID who created the record |

**Backend Behavior:**
1. Saves record with `status: 'pending'`, `is_filing: true`
2. Does NOT call ms-general — the frontend handles the approval workflow

**Success Response (201):**
```json
{
  "success": true,
  "data": {
    "id": 1,
    "employee_id": "EMP001",
    "date_filed": "2026-06-01",
    "date_ob": "2026-06-15",
    "time_start": "08:00:00",
    "time_end": "17:00:00",
    "total_hours": null,
    "reason": "Client meeting",
    "destination": "Makati Office",
    "remarks": "Bring laptop",
    "personal": false,
    "status": "pending",
    "is_filing": true,
    "approval_request_id": null,
    "created_by": "EMP001",
    "updated_by": "EMP001",
    "created_at": "2026-06-01T00:00:00.000000Z",
    "updated_at": "2026-06-01T00:00:00.000000Z"
  },
  "message": "Official business entry created successfully"
}
```

---

### 2. List (Employee's Own)

```
GET /api/v1/official_business
```

**Query Parameters:**

| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `page` | integer | 1 | Page number |
| `limit` | integer | 10 | Items per page |
| `employee_id` | string | — | Filter by employee (required for employee-facing list) |
| `status` | string | — | Filter by status (e.g. `pending`, `approved`, `rejected`) |
| `date_ob` | string (date) | — | Filter by OB date (`Y-m-d`) |
| `reason` | string | — | Filter by purpose/reason |

**Success Response:**
```json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "employee_id": "EMP001",
      "date_ob": "2026-06-15",
      "time_start": "08:00:00",
      "time_end": "17:00:00",
      "reason": "Client meeting",
      "destination": "Makati Office",
      "status": "pending",
      "is_filing": true,
      "approval_request_id": null,
      "created_at": "2026-06-01T00:00:00.000000Z",
      "updated_at": "2026-06-01T00:00:00.000000Z"
    }
  ],
  "message": "Official business entries retrieved successfully"
}
```

When paginated, the response includes `current_page`, `last_page`, `per_page`, `total` at the top level.

---

### 3. View Single

```
GET /api/v1/official_business/{id}
```

**Success Response:**
```json
{
  "success": true,
  "data": {
    "id": 1,
    "employee_id": "EMP001",
    "date_filed": "2026-06-01",
    "date_ob": "2026-06-15",
    "time_start": "08:00:00",
    "time_end": "17:00:00",
    "total_hours": null,
    "reason": "Client meeting",
    "destination": "Makati Office",
    "remarks": "Bring laptop",
    "personal": false,
    "status": "pending",
    "is_filing": true,
    "approval_request_id": null,
    "created_by": "EMP001",
    "updated_by": "EMP001",
    "created_at": "2026-06-01T00:00:00.000000Z",
    "updated_at": "2026-06-01T00:00:00.000000Z"
  },
  "message": "Official business entry retrieved successfully"
}
```

---

### 4. Finalize Approval (triggered by frontend after polling ms-general)

```
POST /api/v1/approval/official_business
```

**Request Body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | integer or array | yes | Single ID or array of IDs |
| `status` | string | yes | `"approved"` or `"rejected"` |
| `remarks` | string | no | Approval/rejection remarks |

**Example (single ID):**
```json
{
  "id": 1,
  "status": "approved"
}
```

**Example (batch):**
```json
{
  "id": [1, 2, 3],
  "status": "approved",
  "remarks": "Approved by all levels"
}
```

**Behavior:**
- Updates the official business record's status
- On approval: auto-creates a time entry linked via `official_business_id` — the OB is reflected in the DTR and prevents absent/tardy tagging
- On approval/rejection: sends an in-app notification to the employee via `NotificationService`
- Logs audit trail

**Success Response (200):**
```json
{
  "success": true,
  "data": {
    "success": true,
    "updated": [1],
    "count": 1
  },
  "message": "Official business entry approval processed successfully"
}
```

---

## Approval Flow (Frontend-Orchestrated)

The Official Business module uses the same **frontend-orchestrated approval flow** as Travel Order and DTR Correction:

```
1. Frontend calls POST /api/v1/filing/official_business
   → Backend creates record with status: 'pending'
   → Returns official_business id

2. Frontend calls ms-general to initiate approval:
   POST /hris-general/api/v1/approval-requests
   {
     "module_code": "official_business",
     "section_code": "{section_code}",
     "filed_by": "{employee_id}",
     "request_reference_id": "{official_business_id}"
   }
   → Stores returned approval_request_id on the official_business record
     (the frontend should update the record via PUT /api/v1/official_business/{id})

3. Frontend polls ms-general for status:
   GET /hris-general/api/v1/approval-requests/{id}
   GET /hris-general/api/v1/approval-requests/{id}/history

4. When terminal status is detected, frontend calls:
   POST /api/v1/approval/official_business
   { "id": {official_business_id}, "status": "approved"|"rejected" }
```

---

## ms-general Endpoints (Shared Across All Modules)

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/hris-general/api/v1/module-settings/approval-setting?module_code=official_business&section_code=...` | Fetch approval chain configuration |
| GET | `/hris-general/api/v1/approval-requests/{id}` | Get approval request with chain snapshot |
| GET | `/hris-general/api/v1/approval-requests/{id}/history` | Get approval audit trail |
| GET | `/hris-general/api/v1/approval-requests/pending?employee_id=N` | Get pending approvals for an approver |
| POST | `/hris-general/api/v1/approval-requests` | Create approval request (called by frontend) |
| POST | `/hris-general/api/v1/approval-requests/{id}/approve` | Approve current level |
| POST | `/hris-general/api/v1/approval-requests/{id}/reject` | Reject (terminal) |
| POST | `/hris-general/api/v1/approval-requests/{id}/use-alternative` | Escalate to alternative approver |

---

## Notification Types

When approval/rejection is finalized, the employee receives an in-app notification:

| Type | Trigger | Message |
|------|---------|---------|
| `official_business_approved` | OB approved | "Your Official Business request for {date_ob} has been approved." |
| `official_business_rejected` | OB rejected | "Your Official Business request for {date_ob} has been rejected." |

Notifications are stored in the `employee_notifications` table and retrieved via `GET /api/v1/notifications?employee_id={id}`.

---

## Error Responses

All endpoints return errors in this format:

```json
{
  "success": false,
  "message": "Error description",
  "error_code": 422
}
```

| HTTP Code | Meaning |
|-----------|---------|
| 201 | Created successfully |
| 200 | Success |
| 422 | Validation error (missing/invalid fields) |
| 404 | Resource not found |
| 500 | Server error |
