9.5 KiB
9.5 KiB
ENCOUNTER API REFERENCE
Endpoints
GET Encounter Detail
GET /api/v1/encounter/{id}
Query Parameters: includes=patient,patient-person,specialist,subspecialist
Response Structure:
{
"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:
{
"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:
{
"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:
- GET
/api/v1/encounter/{id}?includes=patient,patient-person,specialist,subspecialist - Call
mapEncounterToForm()to transform data - Update
formObjects.valuewith transformed data - 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:
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:
- Validate patient selected
- Build payload with transformations:
- Convert doctorId to string
- Convert specialist code to ID
- Map paymentType to paymentMethod_code
- Format dates to ISO
- If
isEditMode: PATCH/api/v1/encounter/{id} - Else: POST
/api/v1/encounter - Success: Toast + redirect to list
- 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
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
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:
- Console shows
✅ [EDIT MODE] Encounter detail loaded✓ - formObjects.value has data (check in Vue DevTools)
- Form watches props.objects properly
Solution:
// 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:
- Console shows full payload being sent
- Required fields are present
- 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:
subSpecialistIdis correctly set in formhandleFetchDoctors(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)