348 lines
9.5 KiB
Markdown
348 lines
9.5 KiB
Markdown
# ENCOUNTER API REFERENCE
|
|
|
|
## Endpoints
|
|
|
|
### GET Encounter Detail
|
|
```
|
|
GET /api/v1/encounter/{id}
|
|
Query Parameters: includes=patient,patient-person,specialist,subspecialist
|
|
```
|
|
|
|
**Response Structure:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "OK",
|
|
"data": {
|
|
"id": 123,
|
|
"patient_id": 456,
|
|
"patient": {
|
|
"id": 456,
|
|
"number": "RM-2025-001",
|
|
"person": {
|
|
"id": 789,
|
|
"name": "John Doe",
|
|
"residentIdentityNumber": "1234567890123456"
|
|
}
|
|
},
|
|
"appointment_doctor_id": 5,
|
|
"responsible_doctor_id": 5,
|
|
"specialist_id": 10,
|
|
"subspecialist_id": 15,
|
|
"specialist": {
|
|
"id": 10,
|
|
"code": "CARDIO",
|
|
"name": "Cardiology"
|
|
},
|
|
"subspecialist": {
|
|
"id": 15,
|
|
"code": "CARDIO_ADULT",
|
|
"name": "Cardiology Adult"
|
|
},
|
|
"registeredAt": "2025-12-02T10:30:00Z",
|
|
"visitDate": "2025-12-02T10:30:00Z",
|
|
"member_number": "0000123456789",
|
|
"ref_number": "0301P123456789",
|
|
"sep_type": "1",
|
|
"participant_group_code": "1",
|
|
"paymentMethod_code": "insurance",
|
|
"vclaimReference": {
|
|
"noSep": "0301P123456789",
|
|
"tglRujukan": "2025-12-02T00:00:00Z",
|
|
"ppkDirujuk": "rssa",
|
|
"jnsPelayanan": "2",
|
|
"catatan": "Rujukan BPJS"
|
|
},
|
|
"class_code": "ambulatory",
|
|
"subClass_code": "reg",
|
|
"unit_code": "UNIT001",
|
|
"created_at": "2025-12-01T08:00:00Z",
|
|
"updated_at": "2025-12-02T10:30:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### PATCH Encounter
|
|
```
|
|
PATCH /api/v1/encounter/{id}
|
|
Content-Type: application/json
|
|
```
|
|
|
|
**Request Payload:**
|
|
```json
|
|
{
|
|
"patient_id": 456,
|
|
"appointment_doctor_code": "5",
|
|
"class_code": "ambulatory",
|
|
"subClass_code": "reg",
|
|
"unit_code": "UNIT001",
|
|
"refSource_name": "RSSA",
|
|
"refTypeCode": "bpjs",
|
|
"vclaimReference": {
|
|
"noSep": "0301P123456789",
|
|
"tglRujukan": "2025-12-02T00:00:00Z",
|
|
"ppkDirujuk": "rssa",
|
|
"jnsPelayanan": "2"
|
|
},
|
|
"paymentType": "jkn",
|
|
"paymentMethod_code": "insurance",
|
|
"specialist_id": 10,
|
|
"subspecialist_id": 15,
|
|
"member_number": "0000123456789",
|
|
"registeredAt": "2025-12-02T10:30:00.000Z",
|
|
"visitDate": "2025-12-02T10:30:00.000Z"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Encounter updated successfully",
|
|
"data": {
|
|
"id": 123,
|
|
"patient_id": 456,
|
|
"updated_at": "2025-12-02T15:45:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Handler Methods
|
|
|
|
### getFetchEncounterDetail()
|
|
**Purpose:** Load encounter data and map to form
|
|
**Trigger:** Automatically on page mount if `props.id > 0`
|
|
**Logic:**
|
|
1. GET `/api/v1/encounter/{id}?includes=patient,patient-person,specialist,subspecialist`
|
|
2. Call `mapEncounterToForm()` to transform data
|
|
3. Update `formObjects.value` with transformed data
|
|
4. Fetch doctors for selected specialist
|
|
|
|
**Logs:**
|
|
```
|
|
📥 [EDIT MODE] Loading encounter detail: {id: 123}
|
|
📥 [EDIT MODE] API Response: {success: true, data: {...}}
|
|
📋 [EDIT MODE] Mapped encounter to form: {...}
|
|
✅ [EDIT MODE] Encounter detail loaded and form mapped successfully
|
|
```
|
|
|
|
### mapEncounterToForm(encounter)
|
|
**Purpose:** Transform API response to form values
|
|
**Input:** Encounter object from GET response
|
|
**Output:** `formObjects.value` with mapped fields
|
|
|
|
**Mapping:**
|
|
```typescript
|
|
formData = {
|
|
patientName: encounter.patient.person.name,
|
|
nationalIdentity: encounter.patient.person.residentIdentityNumber,
|
|
medicalRecordNumber: encounter.patient.number,
|
|
doctorId: String(encounter.appointment_doctor_id),
|
|
subSpecialistId: specialist.code (resolved from ID),
|
|
registerDate: "YYYY-MM-DD" (from registeredAt or visitDate),
|
|
paymentType: "jkn|spm|pks|jkmm" (mapped from paymentMethod_code),
|
|
cardNumber: encounter.member_number,
|
|
sepNumber: encounter.ref_number,
|
|
sepType: encounter.sep_type,
|
|
patientCategory: encounter.participant_group_code,
|
|
sepReference: encounter.vclaimReference?.noSep
|
|
}
|
|
```
|
|
|
|
### handleSaveEncounter(formValues)
|
|
**Purpose:** Save encounter (create or update)
|
|
**Input:** Form values from validation schema
|
|
**Output:** POST/PATCH API call and navigation
|
|
|
|
**Logic:**
|
|
1. Validate patient selected
|
|
2. Build payload with transformations:
|
|
- Convert doctorId to string
|
|
- Convert specialist code to ID
|
|
- Map paymentType to paymentMethod_code
|
|
- Format dates to ISO
|
|
3. If `isEditMode`: PATCH `/api/v1/encounter/{id}`
|
|
4. Else: POST `/api/v1/encounter`
|
|
5. Success: Toast + redirect to list
|
|
6. Error: Toast with error message
|
|
|
|
**Logs:**
|
|
```
|
|
💾 [EDIT MODE] Sending PATCH request: {id: 123, payload: {...}}
|
|
💾 [ADD MODE] Sending POST request: {payload: {...}}
|
|
📤 [SAVE] API Response: {success: true, message: "OK"}
|
|
✅ [SAVE] Success - Redirecting to list page
|
|
```
|
|
|
|
---
|
|
|
|
## Data Type Mapping
|
|
|
|
### Payment Type Mapping
|
|
| Form Value | API Value | Description |
|
|
|-----------|-----------|-------------|
|
|
| jkn | insurance | BPJS (Insurance) |
|
|
| jkmm | insurance | BPJS Mandiri |
|
|
| spm | cash | Out of pocket |
|
|
| pks | membership | Partnership/Membership |
|
|
|
|
### Specialist Type Mapping
|
|
| Field | Type | Example |
|
|
|-------|------|---------|
|
|
| specialist_id | number | 10 |
|
|
| specialist.code | string | "CARDIO" |
|
|
| subspecialist_id | number | 15 |
|
|
| subspecialist.code | string | "CARDIO_ADULT" |
|
|
|
|
### Date Format Mapping
|
|
| Format | Usage | Example |
|
|
|--------|-------|---------|
|
|
| YYYY-MM-DD | Form display | "2025-12-02" |
|
|
| ISO 8601 with Z | API send | "2025-12-02T10:30:00.000Z" |
|
|
| ISO 8601 | API response | "2025-12-02T10:30:00Z" |
|
|
|
|
---
|
|
|
|
## Error Codes & Handling
|
|
|
|
| HTTP Status | Error | Handler Response |
|
|
|-------------|-------|-----------------|
|
|
| 200 | Success | Navigate to list, show success toast |
|
|
| 400 | Validation error | Show error message from API |
|
|
| 401 | Unauthorized | Redirect to login (middleware) |
|
|
| 403 | Forbidden | RBAC middleware blocks, show error page |
|
|
| 404 | Not found | Show error toast, redirect to list |
|
|
| 422 | Unprocessable | Show field-specific validation errors |
|
|
| 500 | Server error | Show generic error toast, allow retry |
|
|
|
|
---
|
|
|
|
## Testing Curl Commands
|
|
|
|
### Get Encounter Detail
|
|
```bash
|
|
curl -X GET "http://localhost:3000/api/v1/encounter/123?includes=patient,patient-person,specialist,subspecialist" \
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
-H "Content-Type: application/json"
|
|
```
|
|
|
|
### Update Encounter
|
|
```bash
|
|
curl -X PATCH "http://localhost:3000/api/v1/encounter/123" \
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"patient_id": 456,
|
|
"appointment_doctor_code": "5",
|
|
"class_code": "ambulatory",
|
|
"subClass_code": "reg",
|
|
"unit_code": "UNIT001",
|
|
"paymentType": "jkn",
|
|
"paymentMethod_code": "insurance",
|
|
"specialist_id": 10,
|
|
"subspecialist_id": 15,
|
|
"member_number": "0000123456789",
|
|
"registeredAt": "2025-12-02T10:30:00.000Z",
|
|
"visitDate": "2025-12-02T10:30:00.000Z"
|
|
}'
|
|
```
|
|
|
|
---
|
|
|
|
## Handler Flow Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ ENCOUNTER EDIT MODE │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
1. PAGE MOUNT (props.id > 0 = Edit Mode)
|
|
│
|
|
├─→ handleInit()
|
|
│ ├─ Load specialists
|
|
│ ├─ Load doctors
|
|
│ └─ Load payment types
|
|
│
|
|
├─→ getFetchEncounterDetail()
|
|
│ ├─ GET /api/v1/encounter/{id}
|
|
│ ├─ await mapEncounterToForm()
|
|
│ │ └─ formObjects.value = { ...mapped data }
|
|
│ └─ await handleFetchDoctors()
|
|
│
|
|
└─→ Entry form watches props.objects
|
|
└─ Updates form UI with formObjects data
|
|
|
|
2. USER EDITS FORM
|
|
│
|
|
└─→ Form state updates reactively
|
|
└─ Validation runs on change
|
|
|
|
3. USER CLICKS SAVE
|
|
│
|
|
└─→ handleSaveEncounter(formValues)
|
|
├─ Build payload
|
|
│ └─ Convert types, dates, payment method
|
|
├─ if isEditMode:
|
|
│ └─ PATCH /api/v1/encounter/{id}
|
|
├─ else:
|
|
│ └─ POST /api/v1/encounter
|
|
├─ Success:
|
|
│ ├─ Show success toast
|
|
│ └─ Navigate to list page
|
|
└─ Error:
|
|
├─ Show error toast
|
|
└─ Stay on page (allow retry)
|
|
```
|
|
|
|
---
|
|
|
|
## Common Issues & Solutions
|
|
|
|
### Issue: Form not populated after page load
|
|
**Check:**
|
|
1. Console shows `✅ [EDIT MODE] Encounter detail loaded` ✓
|
|
2. formObjects.value has data (check in Vue DevTools)
|
|
3. Form watches props.objects properly
|
|
|
|
**Solution:**
|
|
```typescript
|
|
// In entry.vue, ensure watch is set up:
|
|
watch(
|
|
() => props.objects,
|
|
(objects) => {
|
|
// Auto-populate form fields from props.objects
|
|
},
|
|
{ deep: true, immediate: true }
|
|
)
|
|
```
|
|
|
|
### Issue: PATCH request failing with 422
|
|
**Check:**
|
|
1. Console shows full payload being sent
|
|
2. Required fields are present
|
|
3. Data types match API expectations (string IDs, ISO dates)
|
|
|
|
**Solution:**
|
|
Look for payload fields and compare with expected structure in ENCOUNTER_API_REFERENCE.md
|
|
|
|
### Issue: Specialist not loading after edit
|
|
**Check:**
|
|
1. `subSpecialistId` is correctly set in form
|
|
2. `handleFetchDoctors(subSpecialistId)` called after mapEncounterToForm
|
|
|
|
**Solution:**
|
|
Verify specialist code to ID resolution in `getSpecialistIdsFromCode()`
|
|
|
|
---
|
|
|
|
## Performance Notes
|
|
|
|
- GET includes relationships: `patient,patient-person,specialist,subspecialist`
|
|
- No N+1 queries expected (relationships included)
|
|
- Form mapping happens once on page load
|
|
- Doctor list fetched only if specialist selected
|
|
- Debounced search if SEP number changed (500ms)
|
|
|