Compare commits

166 Commits

Author SHA1 Message Date
Hanan Askarim
e3c743aeea add api screening template 2026-06-23 15:00:26 +07:00
Hanan Askarim
43342bf361 add api poli 2026-06-22 15:00:49 +07:00
Hanan Askarim
cf648ac9ba fix birt proxy 2026-06-17 11:40:27 +07:00
sas.fajri
b7c752df84 FHM09062601IBL - fix fn_numbering_klinik reference one.m_branch to one_lab.m_branch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:06:38 +07:00
sas.fajri
0cf019ddf7 FHM08062601IBL - add M_PatientPhoto to sp_rpt_fo_001 for kartu kontrol photo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:50:07 +07:00
sas.fajri
5e1371a248 FHM08062601IBL - decrypt patient address description on delivery search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:17:38 +07:00
Hanan Askarim
b713289b82 add api birt proxy nonlab 2026-06-15 11:53:45 +07:00
sas.fajri
8e72a9f067 FHM15062601SAS - add pending task markdown 2026-06-15 11:20:04 +07:00
sas.fajri
cb45fabe1c FHM09062601IBL - unmask cashier patient search name 2026-06-12 16:23:05 +07:00
sas.fajri
5695a40fe1 FHM09062601IBL - keep payment payload backward compatible 2026-06-12 16:02:39 +07:00
sas.fajri
b653f0e987 FHM09062601IBL - move anamnesis visit info to php 2026-06-12 15:52:43 +07:00
sas.fajri
4a9406cd28 FHM09062601IBL - move patient visit info to php 2026-06-12 15:49:34 +07:00
sas.fajri
1601ec6573 FHM09062601IBL - guard patient visit search info 2026-06-12 15:42:19 +07:00
sas.fajri
567f85a0b9 FHM09062601IBL - split klinik payment before ibl 2026-06-12 15:15:47 +07:00
sas.fajri
9eb92eb340 FHM09062601IBL - add order klinik to save response 2026-06-12 14:47:43 +07:00
sas.fajri
ebd2217921 FHM09062601IBL - fix ibl load klinik tests 2026-06-12 14:26:39 +07:00
sas.fajri
17acf294ba FHM09062601IBL - cashierklinik/patient/search: PDP decrypt M_PatientName, fix bidx search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:50:52 +07:00
sas.fajri
b08ddb68b1 FHM09062601IBL - fix load_klinik tests: T_PriceIsCito AS T_TestIsCito dari ss_price_mou
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:40:29 +07:00
sas.fajri
e8f598c98d FHM09062601IBL - fix load_klinik tests: T_TestIsCito dari ss_price_mou sesuai referensi v35
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:39:29 +07:00
sas.fajri
d0547e38bb FHM09062601IBL - load_klinik: tangkap SQL error di tests query untuk debug
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:38:04 +07:00
sas.fajri
83045fac9a FHM09062601IBL - fix load_klinik tests: T_TestIsCito dari JOIN t_test bukan ss_price_mou
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:36:54 +07:00
sas.fajri
fac5dcb34d FHM09062601IBL - fix load_klinik tests: ganti db_onedev ke db_smartone, tambah schema one_lab ke ss_price_mou
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:35:55 +07:00
sas.fajri
0550497a16 FHM09062601IBL - load_klinik tests format identik registration_v37 selectPx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
667d8d2a1d FHM09062601IBL - trigger sync_order_total: update orderSubtotal+orderTotal otomatis
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
6a1bee3656 FHM09062601IBL - ibl_registration/order/save: link klinik order via klinik_number
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
c80bd8b6c1 FHM09062601IBL - ibl_registration/order/load_klinik: endpoint baru dengan decrypt PDP
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
917115684c FHM09062601IBL - getdefaultmou: support ambil MOU dari order jika orderid dikirim
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
e3be8d6b14 FHM09062601IBL - klinik/patient/search: bidx search + decrypt patient_name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
1b8e00b57e FHM09062601IBL - list_patient: decrypt patient_name dari _enc untuk tampilan asli
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:40 +07:00
sas.fajri
d57bbaec38 FHM09062601IBL - get_resume_medic: tambah vaksinasi_list dan tindakan_medis_list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
d04d8add35 FHM09062601IBL - saveorder: simpan tests ke order_detail_order + create table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
414a3765f7 FHM09062601IBL - Registrationv3: tambah search_test, mouid dari FE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
b18dfa3495 FHM09062601IBL - fix list_order_tindakan: JOIN m_doctor ke one_lab
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
39f4626cdf FHM09062601IBL - tambah fitur tindakan medis: table order_tindakan + 4 endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
e5d5dfd48a FHM09062601IBL - get_vaccines: filter px_type = PX
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
cd795497a7 FHM09062601IBL - order_vaccine: tambah kolom harga (ss_price_mou_id, bruto, disc, total)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:39 +07:00
sas.fajri
e1b91403ed FHM09062601IBL - get_vaccines: decode nat_test dan child_test jadi array
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
ce2068d24a FHM09062601IBL - get_vaccines: return format lengkap sama seperti search_test supervisor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
f1bbb2a932 FHM09062601IBL - get_vaccines: tambah harga (bruto, discountpersen, discountrp, total)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
520c77484b FHM09062601IBL - filter get_vaccines: is_packet = N di ss_price_mou
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
e3c9909c84 FHM09062601IBL - tambah kolom KIPI, observasi 15 menit, reaksi alergi ke order_vaccine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
11860743b8 FHM09062601IBL - petugas penyuntik: autocomplete dari m_staff, ganti kolom ke M_StaffID
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
77a595eba4 FHM09062601IBL - refactor get_vaccines: autocomplete jenis vaksin by order MOU, pisahkan list_order_vaccines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
6858814948 FHM09062601IBL - tambah endpoint vaksin di doctorv5/Anamnesedoctor + create order_vaccine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:38 +07:00
sas.fajri
0e2df4612a FHM09062601IBL - create m_injection_site dan m_route_vaccine di one_klinik
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
98748620dd FHM09062601IBL - fix Ttv: set orderDoctorType FORM saat insert order_doctor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
1bdb54d1c2 FHM09062601IBL - fix mcuofflineapp Preregisterapp: apply PDP compliance (mask/enc/bidx)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
f8d487079b FHM09062601IBL - update runbook PDP: field tabel, DOB VARCHAR, NIK_bidx dari IDNumber, migration steps baru
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
83bd46d521 FHM09062601IBL - update CLAUDE.md: tambah list migration PDP dan catatan DOB VARCHAR
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
49df769c58 FHM09062601IBL - alter m_patient M_PatientDOB ke VARCHAR untuk simpan nilai masked
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
9e68c1cedd FHM09062601IBL - alter mcu_preregister_patients DOB ke VARCHAR untuk simpan nilai masked
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
16316aaa31 FHM09062601IBL - fix Preregister: masking M_PatientDOB dan Mcu_PreregisterPatientsDOB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
a90b0f96c7 FHM09062601IBL - fix Preregister savecsv: terapkan PDP pada insert/update/lookup m_patient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
ece137df06 FHM09062601IBL - update CLAUDE.md: tambah catatan pola PDP NIK_bidx diisi dari IDNumber
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:37 +07:00
sas.fajri
2692c98ef2 FHM09062601IBL - fix registrationv3: IDNumber_bidx simpan ke M_PatientNIK_bidx, search e[3] pakai JSON_CONTAINS NIK_bidx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:36 +07:00
sas.fajri
7c8b1ad36b FHM09062601IBL - fix registrationv3 search: kembalikan AND antar-field, e[3] pakai M_PatientIDNumber LIKE (bukan NIK bidx)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:36 +07:00
sas.fajri
c1f874b96b FHM09062601IBL - fix registrationv3 search: ganti AND antar-field jadi OR, e[3] cari M_PatientIDNumber bukan M_PatientNIK_bidx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:33:36 +07:00
Hanan Askarim
e73fccee17 fix generate order rujukan delevery 2026-06-12 09:03:56 +07:00
Hanan Askarim
2978ecc93d update konfrimasi penerimaan rujukan 2026-06-11 15:10:42 +07:00
sas.fajri
9430b00ee6 FHM09062601IBL - registrationv3/search hapus field alamat dari response (terenkripsi)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 11:11:51 +07:00
sas.fajri
0e590f5959 FHM09062601IBL - samplingcall/search filter klinik order ganti orderIsScreening='D' ke orderIsTTV='D'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 10:59:08 +07:00
sas.fajri
710dbaaec1 FHM09062601IBL - ttv/search sesuaikan data pasien dg screening list: order.*, semua _enc field, patient_name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 10:44:13 +07:00
sas.fajri
1e87def6a7 FHM09062601IBL - buat controller Ttv (search, getttv, savettv, getsexreg) + kolom orderIsTTV
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 10:24:06 +07:00
sas.fajri
c78f53fc18 FHM09062601IBL - search() merge answer ke options: option terpilih value:true, sesuaikan format object
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 09:47:38 +07:00
sas.fajri
67bb072e0d FHM09062601IBL - simpan T_ScreeningAnswerValue sebagai JSON object bukan string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 09:46:36 +07:00
sas.fajri
655757599a FHM09062601IBL - end_session delegate ke endsession agar FE existing langsung pakai logic baru
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 09:42:22 +07:00
sas.fajri
ee3ec98f44 FHM09062601IBL - tambah endpoint endsession: simpan screening DEFAULT ke order_screening, dinamis ke t_screening_answer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 09:31:51 +07:00
sas.fajri
b45f284784 FHM09062601IBL - buat t_screening_answer, update screening/search return template+forms dinamis vs DEFAULT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 08:58:03 +07:00
sas.fajri
beb11cc40f FHM09062601IBL - update id option screening jadi unique per form (format f{formID}o{optionIdx})
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 08:16:45 +07:00
sas.fajri
2a936217c7 FHM09062601IBL - update format options screening ke array-of-objects, tambah mapping template ke clinic unit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:50:33 +07:00
sas.fajri
f39b28361e FHM09062601IBL - buat tabel m_screening_template dan m_screening_form, isi data template VAKSINASI dan KHITAN
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:41:57 +07:00
sas.fajri
270f71f5ea FHM09062601IBL - fix saveorder SQL 1064: tambah ? untuk orderM_MouID, handle param m_mouid dari FE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:30:48 +07:00
sas.fajri
25f17896d4 FHM09062601IBL - saveorder: harga dari FE (bukan dokter), mou_id dari FE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:44 +07:00
sas.fajri
71d64c6637 FHM09062601IBL - registrasi klinik: tambah endpoint searchcompany dan getmoubycompany
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:44 +07:00
sas.fajri
eed0c8fe0d FHM09062601IBL - saveorder: simpan orderM_ClinicUnitID, orderM_CompanyID, orderM_MouID
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
beac903397 FHM09062601IBL - alter order tambah orderM_CompanyID (orderM_MouID sudah ada)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
16fcf81c00 FHM09062601IBL - registrasi klinik: tambah endpoint getpoli, alter order tambah orderM_ClinicUnitID
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
e456ce6354 FHM09062601IBL - create table one_klinik.m_clinic_unit dengan data awal 4 poli
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
45e668def3 FHM09062601IBL - tambah task refactor workflow klinik SATUSEHAT readiness
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
a599f15ec2 FHM09062601IBL - screening list_patient: tambah _enc columns, decrypt PDP, foto pasien
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
ad632ec17c FHM09062601IBL - doctorv5/anamnesedoctor: tambah M_PatientPhoto dan M_PatientPhotoThumb di search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
5e3695a54b FHM09062601IBL - doctorv5/anamnesedoctor: fix regional query (regional_nm, JOIN via pro/kab/kec_cd)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:43 +07:00
sas.fajri
dc586c63f4 FHM09062601IBL - doctorv5/anamnesedoctor: fix kelurahan sub-query, tambah PDP decrypt, gunakan regional
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:42 +07:00
sas.fajri
ae28375cc3 FHM09062601IBL - samplingcall: decrypt PDP fields (nama, HP, email, DOB) di hasil search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:42 +07:00
sas.fajri
e7894e869d FHM09062601IBL - fix settingM_LocationID klinik dari 11 ke 30
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:42 +07:00
sas.fajri
5d9c170bf4 FHM09062601IBL - samplingcall: fix klinik UNION, hapus filter locationID agar semua order klinik muncul
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:42 +07:00
sas.fajri
eb4af1c67c FHM10062601IBL - tambah catatan meeting klinik internal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:42 +07:00
sas.fajri
8282acadd5 FHM09062601IBL - samplingcall: debug sementara cek sql dan params
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:41 +07:00
sas.fajri
01994365d4 FHM09062601IBL - samplingcall: tambah UNION klinik order dari one_klinik.order
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:41 +07:00
sas.fajri
9dd4afed4a FHM09062601IBL - screening: tambah getsexreg sama seperti registrationv3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:41 +07:00
sas.fajri
f1801157c2 FHM09062601IBL - screening search: hapus kelurahan sub-query, tambah PDP decrypt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:41 +07:00
sas.fajri
0c9c67d30f FHM09062601IBL - tambah _mask_dob, terapkan ke newpatient dan editpatient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:41 +07:00
sas.fajri
32131fdaad FHM09062601IBL - mask M_PatientDOB null di plain column, fix dob decrypt di search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:40 +07:00
sas.fajri
473b90b697 FHM09062601IBL - newpatient: simpan M_PatientAddressRegionalCd dan field lokasi alamat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:40 +07:00
sas.fajri
82c3ea5ff0 FHM09062601IBL - getaddress: ganti join ke tabel regional, decrypt address description
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:40 +07:00
sas.fajri
e301eedbeb FHM09062601IBL - fix search registrationv3: hapus kelurahan sub-query, samakan dg ibl_registration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:40 +07:00
sas.fajri
a5d7174b68 FHM09062601IBL - tambah method searchregion dan search_countries di klinik/Registrationv3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:16:40 +07:00
Hanan Askarim
0f54702aa7 update rujukan internal 2026-06-10 15:47:03 +07:00
sas.fajri
04591d6c32 FHM08062601IBL - rapikan separator detail hasil 2026-06-09 13:52:36 +07:00
sas.fajri
48c61fcfd7 FHM08062601IBL - kembalikan lab result ke birt 2026-06-09 11:52:46 +07:00
sas.fajri
e37512624c FHM08062601IBL - rapikan layout lab result fpdf 2026-06-09 11:31:13 +07:00
sas.fajri
de1fb927de FHM08062601IBL - alihkan lab result ke fpdf 2026-06-09 11:27:26 +07:00
sas.fajri
e3f51591a6 FHM08062601IBL - hotfix print report blank 2026-06-09 11:14:52 +07:00
sas.fajri
085a2dc14a FHM08062601IBL - perbaiki header birt proxy 2026-06-09 11:10:44 +07:00
sas.fajri
686db5ed43 FHM08062601IBL - fix table border: pakai Rect() untuk outer border, row separator via 'B' only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 09:19:25 +07:00
sas.fajri
6c7aaf0dd0 FHM08062601IBL - fix font Helvetica 10pt (match BIRT Calibri), 3cm kop gap, form_rev pojok kanan atas, fix cell indent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 09:13:11 +07:00
sas.fajri
934a779770 FHM08062601IBL - tambah footer (Validasi Oleh, Printed by, page number), fix table outer border, QR absolute di atas footer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 09:05:24 +07:00
sas.fajri
fcd125a252 FHM08062601IBL - tambah section sampling (sp_rpt_hasil_lab_sampling) dan Catatan di Rpt_lab_result
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 09:00:26 +07:00
sas.fajri
24c5d2d94f FHM08062601IBL - refactor Rpt_lab_result: pakai sp_rpt_hasil_lab, sesuaikan layout header & grouping 3 level dengan referensi BIRT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 08:57:07 +07:00
sas.fajri
910e1cd08a FHM08062601IBL - fix nama tabel s_systems ke conf_systems di Rpt_lab_result
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 08:42:23 +07:00
Hanan Askarim
1cce2b52dc Merge branch 'main' of https://devone.aplikasi.web.id/gitea/fajri/BE_IBL 2026-06-09 08:41:39 +07:00
Hanan Askarim
bff1943054 fix generate qr report di lab 2026-06-09 08:41:31 +07:00
sas.fajri
77c00e0dd0 FHM08062601IBL - fix access level db_onedev jadi public di Rpt_lab_result
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 08:40:39 +07:00
sas.fajri
c4e590d153 FHM08062601IBL - tambah Rpt_lab_result FPDF controller untuk generate lab result PDF tanpa BIRT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 08:39:21 +07:00
sas.fajri
e797013148 FHM08062601IBL - dokumentasi PDP encryption & BIRT proxy stream pattern di CLAUDE.md dan AGENTS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:46:27 +07:00
sas.fajri
212e27ff72 FHM08062601IBL - fix get_report_codes_by_group pakai kode print_transaction yg benar, tambah rpt_code/e_rpt_code di groups output untuk stream_by_code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:43:14 +07:00
sas.fajri
7e3cd75ce5 FHM08062601IBL - ganti pre_cache/delete_cache dengan proxy stream pattern: Reporturl semua kode lewat stream_by_code, tambah stream_report di Rv_patient untuk resultprintadm-v7
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:34:40 +07:00
sas.fajri
edf60f5574 FHM08062601IBL - tambah endpoint delete_cache di Rv_patient untuk hapus patient_print_cache setelah BIRT selesai load
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:31:34 +07:00
sas.fajri
3138c7f508 FHM08062601IBL - tambah endpoint pre_cache di Rv_patient untuk populate patient_print_cache sebelum BIRT dipanggil dari Vue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:28:15 +07:00
sas.fajri
0e408f2cf4 FHM08062601IBL - populate patient_print_cache di get_report_url_by_code agar BIRT baca DOB terenkripsi
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 15:56:59 +07:00
sas.fajri
1863697315 FHM08062601IBL - fix orphaned return 0 setelah replace get_normal_value di Re_px files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 15:46:03 +07:00
sas.fajri
2f162c3613 FHM08062601IBL - ganti fn_sampling_get_normal dan sp_sampling_check/fix_normal dengan PHP library Ibl_sampling_normal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 15:43:49 +07:00
sas.fajri
dcdfb0e7cc FHM08062601IBL - fallback resolve payment id dari order id di birt_proxy stream_by_code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 14:50:47 +07:00
sas.fajri
d5b358003f FHM08062601IBL - fix access level db_onedev di Birt_proxy jadi public
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 14:46:15 +07:00
sas.fajri
943f037ad9 FHM08062601IBL - tanda tangan inform consent tampilkan prefix nama suffix tanpa title
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:33:25 +07:00
sas.fajri
9eba521e2f FHM08062601IBL - fix concat nama pasien dengan title prefix suffix di inform consent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:29:40 +07:00
sas.fajri
ca1327a6c2 FHM08062601IBL - update print invoice url dinamis dan nama pasien di inform consent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:22:58 +07:00
sas.fajri
33fe960269 FHM08062601IBL - fix cashier print proxy url handling 2026-06-08 13:15:25 +07:00
sas.fajri
23c5c7c67c FHM08062601IBL - add fo patient print name 2026-06-08 12:05:16 +07:00
sas.fajri
bd0790b768 FHM08062601IBL - fix birt proxy params 2026-06-08 11:35:32 +07:00
sas.fajri
c2c9def40d FHM08062601IBL - fix proxy report token 2026-06-08 11:33:23 +07:00
sas.fajri
87c621a5fc FHM08062601IBL - secure fo print cache lifecycle 2026-06-08 11:30:35 +07:00
sas.fajri
9d224ffabf FHM08062601IBL - update fo birt cache sp 2026-06-08 11:18:01 +07:00
sas.fajri
6e0a706b34 FHM08062601IBL - add cashier report url endpoint 2026-06-08 10:33:14 +07:00
sas.fajri
1830710859 FHM08062601IBL - add untracked artefact 2026-06-08 09:03:50 +07:00
sas.fajri
68cda67c56 FHM31052601IBL - update runbook pdp: masking satu kata, controller baru, mcu staging & dashboard enc
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:11:58 +07:00
sas.fajri
065e3ebb34 FHM31052601IBL - pdp masking & enkripsi patient di controller dan SP mcu
- mask_name nama satu kata: tampil 2 char + bintang sisanya
- masking + enkripsi insert/update m_patient di Registrationv3, ibl_registration/Patient, Patientv4, setupmcuoffline-ibl/Preregister, mcuoffline/Preregisterapp
- masking insert ke mcu_preregister_patients (PatientName, KTP, NIK, Email, Hp)
- search patient pakai bidx, decrypt setelah query di mcuoffline/Preregisterapp
- matching existing patient ganti LIKE ke bidx search
- SP sp_upsert_mcu_patient_by_preregister_id & sp_upsert_mcu_patient_by_mgm_mcuid JOIN m_patient ambil _enc, simpan ke one_lab_dashboard.mcu_patient
- ALTER mcu_patient.Mcu_PatientName dan Mcu_PatientDOB ke TEXT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:10:15 +07:00
sas.fajri
8c49b3356f FHM31052601IBL - tambah prompt dan checklist implementasi ke IBL production server
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:19:49 +07:00
sas.fajri
6ec3f338ee FHM31052601IBL - Report.php: auto populate cache + fetch_birt_pdf saat show=Y
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:18:52 +07:00
sas.fajri
620c8b051d FHM31052601IBL - update runbook: disk space warning, patient_print_cache, sp_rpt_t_002_eng
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:16:29 +07:00
sas.fajri
d4ecd7f06d FHM31052601IBL - populate decrypt cache sebelum semua BIRT/PDF fetch
- Ibl_patient_decrypt: tambah fetch_birt_pdf() + pre_cache_and_get_url()
- Reporturl.php: auto pre-cache sebelum return URL atau fetch PDF
- Rv_patient.php: pre_cache sebelum return URL ke frontend
- tgram/Hasil.php: fetch_birt_pdf() via dl_report()
- Qr_report_uploader.php: populate/delete cache wrapping download_file()
- Ibl_merge_report_gateway.php: populate/delete cache wrapping Go merge service call
- send_email.php: populate_birt_cache() + delete_birt_cache() untuk email attachment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:04:36 +07:00
sas.fajri
a88360b1b1 FHM31052601IBL - update runbook: dokumentasi BIRT proxy + FPDF decrypt strategy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:50:14 +07:00
sas.fajri
5c9daffb38 FHM31052601IBL - FPDF controllers: decrypt PII via Ibl_patient_decrypt library
- Ibl_patient_decrypt: helper populate/delete patient_print_cache + decrypt_row
- Inform_consent, Medical_checkup_report: decrypt langsung dari _enc (direct SQL)
- Kartu_kontrol, Rpt_t_002, Rpt_t_002_eng: populate cache sebelum call SP,
  delete cache setelah SP selesai

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:49:45 +07:00
sas.fajri
d7930d5dbc FHM31052601IBL - BIRT proxy + 5 SP header decrypt via patient_print_cache
- Birt_proxy.php: decrypt PII sebelum call BIRT, cache 5 menit
- 5 SP (hasil_header, _2, _eng, fo_001, card_patient): tambah LEFT JOIN
  ke patient_print_cache dengan COALESCE fallback ke masked data
- SP signature tidak berubah, .rptdesign tidak perlu diupdate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:33:02 +07:00
sas.fajri
34d90c95b6 FHM31052601IBL - sampling & klinik controllers: decrypt PII pasien untuk pengambilan sampel
- samplinglab-v15, samplingradiodiagnostic-v5, samplingelectromedis-v5,
  doctorclinicv2: search via bidx, nolab search tanpa nama, decrypt di hasil
- sampling-lab-mobile-cpone-v10: decrypt nama/HP/email/DOB/NIP
- klinik/Registrationv3: search bidx (nama/HP/DOB/NIK), hapus address search, decrypt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:04:20 +07:00
sas.fajri
09c5f70284 FHM31052601IBL - fix mask_patient_plaintext: cursor-based pagination, pisahkan masking nama
Nama ditangani remask_patient_name.php (decrypt dari _enc).
Script ini handle HP/email/alamat/NIK/POB dengan cursor-based
agar tidak infinite loop pada nama pendek satu kata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:53:17 +07:00
sas.fajri
90c156e51a FHM31052601IBL - strip PII (patient_name/address/phone/email) dari order_log sebelum INSERT
Data pasien tidak perlu masuk log — identitas sudah terenkripsi di m_patient
dan bisa di-trace via T_OrderHeaderID → M_PatientID.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:52:58 +07:00
sas.fajri
de7444d5d5 FHM31052601IBL - drop _enc hasil lab: nilai klinis bukan PII, trigger butuh plaintext
t_orderdetail, t_orderheader, so_resultentry*, member_eligible tidak dienkripsi.
Perlindungan via enkripsi identitas pasien (m_patient) + access control.
Hanya t_orderdelivery (email/HP delivery) yang tetap dienkripsi.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:49:37 +07:00
sas.fajri
c1b9891727 FHM31052601IBL - update runbook production: lengkapi semua step dan troubleshooting
Tambah: step truncate log_patient, format masking terbaru,
troubleshooting disk full + MySQL crash, controller sprint berikutnya.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:37:42 +07:00
sas.fajri
18501d07b8 FHM31052601IBL - batalkan enkripsi mcu_resume_results JSON
JSON tidak mengandung PII langsung (nama/NIK/DOB/alamat).
Enkripsi akan memberatkan global MCU report.
Data source (t_orderdetail) sudah dienkripsi.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:26:57 +07:00
sas.fajri
f744a25be8 FHM31052601IBL - tambah runbook implementasi enkripsi PII untuk production
Dokumentasi lengkap urutan eksekusi, field yang dienkripsi,
format masking, disk space requirement, dan restore procedure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:24:24 +07:00
sas.fajri
f667050200 FHM31052601IBL - update format masking nama: kata pertama penuh + inisial kata berikutnya
"FAJRI HARDHITA" → "FAJRI H*******" lebih readable untuk operasional.
Script remask_patient_name.php untuk re-apply ke data yang sudah dimasking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:23:19 +07:00
sas.fajri
ab7ed1c667 FHM31052601IBL - script migrasi NIK bidx dan address enc terpisah
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:19:55 +07:00
sas.fajri
a2d69d1618 FHM31052601IBL - search patient by nama/HP/DOB/NIK, hapus address bidx, tambah NIK bidx
- Search sekarang: nama, HP, DOB, NIK (alamat dihapus - boros disk)
- Tambah M_PatientNIK_bidx untuk search by NIK
- Migration script: NIK bidx + hapus address bidx dari m_patientaddress

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:15:33 +07:00
sas.fajri
5350ab51cc FHM31052601IBL - Patientv4: masterdata pasien tampil data lengkap (decrypt _enc)
- search() pakai trigram bidx, return data terdekripsi
- save()/newpatient(): enkripsi + masking plaintext
- getaddress(): dekripsi alamat
- savenewaddress()/saveeditaddress(): enkripsi + masking alamat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:55:43 +07:00
sas.fajri
82640c3d3b FHM31052601IBL - Patient add_new/edit: tulis masked value ke kolom plaintext lama
Kolom lama (M_PatientName, HP, Email, dll) kini menyimpan nilai masked.
Data asli tetap aman di _enc. Konsisten dengan bulk masking script.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:49:09 +07:00
sas.fajri
e990609523 FHM31052601IBL - script masking kolom plaintext PII m_patient & m_patientaddress
Semua 300+ controller otomatis tampilkan data termasking tanpa perlu
diupdate satu-satu. Data asli tetap aman di kolom _enc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:47:29 +07:00
sas.fajri
6c0394aea3 FHM31052601IBL - migration script enkripsi t_orderdelivery destination
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:32:54 +07:00
sas.fajri
17a788baac FHM31052601IBL - update FO registration controllers: decrypt PII sebelum return response
- Payment, History, Delivery: load ibl_encryptor, decrypt Name/Email/HP
- Order, Order copy: decrypt patient_name di get_header & get_order_header
- Order: pre-fetch decrypt email/HP sebelum UNION delivery query
- Order: enkripsi T_OrderDeliveryDestination saat INSERT, decrypt saat SELECT
- SQL: tambah kolom T_OrderDeliveryDestination_enc
- migrate_encrypt_results: tambah migrasi t_orderdelivery

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:31:54 +07:00
sas.fajri
c63afddaa0 FHM31052601IBL - update trigger m_patient & m_patientaddress pakai _enc di log JSON
Ganti field PII plaintext (Name, HP, Email, DOB, NIK, IDNumber, dll)
dengan field _enc di JSON log_patient. Trigger m_patient_bu tetap
UPPER-kan M_PatientName untuk backward compat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:20:23 +07:00
sas.fajri
2d7151b154 FHM31052601IBL - fix syntax migrate_encrypt_results.php compat PHP 7.2
Ganti arrow function fn() ke closure biasa karena server pakai PHP 7.2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:12:58 +07:00
sas.fajri
c410d7bbd9 FHM31052601IBL - implementasi enkripsi PII pasien dan data medis (UU PDP)
- Tambah .env loader di index.php untuk IBL_ENCRYPT_KEY dan IBL_ENCRYPT_SEARCH_KEY
- Library Ibl_encryptor: AES-256-GCM encrypt/decrypt + trigram blind index untuk partial search
- SQL migration: tambah kolom _enc dan _bidx di 16 tabel (m_patient, m_patientaddress, hasil lab, log)
- Script backup_pdp_tables.sh: backup tabel terdampak sebelum migrasi
- Script migrate_encrypt_patient.php: enkripsi batch 178K data PII pasien
- Script migrate_encrypt_results.php: enkripsi data medis hasil lab dan log
- Patient.php: search via trigram blind index, add_new/edit enkripsi sebelum save

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:07:42 +07:00
sas.fajri
cf8ef0e590 FHM29052601IBL - add GET /report/qr/{id} preview endpoint dari qr_printout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 16:33:00 +07:00
sas.fajri
a3f9e04787 FHM29052601IBL - simplify stream_from_qr_printout tanpa get_order_header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 16:02:46 +07:00
sas.fajri
84e0d60d23 FHM29052601IBL - add merge_from_qr endpoint via qr_printout URLs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:54:16 +07:00
sas.fajri
fd9511171b FHM29052601IBL - implement ibl_merge_report_service Go service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:39:51 +07:00
240 changed files with 86453 additions and 68246 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
.DS_Store
.env
# Added by code-review-graph
.code-review-graph/
build/
services/ibl_merge_report_service/ibl-merge-report-service

View File

@@ -10,6 +10,74 @@
- Before every `commit` and `push`, always check first whether local branch needs to pull/rebase from remote.
- To upload to IBL, run `bash scripts/upload_ibl_committed_files.sh`. Only run this when the user explicitly asks to upload to IBL. Do not run automatically after commit/push.
## PDP Encryption & BIRT Report
UU PDP No. 27/2022 mengharuskan enkripsi PII pasien. `M_PatientDOB`, `M_PatientName`, dll
di-mask NULL di kolom plain, nilai asli ada di kolom `_enc` (AES-256-GCM).
### Pola wajib: PHP Proxy Stream
BIRT membaca dari tabel `patient_print_cache`. Cache harus di-populate PHP sebelum BIRT
dipanggil, dan dihapus segera setelah PDF di-stream.
```
FE → PHP proxy → populate cache → fetch BIRT → delete cache → stream PDF
```
**Jangan pernah** buat FE langsung build URL `/birt/frameset?...` lalu set ke iframe/window.open
tanpa lewat PHP proxy — cache tidak akan pernah terisi, data pasien kosong di report.
### Endpoint proxy yang tersedia
```
GET /one-api-lab/tools/birt_proxy/stream_by_code
Params: token, report_code (print_transaction code), PT_OrderHeaderID
Return: binary PDF
```
Untuk BE yang perlu return URL ke FE, gunakan `Reporturl` library:
```php
$this->load->library('reporturl');
[$ok, $url] = $this->reporturl->get_report_url_by_code($report_code, [
'PT_OrderHeaderID' => $order_id,
'PUsername' => $username,
]);
// $url sudah mengarah ke stream_by_code — tidak perlu populate/delete cache manual
```
### Daftar print_transaction code
| Group | Print siap | Print belum siap | Email |
|-------|-----------|-----------------|-------|
| LAB | `LAB-RESULT-P-01` | `LAB-RESULT-NP-01` | `LAB-RESULT-P-02` |
| LAB (Inggris) | `LABEN-RESULT-P-01` | `LABEN-RESULT-NP-01` | `LABEN-RESULT-P-02` |
| Mikro (terlampir) | `MIKRO-RESULT-P-01` | `MIKRO-RESULT-NP-01` | `MIKRO-RESULT-P-02` |
| Mikro (tidak terlampir) | `LAB-RESULT-P-01` | `LAB-RESULT-NP-01` | `LAB-RESULT-P-02` |
| Mikro (Inggris) | `MIKROEN-RESULT-P-01` | `MIKROEN-RESULT-NP-01` | `MIKROEN-RESULT-P-02` |
| FNA | `FNA-RESULT-P-01` | `FNA-RESULT-NP-01` | `FNA-RESULT-P-02` |
| Patologi Anatomi | `PA-RESULT-P-01` | `PA-RESULT-NP-01` | `PA-RESULT-P-02` |
| Papsmear | `PAP-RESULT-P-01` | `PAP-RESULT-NP-01` | `PAP-RESULT-P-02` |
| Pap Smear LCP | `PAPLCP-RESULT-P-01` | `PAPLCP-RESULT-NP-01` | `PAPLCP-RESULT-P-02` |
| Pap Smear LCP (Inggris) | `PAPLEN-RESULT-P-01` | `PAPLEN-RESULT-NP-01` | `PAPLEN-RESULT-P-02` |
| Preparasi Sperma | `PS-RESULT-P-01` | `PS-RESULT-NP-01` | `PS-RESULT-P-02` |
| DFI | `DFI-RESULT-P-01` | `DFI-RESULT-NP-01` | `DFI-RESULT-P-02` |
| Cytologi | `CT-RESULT-P-01` | `CT-RESULT-NP-01` | `CT-RESULT-P-02` |
### Deteksi modul yang belum difix
Cari pola ini di FE:
```
/birt/frameset?__report=
```
Kalau ditemukan di JS/Vue yang langsung set ke iframe/object/window.open tanpa lewat PHP
proxy, itu harus diganti ke `stream_by_code`.
### Library terkait
- `application/libraries/Ibl_patient_decrypt.php` — populate/delete `patient_print_cache`
- `application/libraries/Ibl_sampling_normal.php` — pengganti `fn_sampling_get_normal` MySQL function (semua `Re_px.php` sudah diupdate)
- `application/controllers/tools/Birt_proxy.php` — proxy stream handler
## graphify
This project has a graphify knowledge graph at graphify-out/.

View File

@@ -19,6 +19,78 @@
- SSH command: `ssh -i /Users/fajrihardhitamurti/id_rsa -o BatchMode=yes -o StrictHostKeyChecking=accept-new one@10.9.20.31`
- BIRT reports path: `/home/one/project/birt/onelab/reports/`
## PDP Encryption & BIRT Report
Dokumentasi lengkap ada di **`docs/pdp-encryption-runbook.md`**.
Poin penting yang sering terlewat:
- `M_PatientNIK_bidx` diisi dari **`M_PatientIDNumber`**, bukan kolom `M_PatientNIK`
- `M_PatientDOB` bertipe `VARCHAR(20)` (bukan DATE) agar masked value `**-**-YYYY` tersimpan
- `Mcu_PreregisterPatientsDOB` juga `VARCHAR(20)` — tidak punya `_enc`, data asli di `m_patient`
### Pola wajib: PHP Proxy Stream
BIRT membaca dari tabel `patient_print_cache`. Cache harus di-populate PHP sebelum BIRT
dipanggil, dan dihapus segera setelah PDF di-stream.
```
FE → PHP proxy → populate cache → fetch BIRT → delete cache → stream PDF
```
**Jangan pernah** buat FE langsung build URL `/birt/frameset?...` lalu set ke iframe/window.open
tanpa lewat PHP proxy — cache tidak akan pernah terisi, data pasien kosong di report.
### Endpoint proxy yang tersedia
```
GET /one-api-lab/tools/birt_proxy/stream_by_code
Params: token, report_code (print_transaction code), PT_OrderHeaderID
Return: binary PDF
```
Untuk BE yang perlu return URL ke FE, gunakan `Reporturl` library:
```php
$this->load->library('reporturl');
[$ok, $url] = $this->reporturl->get_report_url_by_code($report_code, [
'PT_OrderHeaderID' => $order_id,
'PUsername' => $username,
]);
// $url sudah mengarah ke stream_by_code — tidak perlu populate/delete cache manual
```
### Daftar print_transaction code
| Group | Print siap | Print belum siap | Email |
|-------|-----------|-----------------|-------|
| LAB | `LAB-RESULT-P-01` | `LAB-RESULT-NP-01` | `LAB-RESULT-P-02` |
| LAB (Inggris) | `LABEN-RESULT-P-01` | `LABEN-RESULT-NP-01` | `LABEN-RESULT-P-02` |
| Mikro (terlampir) | `MIKRO-RESULT-P-01` | `MIKRO-RESULT-NP-01` | `MIKRO-RESULT-P-02` |
| Mikro (tidak terlampir) | `LAB-RESULT-P-01` | `LAB-RESULT-NP-01` | `LAB-RESULT-P-02` |
| Mikro (Inggris) | `MIKROEN-RESULT-P-01` | `MIKROEN-RESULT-NP-01` | `MIKROEN-RESULT-P-02` |
| FNA | `FNA-RESULT-P-01` | `FNA-RESULT-NP-01` | `FNA-RESULT-P-02` |
| Patologi Anatomi | `PA-RESULT-P-01` | `PA-RESULT-NP-01` | `PA-RESULT-P-02` |
| Papsmear | `PAP-RESULT-P-01` | `PAP-RESULT-NP-01` | `PAP-RESULT-P-02` |
| Pap Smear LCP | `PAPLCP-RESULT-P-01` | `PAPLCP-RESULT-NP-01` | `PAPLCP-RESULT-P-02` |
| Pap Smear LCP (Inggris) | `PAPLEN-RESULT-P-01` | `PAPLEN-RESULT-NP-01` | `PAPLEN-RESULT-P-02` |
| Preparasi Sperma | `PS-RESULT-P-01` | `PS-RESULT-NP-01` | `PS-RESULT-P-02` |
| DFI | `DFI-RESULT-P-01` | `DFI-RESULT-NP-01` | `DFI-RESULT-P-02` |
| Cytologi | `CT-RESULT-P-01` | `CT-RESULT-NP-01` | `CT-RESULT-P-02` |
### Deteksi modul yang belum difix
Cari pola ini di FE:
```
/birt/frameset?__report=
```
Kalau ditemukan di JS/Vue yang langsung set ke iframe/object/window.open tanpa lewat PHP
proxy, itu harus diganti ke `stream_by_code`.
### Library terkait
- `application/libraries/Ibl_patient_decrypt.php` — populate/delete `patient_print_cache`
- `application/libraries/Ibl_sampling_normal.php` — pengganti `fn_sampling_get_normal` MySQL function (semua `Re_px.php` sudah diupdate)
- `application/controllers/tools/Birt_proxy.php` — proxy stream handler
## graphify
This project has a graphify knowledge graph at graphify-out/.

View File

@@ -52,4 +52,5 @@ defined('BASEPATH') OR exit('No direct script access allowed');
$route['default_controller'] = 'welcome';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;
$route['report/qr/(:num)'] = 'tools/merge_report/preview_qr/$1';
$route['report/(:num)'] = 'tools/merge_report/preview/$1';

View File

@@ -124,11 +124,20 @@ EOF;
}
public function v3($orderHeaderID)
{
$sql = "SELECT QR_PrintOutReportURL
FROM qr_printout
WHERE QR_PrintOutT_OrderHeaderID = ?
AND QR_PrintOutIsActive = 1
ORDER BY QR_PrintOutID DESC
$sql = "SELECT IFNULL(qp.QR_PrintOutVerifyURL, '') AS verify_url
FROM one_lab.qr_printout qp
JOIN one_lab.t_orderheader_group_result r
ON r.T_OrderHeaderGroupResultT_OrderHeaderID = qp.QR_PrintOutT_OrderHeaderID
AND r.T_OrderHeaderGroupResultGroup_ResultID = qp.QR_PrintOutGroup_ResultID
AND r.T_OrderHeaderGroupResultT_TestID = qp.QR_PrintOutT_TestID
JOIN one_lab.t_orderheader_group_result_details d
ON d.T_OrderHeaderGroupResultDetailsT_OrderHeaderGroupResultID = r.T_OrderHeaderGroupResultID
WHERE d.T_OrderHeaderGroupResultDetailsT_OrderHeaderID = ?
AND d.T_OrderHeaderGroupResultDetailsIsActive = 'Y'
AND r.T_OrderHeaderGroupResultIsActive = 'Y'
AND qp.QR_PrintOutIsActive = 1
AND IFNULL(qp.QR_PrintOutVerifyURL, '') <> ''
ORDER BY qp.QR_PrintOutID DESC
LIMIT 1";
$rs = $this->get_one_row($sql, array($orderHeaderID));
if ($rs["status"] == -1) {
@@ -140,8 +149,8 @@ EOF;
exit;
}
$reportUrl = $rs["data"]["QR_PrintOutReportURL"];
$img_qrcode = $this->post("http://localhost/charts/qrtext.php", $reportUrl);
$verifyUrl = $rs["data"]["verify_url"];
$img_qrcode = $this->post("http://localhost/charts/qrtext.php", $verifyUrl);
header("Content-type: image/png");
echo $img_qrcode;
exit;
@@ -149,7 +158,7 @@ EOF;
public function v3_nonlab($resultEntryID)
{
$sql = " SELECT
qp.QR_PrintOutReportURL
IFNULL(qp.QR_PrintOutVerifyURL, '') AS verify_url
FROM one_lab.so_resultentry se
JOIN one_lab.t_orderheader oh
ON oh.T_OrderHeaderID = se.SO_ResultEntryT_OrderHeaderID
@@ -170,8 +179,8 @@ EOF;
exit;
}
$reportUrl = $rs["data"]["QR_PrintOutReportURL"];
$img_qrcode = $this->post("http://localhost/charts/qrtext.php", $reportUrl);
$verifyUrl = $rs["data"]["verify_url"];
$img_qrcode = $this->post("http://localhost/charts/qrtext.php", $verifyUrl);
header("Content-type: image/png");
echo $img_qrcode;
exit;

View File

@@ -7,6 +7,7 @@ class Patient extends MY_Controller
{
parent::__construct();
$this->db = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
function index()
@@ -40,20 +41,33 @@ class Patient extends MY_Controller
$where = " orderIsActive = 'Y' $filter_date";
$bidx_where = '';
if ($search != "") {
$where .= " AND (orderNumber LIKE '{$search}' OR M_PatientName LIKE '{$search}')";
$raw_search = trim($prm['search']);
$tokens = $this->ibl_encryptor->query_tokens($raw_search);
if ($tokens) {
$bidx_conds = implode(' AND ', array_map(function($h) {
return "JSON_CONTAINS(M_PatientName_bidx, '\"$h\"')";
}, $tokens));
$bidx_where = " AND (orderNumber LIKE '{$search}' OR ({$bidx_conds}))";
} else {
$bidx_where = " AND orderNumber LIKE '{$search}'";
}
$where .= $bidx_where;
}
$sql_total = "SELECT COUNT(*) as total FROM (
SELECT `order`.*,S_MenuUrl,
DATE_FORMAT(orderDate, '%d-%m-%Y %H:%i') as order_date,
CONCAT(M_TitleName,'. ',M_PatientName) as patient_fullname,
M_PatientName_enc as patient_name_enc,
M_PatientName as patient_name_masked,
M_PatientPrefix, M_PatientSuffix, M_TitleName,
IFNULL(T_OrderHeaderLabNumber,'-') as labnumber
FROM one_klinik.order
JOIN m_patient ON orderM_PatientID = M_PatientID
AND M_PatientIsActive = 'Y'
JOIN s_menu ON S_MenuName = 'Registration' AND S_MenuIsActive = 'Y'
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
AND M_TitleIsActive = 'Y'
LEFT JOIN t_orderheader ON orderT_OrderHeaderID = T_OrderHeaderID
WHERE $where
@@ -74,13 +88,15 @@ class Patient extends MY_Controller
$sql = "SELECT * FROM (
SELECT `order`.*,S_MenuUrl,
DATE_FORMAT(orderDate, '%d-%m-%Y %H:%i') as order_date,
CONCAT(M_TitleName,'. ',M_PatientName) as patient_fullname,
M_PatientName_enc as patient_name_enc,
M_PatientName as patient_name_masked,
M_PatientPrefix, M_PatientSuffix, M_TitleName,
IFNULL(T_OrderHeaderLabNumber,'-') as labnumber
FROM one_klinik.order
JOIN m_patient ON orderM_PatientID = M_PatientID
AND M_PatientIsActive = 'Y'
JOIN s_menu ON S_MenuName = 'Registration' AND S_MenuIsActive = 'Y'
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
AND M_TitleIsActive = 'Y'
LEFT JOIN t_orderheader ON orderT_OrderHeaderID = T_OrderHeaderID
WHERE $where
@@ -89,12 +105,17 @@ class Patient extends MY_Controller
limit 0, $tot_count";
$qry = $this->db->query($sql);
$last_query = $this->db->last_query();
// echo $last_query;
// exit;
if ($qry) {
$rows = $qry->result_array();
$enc = $this->ibl_encryptor;
$rows = array_map(function($row) use ($enc) {
$name = $enc->decrypt($row['patient_name_enc'] ?? '') ?: $row['patient_name_masked'];
$title = $row['M_TitleName'] ? $row['M_TitleName'] . '. ' : '';
$prefix = $row['M_PatientPrefix'] ? $row['M_PatientPrefix'] . ' ' : '';
$suffix = $row['M_PatientSuffix'] ? ' ' . $row['M_PatientSuffix'] : '';
$row['patient_fullname'] = trim($title . $prefix . $name . $suffix);
unset($row['patient_name_enc'], $row['patient_name_masked']);
return $row;
}, $qry->result_array());
} else {
$this->sys_error_db("Select order error", $this->db);
exit;
@@ -232,11 +253,45 @@ class Patient extends MY_Controller
$row_results = [];
$rtn_mou = [];
$sql = "SELECT M_CompanyID,
$orderid = intval($prm['orderid'] ?? 0);
if ($orderid) {
$sql = "SELECT M_CompanyID, M_CompanyName, m.M_MouID as settingM_MouID
FROM one_klinik.`order` o
JOIN m_mou m ON o.orderM_MouID = m.M_MouID
JOIN m_company ON m.M_MouM_CompanyID = M_CompanyID
WHERE o.orderID = ?
LIMIT 1";
$qry_order = $this->db->query($sql, [$orderid]);
if ($qry_order && $qry_order->num_rows() > 0) {
$order_row = $qry_order->row_array();
$mous = $this->db->query(
"SELECT M_MouID, M_MouName FROM m_mou
WHERE M_MouIsActive = 'Y' AND M_MouIsApproved = 'Y' AND M_MouIsReleased = 'Y'
AND M_MouStartDate <= date(now()) AND M_MouEndDate >= date(now())
AND M_MouM_CompanyID = ?",
[$order_row['M_CompanyID']]
)->result_array();
$row_results[] = [
'M_CompanyID' => $order_row['M_CompanyID'],
'M_CompanyName' => $order_row['M_CompanyName'],
'mous' => $mous,
];
foreach ($mous as $v) {
if ($v['M_MouID'] == $order_row['settingM_MouID']) {
$rtn_mou = $v;
}
}
$this->sys_ok(['total_display' => 1, 'records' => $row_results, 'mou' => $rtn_mou]);
exit;
}
}
$sql = "SELECT M_CompanyID,
M_CompanyName, '' as mous, settingM_MouID
FROM m_company
JOIN one_klinik.setting ON settingIsActive = 'Y'
JOIN m_mou ON M_MouID = settingM_MouID AND M_MouM_CompanyID = M_CompanyID AND
JOIN m_mou ON M_MouID = settingM_MouID AND M_MouM_CompanyID = M_CompanyID AND
M_MouIsActive = 'Y' AND M_MouIsApproved = 'Y' AND M_MouIsReleased = 'Y'
AND M_MouStartDate <= date(now()) AND M_MouEndDate >= date(now())
WHERE M_CompanyIsActive = 'Y'

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
<?php
class Ttv extends MY_Controller
{
public function index()
{
echo "TTV API";
}
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->db_oneklinik = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
// -----------------------------------------------------------------------
// POST /klinik/ttv/search
// Listing order yang sudah selesai screening (orderIsScreening='D')
// -----------------------------------------------------------------------
public function search()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$prm = $this->sys_input;
$limit = 20;
$offset = (max(1, intval($prm['current_page'] ?? 1)) - 1) * $limit;
$where = ["o.orderIsScreening = 'D'", "o.orderIsActive = 'Y'"];
$binds = [];
// Filter tanggal
$start_date = $prm['start_date'] ?? date('Y-m-d');
$where[] = "DATE(o.orderDate) = ?";
$binds[] = $start_date;
// Filter status TTV
$status = $prm['status'] ?? '';
if ($status !== '') {
$where[] = "o.orderIsTTV = ?";
$binds[] = $status;
}
// Filter noreg
$noreg = trim($prm['noreg'] ?? '');
if ($noreg !== '') {
$where[] = "p.M_PatientNoReg LIKE ?";
$binds[] = '%' . $noreg . '%';
}
// Filter nama / HP via trigram index (PDP-safe)
$search = trim($prm['search'] ?? '');
if ($search !== '') {
$where[] = "(p.M_PatientName_bidx LIKE ? OR p.M_PatientHP_bidx LIKE ?)";
$binds[] = '%' . $search . '%';
$binds[] = '%' . $search . '%';
}
$where_sql = implode(' AND ', $where);
$sql = "SELECT
'N' AS divider,
p.M_PatientName, p.M_PatientName_enc,
p.M_PatientHP, p.M_PatientHP_enc,
p.M_PatientDOB, p.M_PatientDOB_enc,
p.M_PatientEmail, p.M_PatientEmail_enc,
p.M_PatientPhone, p.M_PatientPhone_enc,
p.M_PatientPOB, p.M_PatientPOB_enc,
p.M_PatientIDNumber, p.M_PatientIDNumber_enc,
p.M_PatientNIK, p.M_PatientNIK_enc,
p.M_PatientPhoto,
p.M_PatientPhotoThumb,
p.M_PatientNoReg,
p.M_PatientJob,
p.M_PatientM_SexID,
p.M_PatientM_TitleID,
p.M_PatientM_IdTypeID,
o.*,
DATE_FORMAT(o.orderDate, '%d-%m-%Y') AS date_order,
'' AS kode_status,
s.M_SexName,
t.M_TitleName
FROM one_klinik.`order` o
JOIN m_patient p ON p.M_PatientID = o.orderM_PatientID AND p.M_PatientIsActive = 'Y'
JOIN m_sex s ON s.M_SexID = p.M_PatientM_SexID
JOIN m_title t ON t.M_TitleID = p.M_PatientM_TitleID
WHERE $where_sql
ORDER BY o.orderDate ASC
LIMIT $limit OFFSET $offset";
$query = $this->db_oneklinik->query($sql, $binds);
if (!$query) {
$this->sys_error_db("ttv search", $this->db_oneklinik);
return;
}
$rows = $query->result_array();
$enc = $this->ibl_encryptor;
foreach ($rows as $k => $v) {
$rows[$k]['M_PatientName'] = $enc->decrypt($v['M_PatientName_enc'] ?? '') ?: $v['M_PatientName'];
$rows[$k]['M_PatientHP'] = $enc->decrypt($v['M_PatientHP_enc'] ?? '') ?: $v['M_PatientHP'];
$rows[$k]['M_PatientDOB'] = $enc->decrypt($v['M_PatientDOB_enc'] ?? '') ?: $v['M_PatientDOB'];
$rows[$k]['M_PatientEmail'] = $enc->decrypt($v['M_PatientEmail_enc'] ?? '') ?: $v['M_PatientEmail'];
$rows[$k]['M_PatientPhone'] = $enc->decrypt($v['M_PatientPhone_enc'] ?? '') ?: $v['M_PatientPhone'];
$rows[$k]['M_PatientPOB'] = $enc->decrypt($v['M_PatientPOB_enc'] ?? '') ?: $v['M_PatientPOB'];
$rows[$k]['M_PatientIDNumber'] = $enc->decrypt($v['M_PatientIDNumber_enc'] ?? '') ?: $v['M_PatientIDNumber'];
$rows[$k]['M_PatientNIK'] = $enc->decrypt($v['M_PatientNIK_enc'] ?? '') ?: $v['M_PatientNIK'];
$rows[$k]['patient_name'] = trim(($v['M_TitleName'] ?? '') . ' ' . $rows[$k]['M_PatientName']);
foreach (array_keys($rows[$k]) as $col) {
if (substr($col, -4) === '_enc') unset($rows[$k][$col]);
}
}
$this->sys_ok(['total' => count($rows), 'records' => $rows]);
}
// -----------------------------------------------------------------------
// POST /klinik/ttv/getttv
// Load data TTV yang sudah pernah disimpan untuk satu order
// -----------------------------------------------------------------------
public function getttv()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$prm = $this->sys_input;
$orderid = intval($prm['orderid'] ?? 0);
if (!$orderid) {
$this->sys_error("orderid required");
return;
}
$row = $this->db_oneklinik->query(
"SELECT orderDoctorVitalSign, orderDoctorSaran AS xnote
FROM one_klinik.order_doctor
WHERE orderDoctorOrderID = ?
ORDER BY orderDoctorID DESC LIMIT 1",
[$orderid]
)->row_array();
$fisiks = null;
$xnote = '';
if ($row) {
$fisiks = $row['orderDoctorVitalSign']
? json_decode($row['orderDoctorVitalSign'], true)
: null;
$xnote = $row['xnote'] ?? '';
}
$this->sys_ok(['fisiks' => $fisiks, 'xnote' => $xnote]);
}
// -----------------------------------------------------------------------
// POST /klinik/ttv/savettv
// Simpan TTV ke order_doctor + order_tanda_vital, set orderIsTTV='D'
// -----------------------------------------------------------------------
public function savettv()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$prm = $this->sys_input;
$userID = $this->sys_user['M_UserID'];
$orderid = intval($prm['orderid'] ?? 0);
$fisiks = $prm['fisiks'] ?? [];
$xnote = $prm['xnote'] ?? '';
if (!$orderid) {
$this->sys_error("orderid required");
return;
}
$fisiks_json = json_encode($fisiks);
// 1. Upsert order_doctor
$exists = $this->db_oneklinik->query(
"SELECT orderDoctorID FROM one_klinik.order_doctor WHERE orderDoctorOrderID = ? LIMIT 1",
[$orderid]
)->row_array();
if ($exists) {
$ok = $this->db_oneklinik->query(
"UPDATE one_klinik.order_doctor
SET orderDoctorVitalSign = ?,
orderDoctorSaran = ?,
orderDoctorLastUpdated = NOW()
WHERE orderDoctorOrderID = ?",
[$fisiks_json, $xnote, $orderid]
);
} else {
$ok = $this->db_oneklinik->query(
"INSERT INTO one_klinik.order_doctor
(orderDoctorOrderID, orderDoctorVitalSign, orderDoctorSaran,
orderDoctorType, orderDoctorIsActive, orderDoctorUserID, orderDoctorCreated)
VALUES (?, ?, ?, 'FORM', 'Y', ?, NOW())",
[$orderid, $fisiks_json, $xnote, $userID]
);
}
if (!$ok) {
$this->sys_error_db("upsert order_doctor", $this->db_oneklinik);
return;
}
// 2. Parse fisiks → nilai terstruktur untuk order_tanda_vital
$ttv = [
'pulse' => 0,
'sistole' => 0,
'diastole' => 0,
'temperature' => 0,
'weight' => 0,
'height' => 0,
'saturation' => 0,
];
foreach ((array)$fisiks as $item) {
$code = $item['id_code'] ?? '';
$value = trim($item['value'] ?? '');
switch ($code) {
case 'tanda_vital_1': $ttv['pulse'] = intval($value); break;
case 'tanda_vital_5':
$parts = explode('/', $value);
$ttv['sistole'] = intval($parts[0] ?? 0);
$ttv['diastole'] = intval($parts[1] ?? 0);
break;
case 'tanda_vital_6': $ttv['temperature'] = intval($value); break;
case 'tanda_vital_7': $ttv['saturation'] = intval($value); break;
case 'status_gizi_1': $ttv['weight'] = intval($value); break;
case 'status_gizi_2': $ttv['height'] = intval($value); break;
}
}
// 3. Upsert order_tanda_vital
$tv_exists = $this->db_oneklinik->query(
"SELECT orderTandaVitalID FROM one_klinik.order_tanda_vital WHERE orderTandaVitalOrderID = ? LIMIT 1",
[$orderid]
)->row_array();
if ($tv_exists) {
$this->db_oneklinik->query(
"UPDATE one_klinik.order_tanda_vital SET
orderTandaVitalPulse = ?,
orderTandaVitalSistole = ?,
orderTandaVitalDiastole = ?,
orderTandaVitalTemperature = ?,
orderTandaVitalWeight = ?,
orderTandaVitalHeight = ?,
orderTandaVitalSaturation = ?,
orderTandaVitalUserID = ?,
orderTandaVitalLastUpdated = NOW()
WHERE orderTandaVitalOrderID = ?",
[$ttv['pulse'], $ttv['sistole'], $ttv['diastole'],
$ttv['temperature'], $ttv['weight'], $ttv['height'],
$ttv['saturation'], $userID, $orderid]
);
} else {
$this->db_oneklinik->query(
"INSERT INTO one_klinik.order_tanda_vital
(orderTandaVitalOrderID, orderTandaVitalPulse, orderTandaVitalSistole,
orderTandaVitalDiastole, orderTandaVitalTemperature, orderTandaVitalWeight,
orderTandaVitalHeight, orderTandaVitalSaturation,
orderTandaVitalIsActive, orderTandaVitalUserID, orderTandaVitalCreated)
VALUES (?,?,?,?,?,?,?,?,'Y',?,NOW())",
[$orderid, $ttv['pulse'], $ttv['sistole'], $ttv['diastole'],
$ttv['temperature'], $ttv['weight'], $ttv['height'],
$ttv['saturation'], $userID]
);
}
// 4. Update status order
$this->db_oneklinik->query(
"UPDATE one_klinik.`order` SET orderIsTTV = 'D', orderUserID = ? WHERE orderID = ?",
[$userID, $orderid]
);
$this->sys_ok(['process' => 'OK']);
}
// -----------------------------------------------------------------------
// POST /klinik/ttv/getsexreg
// Return kartuidentitass, sexes, titles, religions
// -----------------------------------------------------------------------
public function getsexreg()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$rows = [];
$rows['kartuidentitass'] = $this->db_onedev->query(
"SELECT * FROM m_idtype WHERE M_IdTypeIsActive = 'Y'"
)->result_array();
$rows['sexes'] = $this->db_onedev->query(
"SELECT * FROM m_sex WHERE M_SexIsActive = 'Y'"
)->result_array();
$rows['titles'] = $this->db_onedev->query(
"SELECT * FROM m_title WHERE M_TitleIsActive = 'Y'"
)->result_array();
$rows['religions'] = $this->db_onedev->query(
"SELECT * FROM m_religion WHERE M_ReligionIsActive = 'Y'"
)->result_array();
$this->sys_ok($rows);
}
}

View File

@@ -10,7 +10,7 @@ class Patient extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
//$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
public function add_notes($orderid){
@@ -61,62 +61,66 @@ class Patient extends MY_Controller
*/
$number_limit = 10;
$number_offset = ($prm['current_page'] - 1) * $number_limit ;
$where = " ( DATE(orderDate) = '{$startdate}' ) AND ";
if($search != ''){
$where = "( M_PatientName LIKE '%{$search}%' OR orderNumber LIKE '%{$search}%' ) AND ";
if(strlen($search) == 11){
$number_offset = ($prm['current_page'] - 1) * $number_limit;
$where = " ( DATE(orderDate) BETWEEN '{$startdate}' AND '{$enddate}' ) AND ";
if ($search != '') {
if (strlen($search) == 11) {
$where = "orderNumber = '{$search}' AND ";
} else {
$tokens = $this->ibl_encryptor->query_tokens($search);
if ($tokens) {
$bidx_conds = implode(' AND ', array_map(function($h) {
return "JSON_CONTAINS(M_PatientName_bidx, '\"$h\"')";
}, $tokens));
$where = "( orderNumber LIKE '%{$search}%' OR ({$bidx_conds}) ) AND ";
} else {
$where = "orderNumber LIKE '%{$search}%' AND ";
}
}
}
$sql = " SELECT count(*) as total
$sql = "SELECT count(*) as total
FROM one_klinik.`order`
JOIN m_patient ON orderM_PatientID = M_PatientID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_sex ON M_PatientM_SexID = M_SexID
WHERE
$where
( ('{$status}' = 'N' AND orderIsLunas = 'N') OR ('{$status}' = 'Y' AND orderIsLunas = 'Y') )";
$query = $this->db_onedev->query($sql, $sql_param);
//echo $this->db_onedev->last_query();
$query = $this->db_onedev->query($sql);
$tot_count = 0;
$tot_page = 0;
$tot_page = 0;
if ($query) {
$tot_count = $query->result_array()[0]["total"];
$tot_page = ceil($tot_count/$number_limit);
$tot_page = ceil($tot_count / $number_limit);
} else {
$this->sys_error_db("t_samplestorage count", $this->db_onedev);
$this->sys_error_db("patient count", $this->db_onedev);
exit;
}
$sql = "SELECT orderID,
}
$sql = "SELECT orderID,
orderDate,
orderNumber,
orderM_PatientID,
M_PatientNoReg,
orderKeluhan,
DATE_FORMAT(orderDate,'%d-%m-%Y %H:%i') as order_date,
CONCAT(M_TitleName,'. ',M_PatientName) as M_PatientName,
CONCAT(M_TitleLangName,'. ',M_PatientName) as M_PatientName_eng,
M_TitleName,
M_PatientName_enc,
M_PatientName AS patient_name_masked,
M_PatientPrefix, M_PatientSuffix,
M_TitleName, M_TitleLangName,
orderTotal as totalbill,
0 as paid,
0 as unpaid,
orderIsLunas as flaglunas,
'' as notes,
100 as mindp_percent,
100 as mindp_percent,
settingPriceDefault as mindp_amount,
0 as F_BillDetailID
FROM one_klinik.`order`
JOIN m_patient ON orderM_PatientID = M_PatientID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_sex ON M_PatientM_SexID = M_SexID
JOIN one_klinik.`setting` ON settingIsActive = 'Y'
WHERE
@@ -124,23 +128,36 @@ class Patient extends MY_Controller
( ('{$status}' = 'N' AND orderIsLunas = 'N') OR ('{$status}' = 'Y' AND orderIsLunas = 'Y') )
GROUP BY orderID
ORDER BY orderID ASC
limit $number_limit offset $number_offset";
//echo $sql;
$query = $this->db_onedev->query($sql, $sql_param);
//echo $this->db_onedev->last_query();
$rows = $query->result_array();
if($rows){
foreach($rows as $k => $v){
$sql = "SELECT IFNULL(SUM(PaymentTotal),0) as total
FROM one_klinik.payment
WHERE
PaymentOrderID = ? AND PaymentIsActive = 'Y'";
$data_payment = $this->db_onedev->query($sql, array($v['orderID']))->row();
$unpaid = $v['totalbill'] - $data_payment->total;
$rows[$k]['unpaid'] = $unpaid;
$rows[$k]['paid'] = $data_payment->total;
LIMIT $number_limit OFFSET $number_offset";
$rows[$k]['notes'] = $this->add_notes($v['orderID']);
$query = $this->db_onedev->query($sql);
if (!$query) {
$this->sys_error_db("patient rows", $this->db_onedev);
exit;
}
$rows = $query->result_array();
$enc = $this->ibl_encryptor;
if ($rows) {
foreach ($rows as $k => $v) {
$p_name = $enc->decrypt($v['M_PatientName_enc'] ?? '') ?: $v['patient_name_masked'];
$title = $v['M_TitleName'] ? $v['M_TitleName'] . '. ' : '';
$title_e = $v['M_TitleLangName'] ? $v['M_TitleLangName'] . '. ': '';
$prefix = $v['M_PatientPrefix'] ? $v['M_PatientPrefix'] . ' ' : '';
$suffix = $v['M_PatientSuffix'] ? ' ' . $v['M_PatientSuffix'] : '';
$rows[$k]['M_PatientName'] = trim($title . $prefix . $p_name . $suffix);
$rows[$k]['M_PatientName_eng'] = trim($title_e . $prefix . $p_name . $suffix);
unset($rows[$k]['M_PatientName_enc'], $rows[$k]['patient_name_masked'],
$rows[$k]['M_PatientPrefix'], $rows[$k]['M_PatientSuffix'],
$rows[$k]['M_TitleLangName']);
$data_payment = $this->db_onedev->query(
"SELECT IFNULL(SUM(PaymentTotal),0) as total FROM one_klinik.payment
WHERE PaymentOrderID = ? AND PaymentIsActive = 'Y'",
[$v['orderID']]
)->row();
$rows[$k]['unpaid'] = $v['totalbill'] - $data_payment->total;
$rows[$k]['paid'] = $data_payment->total;
$rows[$k]['notes'] = $this->add_notes($v['orderID']);
}
}

View File

@@ -14,6 +14,7 @@ class Anamnesedoctor extends MY_Controller
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->db_oneklinik = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
$this->IP_SOCKET_IO = "localhost";
}
@@ -35,17 +36,27 @@ class Anamnesedoctor extends MY_Controller
'N' divider,
M_PatientID,
M_PatientNoReg,
M_PatientPhoto,
M_PatientPhotoThumb,
M_PatientPrefix,
M_PatientName,
M_PatientName_enc,
M_PatientSuffix,
M_PatientHP,
M_PatientHP_enc,
M_PatientEmail,
M_PatientEmail_enc,
M_PatientPOB,
M_PatientPOB_enc,
M_PatientPhone,
M_PatientPhone_enc,
M_PatientIDNumber,
M_PatientIDNumber_enc,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as M_PatientDOB,
M_PatientDOB_enc,
M_PatientNote,
M_PatientNIK,
M_PatientNIK_enc,
M_PatientJabatan,
M_PatientKedudukan,
M_PatientPJ,
@@ -61,9 +72,12 @@ class Anamnesedoctor extends MY_Controller
M_IdTypeName,
M_PatientIDNumber,
IF(ISNULL(M_PatientSuspendID),'active','suspend' ) as status,
M_PatientAddressM_KelurahanID M_KelurahanID,
0 M_DistrictID,
0 M_CityID,
M_PatientAddressM_KelurahanID M_KelurahanID,
M_PatientAddressRegionalCd,
M_PatientAddressDescription,
M_PatientAddressDescription_enc,
0 M_DistrictID,
0 M_CityID,
0 M_ProvinceID
FROM one_klinik.order
JOIN m_patient ON M_PatientID = orderM_PatientID AND M_PatientIsActive = 'Y'
@@ -86,40 +100,90 @@ class Anamnesedoctor extends MY_Controller
$rows = $query->result_array();
foreach ($rows as $k => $v) {
$rows[$k]['M_PatientName'] = stripslashes($rows[$k]['M_PatientName']);
$rows[$k]['M_PatientAddressDescription'] = stripslashes($v['M_PatientAddressDescription']);
$patient_name = str_replace("'", "\\'", $prm['M_PatientName']);
$sql = "SELECT *, concat('{$rows[$k]['M_PatientAddressDescription']}', '\n\n',
m_kelurahanname, ', ',
m_districtname,'\n',
m_cityname, ', ',
m_provincename) as xaddress
FROM m_kelurahan
JOIN m_district ON M_KelurahanM_DistrictID = M_DistrictID
JOIN m_city ON M_DistrictM_CityID = M_CityID
JOIN m_province ON M_CityM_ProvinceID = M_ProvinceID
WHERE
M_KelurahanID = {$v['M_KelurahanID']} ";
//echo $sql;
$row_address = $this->db_onedev->query($sql)->row_array();
$rows[$k]['M_PatientAddress'] = stripslashes($row_address['xaddress']);
$rows[$k]['M_DistrictID'] = $row_address['M_DistrictID'];
$rows[$k]['M_CityID'] = $row_address['M_CityID'];
$rows[$k]['M_ProvinceID'] = $row_address['M_ProvinceID'];
$info = $this->db_onedev->query("SELECT fn_fo_patient_visit(?) info", [$v['M_PatientID']])->row();
$rows[$k]['info'] = json_decode($info->info);
}
$enc = $this->ibl_encryptor;
$rows[$k]['M_PatientName'] = $enc->decrypt($v['M_PatientName_enc'] ?? '') ?: stripslashes($v['M_PatientName']);
$rows[$k]['M_PatientHP'] = $enc->decrypt($v['M_PatientHP_enc'] ?? '') ?: $v['M_PatientHP'];
$rows[$k]['M_PatientEmail'] = $enc->decrypt($v['M_PatientEmail_enc'] ?? '') ?: $v['M_PatientEmail'];
$rows[$k]['M_PatientPOB'] = $enc->decrypt($v['M_PatientPOB_enc'] ?? '') ?: $v['M_PatientPOB'];
$rows[$k]['M_PatientPhone'] = $enc->decrypt($v['M_PatientPhone_enc'] ?? '') ?: $v['M_PatientPhone'];
$rows[$k]['M_PatientIDNumber'] = $enc->decrypt($v['M_PatientIDNumber_enc'] ?? '') ?: $v['M_PatientIDNumber'];
$rows[$k]['M_PatientDOB'] = $enc->decrypt($v['M_PatientDOB_enc'] ?? '') ?: $v['M_PatientDOB'];
$rows[$k]['M_PatientNIK'] = $enc->decrypt($v['M_PatientNIK_enc'] ?? '') ?: $v['M_PatientNIK'];
$rows[$k]['M_PatientAddressDescription'] = $enc->decrypt($v['M_PatientAddressDescription_enc'] ?? '') ?: stripslashes($v['M_PatientAddressDescription'] ?? '');
$reg_cd = $v['M_PatientAddressRegionalCd'] ?? '';
if ($reg_cd) {
$reg = $this->db_onedev->query(
"SELECT r_kel.regional_nm as kel_name, r_kec.regional_nm as kec_name, r_kab.regional_nm as kab_name, r_pro.regional_nm as pro_name
FROM regional r_kel
LEFT JOIN regional r_kec ON r_kec.regional_cd = CONCAT(r_kel.pro_cd, r_kel.kab_cd, r_kel.kec_cd, '000')
LEFT JOIN regional r_kab ON r_kab.regional_cd = CONCAT(r_kel.pro_cd, r_kel.kab_cd, '000', '000')
LEFT JOIN regional r_pro ON r_pro.regional_cd = CONCAT(r_kel.pro_cd, '00', '000', '000')
WHERE r_kel.regional_cd = ?", [$reg_cd]
)->row_array();
$rows[$k]['M_PatientAddress'] = $rows[$k]['M_PatientAddressDescription'] . "\n\n" .
implode(', ', array_filter([$reg['kel_name'] ?? '', $reg['kec_name'] ?? '', $reg['kab_name'] ?? '', $reg['pro_name'] ?? '']));
} else {
$rows[$k]['M_PatientAddress'] = $rows[$k]['M_PatientAddressDescription'];
}
$rows[$k]['info'] = $this->build_patient_visit_info($v['M_PatientID'], $rows[$k]['M_PatientDOB']);
}
$result = array("total" => 1, "records" => $rows, "sql" => $this->db_onedev->last_query());
$this->sys_ok($result);
} else {
$this->sys_error_db("m_patient rows", $this->db_onedev);
exit;
}
}
function get_data()
}
}
protected function build_patient_visit_info($patient_id, $patient_dob)
{
$visit = 1;
$birthday = 'N';
$visit_query = $this->db_onedev->query(
"SELECT COUNT(DISTINCT T_OrderHeaderID) AS n
FROM t_orderheader
JOIN t_orderdetail ON T_OrderHeaderID = T_OrderDetailT_OrderHeaderID AND T_OrderDetailIsActive = 'Y'
WHERE T_OrderHeaderIsActive = 'Y'
AND T_OrderHeaderM_PatientID = ?",
[$patient_id]
);
if ($visit_query) {
$visit_row = $visit_query->row_array();
$visit += (int) ($visit_row['n'] ?? 0);
}
$init_visit_query = $this->db_onedev->query(
"SELECT M_PatientInitialVisit
FROM m_patient
WHERE M_PatientID = ?",
[$patient_id]
);
if ($init_visit_query) {
$init_visit_row = $init_visit_query->row_array();
if (!empty($init_visit_row['M_PatientInitialVisit'])) {
$visit += (int) $init_visit_row['M_PatientInitialVisit'];
}
}
$dob_time = empty($patient_dob) ? false : strtotime($patient_dob);
if ($dob_time !== false) {
$birthday = date('m-d', $dob_time) === date('m-d') ? 'Y' : 'N';
}
return json_decode(json_encode([
'visit' => $visit,
'birthday' => $birthday,
]));
}
function get_data()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
@@ -1785,34 +1849,38 @@ class Anamnesedoctor extends MY_Controller
$status = $prm['status'];
$sql = "SELECT 'N' divider,
CONCAT(M_TitleName,' ',IF(ISNULL(M_PatientPrefix),'',CONCAT(M_PatientPrefix,' ')),M_PatientName,IF(ISNULL(M_PatientSuffix),'',CONCAT(M_PatientSuffix,' '))) as patient_name,
`order`.*,DATE_FORMAT(orderDate,'%d-%m-%Y') as date_order,
'' as kode_status, '' as status
`order`.*,
DATE_FORMAT(orderDate,'%d-%m-%Y') as date_order,
'' as kode_status, '' as status,
M_PatientName as patient_name_masked,
M_PatientName_enc as patient_name_enc,
M_PatientPrefix,
M_PatientSuffix,
M_TitleName
FROM one_klinik.`order`
JOIN m_patient ON orderM_PatientID = M_PatientID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
LEFT JOIN s_menu ON ( S_MenuUrl LIKE CONCAT('%test/vuex/one-klinik-fo-registration-v') OR S_MenuName = 'Periksa dokter')
WHERE
orderIsActive = 'Y' AND DATE(orderDate) = ? AND orderIsScreening = 'D' AND orderIsCheck = ?
WHERE
orderIsActive = 'Y' AND DATE(orderDate) = ? AND orderIsScreening = 'D' AND orderIsCheck = ?
LIMIT $number_limit offset $number_offset";
//echo $sql;
$query = $this->db_oneklinik->query($sql, array($xdate, $status));
//echo $this->db_oneklinik->last_query();
if ($query) {
$rows = $query->result_array();
foreach ($rows as $key => $value) {
$sql = "SELECT
FROM one_klinik.order_status
WHERE
orderStatusOrderID = ?
ORDER BY ";
}
$enc = $this->ibl_encryptor;
$rows = array_map(function($row) use ($enc) {
$name = $enc->decrypt($row['patient_name_enc'] ?? '') ?: $row['patient_name_masked'];
$prefix = $row['M_PatientPrefix'] ? $row['M_PatientPrefix'] . ' ' : '';
$suffix = $row['M_PatientSuffix'] ? ' ' . $row['M_PatientSuffix'] : '';
$title = $row['M_TitleName'] ? $row['M_TitleName'] . ' ' : '';
$row['patient_name'] = trim($title . $prefix . $name . $suffix);
unset($row['patient_name_masked'], $row['patient_name_enc']);
return $row;
}, $query->result_array());
$result = array("total" => $tot_page, "records" => $rows, "sql" => $this->db_onedev->last_query());
$result = array("total" => count($rows), "records" => $rows);
$this->sys_ok($result);
} else {
$this->sys_error_db("m_patient rows", $this->db_onedev);
$this->sys_error_db("list_patient", $this->db_oneklinik);
exit;
}
}
@@ -2368,6 +2436,8 @@ function get_resume_medic(){
$receipt = [];
$saran = [];
$tindakan = [];
$vaksinasi_list = [];
$tindakan_medis_list = [];
$doctor = [];
$pemeriksaan_penunjang = [];
@@ -2562,6 +2632,40 @@ function get_resume_medic(){
$receipt[] = $data_periksa_doctor['text']['receipt'];
$saran[] = $data_periksa_doctor['text']['saran'];
$tindakan[] = $data_periksa_doctor['text']['tindakan'];
// Vaksinasi per order
$qv = $this->db_oneklinik->query(
"SELECT ov.*,
t.T_TestName as jenis_vaksin_name,
t.T_TestSasCode as jenis_vaksin_code,
ins.M_InjectionSiteName as injection_site_name,
rv.M_RouteVaccineName as route_name,
s.M_StaffName as petugas_name
FROM one_klinik.order_vaccine ov
LEFT JOIN one_lab.t_test t ON ov.orderVaccineT_TestID = t.T_TestID
LEFT JOIN one_klinik.m_injection_site ins ON ov.orderVaccineInjectionSiteCode = ins.M_InjectionSiteCode
LEFT JOIN one_klinik.m_route_vaccine rv ON ov.orderVaccineRouteCode = rv.M_RouteVaccineCode
LEFT JOIN one_lab.m_staff s ON ov.orderVaccinePetugasM_StaffID = s.M_StaffID
WHERE ov.orderVaccineOrderID = ? AND ov.orderVaccineIsActive = 'Y'
ORDER BY ov.orderVaccineCreated ASC",
[$value['orderID']]
);
$vaksinasi_list[] = $qv ? $qv->result_array() : [];
// Tindakan medis per order
$qt = $this->db_oneklinik->query(
"SELECT ot.*,
t.T_TestName as jenis_tindakan_name,
t.T_TestSasCode as jenis_tindakan_code,
d.M_DoctorName as dokter_name
FROM one_klinik.order_tindakan ot
LEFT JOIN one_lab.t_test t ON ot.orderTindakanT_TestID = t.T_TestID
LEFT JOIN one_lab.m_doctor d ON ot.orderTindakanM_DoctorID = d.M_DoctorID
WHERE ot.orderTindakanOrderID = ? AND ot.orderTindakanIsActive = 'Y'
ORDER BY ot.orderTindakanCreated ASC",
[$value['orderID']]
);
$tindakan_medis_list[] = $qt ? $qt->result_array() : [];
}
$data_orders['date_orders'] = $date_orders;
@@ -2598,6 +2702,8 @@ function get_resume_medic(){
$data_orders['receipt'] = $receipt;
$data_orders['saran'] = $saran;
$data_orders['tindakan'] = $tindakan;
$data_orders['vaksinasi_list'] = $vaksinasi_list;
$data_orders['tindakan_medis_list'] = $tindakan_medis_list;
$data_orders['pemeriksaan_penunjang'] = $pemeriksaan_penunjang;
$this->sys_ok($data_orders);
@@ -3096,5 +3202,491 @@ function get_data_order_anamnesis($orderID){
return array('text'=>$doctor_text,'form'=>$doctor_form);
}
// ─── Vaksin ────────────────────────────────────────────────────────────────
// Master statis: injection_sites, routes (dipanggil sekali saat form dibuka)
function get_vaccine_masters()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
// Lokasi suntik
$q = $this->db_oneklinik->query(
"SELECT M_InjectionSiteCode as code, M_InjectionSiteName as name
FROM one_klinik.m_injection_site
WHERE M_InjectionSiteIsActive = 'Y'
ORDER BY M_InjectionSiteID"
);
if (!$q) { $this->sys_error_db("get m_injection_site", $this->db_oneklinik); exit; }
$result['injection_sites'] = $q->result_array();
// Rute vaksin
$q = $this->db_oneklinik->query(
"SELECT M_RouteVaccineCode as code, M_RouteVaccineName as name
FROM one_klinik.m_route_vaccine
WHERE M_RouteVaccineIsActive = 'Y'
ORDER BY M_RouteVaccineID"
);
if (!$q) { $this->sys_error_db("get m_route_vaccine", $this->db_oneklinik); exit; }
$result['routes'] = $q->result_array();
$this->sys_ok($result);
exit;
}
// Autocomplete petugas penyuntik dari one_lab.m_staff, param: search
function get_vaccine_petugas()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$search = trim($this->sys_input['search'] ?? '');
$search_esc = $this->db_onedev->escape_str($search);
$sql = "SELECT M_StaffID as id, M_StaffName as name, M_StaffCode as code
FROM one_lab.m_staff
WHERE M_StaffIsActive = 'Y'
AND M_StaffName LIKE CONCAT('%', '{$search_esc}', '%')
ORDER BY M_StaffName
LIMIT 30";
$q = $this->db_onedev->query($sql);
if (!$q) { $this->sys_error_db("get m_staff", $this->db_onedev); exit; }
$this->sys_ok(['records' => $q->result_array()]);
exit;
}
// Autocomplete jenis vaksin: filter by MOU order, param: orderid + search
function get_vaccines()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$orderid = intval($prm['orderid'] ?? 0);
$search = trim($prm['search'] ?? '');
if (!$orderid) { $this->sys_error("orderid required"); exit; }
// Ambil MOU dari order
$order_row = $this->db_oneklinik->query(
"SELECT orderM_MouID FROM one_klinik.`order` WHERE orderID = ? LIMIT 1",
[$orderid]
)->row_array();
if (!$order_row || !$order_row['orderM_MouID']) {
$this->sys_ok(['records' => []]);
exit;
}
$mou_id = intval($order_row['orderM_MouID']);
$search_esc = $this->db_onedev->escape_str($search);
$sql = "SELECT
spm.Ss_PriceMouID as ss_price_mou_id,
spm.Ss_PriceMouM_MouID as mouid,
0 as xid,
spm.Nat_TestID as nat_testid,
spm.nat_test,
spm.is_packet,
spm.packet_id,
spm.px_type as type,
spm.T_TestID as pxid,
t.T_TestCode as pxcode,
t.T_TestSasCode as pxsascode,
t.T_TestName as test_name,
CONCAT(t.T_TestSasCode,' ',t.T_TestName) as pxname,
t.T_TestIsResult as isresult,
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount')) as bruto,
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) as discountpersen,
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp')) as discountrp,
if(JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) <> 0,
(((JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) / 100)
* JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount')))
+ JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp'))),
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp'))
) as discount,
( JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount'))
- ((JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) / 100)
* JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount')))
- JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp'))
) as total,
spm.child_test
FROM one_lab.ss_price_mou spm
JOIN one_lab.t_test t ON spm.T_TestID = t.T_TestID AND t.T_TestIsActive = 'Y'
WHERE spm.Ss_PriceMouM_MouID = {$mou_id}
AND spm.is_packet = 'N'
AND spm.px_type = 'PX'
AND CONCAT(t.T_TestSasCode,' ',t.T_TestName) LIKE CONCAT('%', '{$search_esc}', '%')
ORDER BY t.T_TestName
LIMIT 50";
$q = $this->db_onedev->query($sql);
if (!$q) { $this->sys_error_db("get jenis vaksin", $this->db_onedev); exit; }
$records = array_map(function($row) {
$row['nat_test'] = json_decode($row['nat_test'] ?? '[]', true) ?: [];
$row['child_test'] = json_decode($row['child_test'] ?? '[]', true) ?: [];
return $row;
}, $q->result_array());
$this->sys_ok(['records' => $records]);
exit;
}
// List vaksin yang sudah tersimpan untuk satu order
function list_order_vaccines()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$orderid = intval($prm['orderid'] ?? 0);
if (!$orderid) { $this->sys_error("orderid required"); exit; }
$sql = "SELECT
ov.*,
t.T_TestName as jenis_vaksin_name,
t.T_TestSasCode as jenis_vaksin_code,
ins.M_InjectionSiteName as injection_site_name,
rv.M_RouteVaccineName as route_name,
s.M_StaffName as petugas_name
FROM one_klinik.order_vaccine ov
LEFT JOIN one_lab.t_test t ON ov.orderVaccineT_TestID = t.T_TestID
LEFT JOIN one_klinik.m_injection_site ins
ON ov.orderVaccineInjectionSiteCode = ins.M_InjectionSiteCode
LEFT JOIN one_klinik.m_route_vaccine rv
ON ov.orderVaccineRouteCode = rv.M_RouteVaccineCode
LEFT JOIN one_lab.m_staff s ON ov.orderVaccinePetugasM_StaffID = s.M_StaffID
WHERE ov.orderVaccineOrderID = ?
AND ov.orderVaccineIsActive = 'Y'
ORDER BY ov.orderVaccineCreated ASC";
$q = $this->db_oneklinik->query($sql, [$orderid]);
if (!$q) { $this->sys_error_db("list order_vaccine", $this->db_oneklinik); exit; }
$this->sys_ok(['records' => $q->result_array()]);
exit;
}
function save_vaccine()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$userID = $this->sys_user['M_UserID'];
$orderid = intval($prm['orderid'] ?? 0);
$vaccine_id = intval($prm['vaccine_id'] ?? 0); // orderVaccineID; 0 = insert baru
$t_test_id = intval($prm['t_test_id'] ?? 0);
if (!$orderid || !$t_test_id) {
$this->sys_error("orderid dan t_test_id wajib diisi");
exit;
}
$batch = trim($prm['batch_number'] ?? '');
$expired = trim($prm['expired_date'] ?? '');
$dosis = max(1, intval($prm['dosis'] ?? 1));
$site_code = trim($prm['injection_site_code'] ?? '');
$route_code = trim($prm['route_code'] ?? '');
$petugas_id = isset($prm['petugas_id']) && $prm['petugas_id'] ? intval($prm['petugas_id']) : null;
$given_at = trim($prm['given_at'] ?? '');
$next_date = trim($prm['next_date'] ?? '');
$catatan = trim($prm['catatan'] ?? '');
$kipi = trim($prm['kipi'] ?? '');
$observasi15 = ($prm['observasi_15'] ?? false) ? 'Y' : 'N';
$reaksi_alergi = ($prm['reaksi_alergi'] ?? false) ? 'Y' : 'N';
$ss_price_mou_id = isset($prm['ss_price_mou_id']) && $prm['ss_price_mou_id'] ? intval($prm['ss_price_mou_id']) : null;
$bruto = floatval($prm['bruto'] ?? 0);
$disc_persen = floatval($prm['discountpersen'] ?? 0);
$disc_rp = floatval($prm['discountrp'] ?? 0);
$total = floatval($prm['total'] ?? 0);
$given_at_sql = ($given_at && strtotime($given_at)) ? date('Y-m-d H:i:s', strtotime($given_at)) : date('Y-m-d H:i:s');
$expired_val = ($expired && strtotime($expired)) ? date('Y-m-d', strtotime($expired)) : null;
$next_date_val = ($next_date && strtotime($next_date)) ? date('Y-m-d', strtotime($next_date)) : null;
if ($vaccine_id > 0) {
$ok = $this->db_oneklinik->query(
"UPDATE one_klinik.order_vaccine SET
orderVaccineT_TestID = ?,
orderVaccineSsPriceMouID = ?,
orderVaccineBruto = ?,
orderVaccineDiscPersen = ?,
orderVaccineDiscRp = ?,
orderVaccineTotal = ?,
orderVaccineBatchNumber = ?,
orderVaccineExpiredDate = ?,
orderVaccineDosis = ?,
orderVaccineInjectionSiteCode = ?,
orderVaccineRouteCode = ?,
orderVaccinePetugasM_StaffID = ?,
orderVaccineGivenAt = ?,
orderVaccineNextDate = ?,
orderVaccineCatatan = ?,
orderVaccineKipi = ?,
orderVaccineObservasi15 = ?,
orderVaccineReaksiAlergi = ?,
orderVaccineUserID = ?
WHERE orderVaccineID = ? AND orderVaccineOrderID = ?",
[
$t_test_id, $ss_price_mou_id, $bruto, $disc_persen, $disc_rp, $total,
$batch ?: null, $expired_val, $dosis,
$site_code ?: null, $route_code ?: null, $petugas_id,
$given_at_sql, $next_date_val, $catatan ?: null,
$kipi ?: null, $observasi15, $reaksi_alergi,
$userID, $vaccine_id, $orderid
]
);
} else {
$ok = $this->db_oneklinik->query(
"INSERT INTO one_klinik.order_vaccine
(orderVaccineOrderID, orderVaccineT_TestID,
orderVaccineSsPriceMouID, orderVaccineBruto, orderVaccineDiscPersen,
orderVaccineDiscRp, orderVaccineTotal,
orderVaccineBatchNumber, orderVaccineExpiredDate, orderVaccineDosis,
orderVaccineInjectionSiteCode, orderVaccineRouteCode,
orderVaccinePetugasM_StaffID, orderVaccineGivenAt,
orderVaccineNextDate, orderVaccineCatatan,
orderVaccineKipi, orderVaccineObservasi15, orderVaccineReaksiAlergi,
orderVaccineUserID)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
[
$orderid, $t_test_id,
$ss_price_mou_id, $bruto, $disc_persen, $disc_rp, $total,
$batch ?: null, $expired_val, $dosis,
$site_code ?: null, $route_code ?: null, $petugas_id,
$given_at_sql, $next_date_val, $catatan ?: null,
$kipi ?: null, $observasi15, $reaksi_alergi, $userID
]
);
$vaccine_id = $this->db_oneklinik->insert_id();
}
if (!$ok) { $this->sys_error_db("save order_vaccine", $this->db_oneklinik); exit; }
$this->sys_ok(['orderVaccineID' => $vaccine_id]);
exit;
}
function delete_vaccine()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$vaccine_id = intval($prm['orderVaccineID'] ?? 0);
$userID = $this->sys_user['M_UserID'];
if (!$vaccine_id) { $this->sys_error("orderVaccineID required"); exit; }
$ok = $this->db_oneklinik->query(
"UPDATE one_klinik.order_vaccine SET orderVaccineIsActive = 'N', orderVaccineUserID = ?
WHERE orderVaccineID = ?",
[$userID, $vaccine_id]
);
if (!$ok) { $this->sys_error_db("delete order_vaccine", $this->db_oneklinik); exit; }
$this->sys_ok(['msg' => 'deleted']);
exit;
}
// ── TINDAKAN MEDIS ────────────────────────────────────────────────────────
// Autocomplete jenis tindakan dari ss_price_mou berdasarkan MOU order
function get_tindakan()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$orderid = intval($prm['orderid'] ?? 0);
$search = trim($prm['search'] ?? '');
if (!$orderid) { $this->sys_error("orderid required"); exit; }
$order_row = $this->db_oneklinik->query(
"SELECT orderM_MouID FROM one_klinik.`order` WHERE orderID = ? LIMIT 1",
[$orderid]
)->row_array();
if (!$order_row || !$order_row['orderM_MouID']) {
$this->sys_ok(['records' => []]);
exit;
}
$mou_id = intval($order_row['orderM_MouID']);
$search_esc = $this->db_onedev->escape_str($search);
$sql = "SELECT
spm.Ss_PriceMouID as ss_price_mou_id,
spm.Ss_PriceMouM_MouID as mouid,
0 as xid,
spm.Nat_TestID as nat_testid,
spm.nat_test,
spm.is_packet,
spm.packet_id,
spm.px_type as type,
spm.T_TestID as pxid,
t.T_TestCode as pxcode,
t.T_TestSasCode as pxsascode,
t.T_TestName as test_name,
CONCAT(t.T_TestSasCode,' ',t.T_TestName) as pxname,
t.T_TestIsResult as isresult,
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount')) as bruto,
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) as discountpersen,
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp')) as discountrp,
if(JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) <> 0,
(((JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) / 100)
* JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount')))
+ JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp'))),
JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp'))
) as discount,
( JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount'))
- ((JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDisc')) / 100)
* JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceAmount')))
- JSON_UNQUOTE(JSON_EXTRACT(one_json_sum(spm.Ss_PriceMouID),'$.T_PriceDiscRp'))
) as total,
spm.child_test
FROM one_lab.ss_price_mou spm
JOIN one_lab.t_test t ON spm.T_TestID = t.T_TestID AND t.T_TestIsActive = 'Y'
WHERE spm.Ss_PriceMouM_MouID = {$mou_id}
AND spm.is_packet = 'N'
AND spm.px_type = 'PX'
AND CONCAT(t.T_TestSasCode,' ',t.T_TestName) LIKE CONCAT('%', '{$search_esc}', '%')
ORDER BY t.T_TestName
LIMIT 50";
$q = $this->db_onedev->query($sql);
if (!$q) { $this->sys_error_db("get jenis tindakan", $this->db_onedev); exit; }
$records = array_map(function($row) {
$row['nat_test'] = json_decode($row['nat_test'] ?? '[]', true) ?: [];
$row['child_test'] = json_decode($row['child_test'] ?? '[]', true) ?: [];
return $row;
}, $q->result_array());
$this->sys_ok(['records' => $records]);
exit;
}
// List tindakan yang sudah tersimpan untuk satu order
function list_order_tindakan()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$orderid = intval($prm['orderid'] ?? 0);
if (!$orderid) { $this->sys_error("orderid required"); exit; }
$sql = "SELECT
ot.*,
t.T_TestName as jenis_tindakan_name,
t.T_TestSasCode as jenis_tindakan_code,
d.M_DoctorName as dokter_name
FROM one_klinik.order_tindakan ot
LEFT JOIN one_lab.t_test t ON ot.orderTindakanT_TestID = t.T_TestID
LEFT JOIN one_lab.m_doctor d ON ot.orderTindakanM_DoctorID = d.M_DoctorID
WHERE ot.orderTindakanOrderID = ?
AND ot.orderTindakanIsActive = 'Y'
ORDER BY ot.orderTindakanCreated ASC";
$q = $this->db_oneklinik->query($sql, [$orderid]);
if (!$q) { $this->sys_error_db("list order_tindakan", $this->db_oneklinik); exit; }
$this->sys_ok(['records' => $q->result_array()]);
exit;
}
function save_tindakan()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$userID = $this->sys_user['M_UserID'];
$orderid = intval($prm['orderid'] ?? 0);
$tindakan_id = intval($prm['tindakan_id'] ?? 0); // orderTindakanID; 0 = insert baru
$t_test_id = intval($prm['t_test_id'] ?? 0);
if (!$orderid || !$t_test_id) {
$this->sys_error("orderid dan t_test_id wajib diisi");
exit;
}
$doctor_id = isset($prm['doctor_id']) && $prm['doctor_id'] ? intval($prm['doctor_id']) : null;
$performed_at = trim($prm['performed_at'] ?? '');
$catatan = trim($prm['catatan'] ?? '');
$ss_price_mou_id = isset($prm['ss_price_mou_id']) && $prm['ss_price_mou_id'] ? intval($prm['ss_price_mou_id']) : null;
$bruto = floatval($prm['bruto'] ?? 0);
$disc_persen = floatval($prm['discountpersen'] ?? 0);
$disc_rp = floatval($prm['discountrp'] ?? 0);
$total = floatval($prm['total'] ?? 0);
$performed_at_sql = ($performed_at && strtotime($performed_at))
? date('Y-m-d H:i:s', strtotime($performed_at))
: date('Y-m-d H:i:s');
if ($tindakan_id > 0) {
$ok = $this->db_oneklinik->query(
"UPDATE one_klinik.order_tindakan SET
orderTindakanT_TestID = ?,
orderTindakanSsPriceMouID = ?,
orderTindakanBruto = ?,
orderTindakanDiscPersen = ?,
orderTindakanDiscRp = ?,
orderTindakanTotal = ?,
orderTindakanM_DoctorID = ?,
orderTindakanPerformedAt = ?,
orderTindakanCatatan = ?,
orderTindakanUserID = ?
WHERE orderTindakanID = ? AND orderTindakanOrderID = ?",
[
$t_test_id, $ss_price_mou_id, $bruto, $disc_persen, $disc_rp, $total,
$doctor_id, $performed_at_sql, $catatan ?: null,
$userID, $tindakan_id, $orderid
]
);
} else {
$ok = $this->db_oneklinik->query(
"INSERT INTO one_klinik.order_tindakan
(orderTindakanOrderID, orderTindakanT_TestID,
orderTindakanSsPriceMouID, orderTindakanBruto, orderTindakanDiscPersen,
orderTindakanDiscRp, orderTindakanTotal,
orderTindakanM_DoctorID, orderTindakanPerformedAt,
orderTindakanCatatan, orderTindakanUserID)
VALUES (?,?,?,?,?,?,?,?,?,?,?)",
[
$orderid, $t_test_id,
$ss_price_mou_id, $bruto, $disc_persen, $disc_rp, $total,
$doctor_id, $performed_at_sql, $catatan ?: null, $userID
]
);
$tindakan_id = $this->db_oneklinik->insert_id();
}
if (!$ok) { $this->sys_error_db("save order_tindakan", $this->db_oneklinik); exit; }
$this->sys_ok(['orderTindakanID' => $tindakan_id]);
exit;
}
function delete_tindakan()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$tindakan_id = intval($prm['orderTindakanID'] ?? 0);
$userID = $this->sys_user['M_UserID'];
if (!$tindakan_id) { $this->sys_error("orderTindakanID required"); exit; }
$ok = $this->db_oneklinik->query(
"UPDATE one_klinik.order_tindakan SET orderTindakanIsActive = 'N', orderTindakanUserID = ?
WHERE orderTindakanID = ?",
[$userID, $tindakan_id]
);
if (!$ok) { $this->sys_error_db("delete order_tindakan", $this->db_oneklinik); exit; }
$this->sys_ok(['msg' => 'deleted']);
exit;
}
}

View File

@@ -12,34 +12,29 @@ class Screening extends MY_Controller
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->db_oneklinik = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
public function search()
{
$prm = $this->sys_input;
$id = $prm['id'];
$id = $this->db_onedev->escape_str($prm['id']);
$sql = "SELECT orderID,
orderDate,
$sql = "SELECT orderID,
orderDate,
orderNumber,
orderIsScreening,
orderIsAnamnese,
orderIsCheck,
orderAge as patient_age,
DATE_FORMAT(orderDate,'%d-%m-%Y') as order_date,
orderM_ClinicUnitID,
'N' divider,
M_PatientID,
M_PatientNoReg,
M_PatientPrefix,
M_PatientName,
M_PatientSuffix,
M_PatientHP,
M_PatientEmail,
M_PatientPOB,
M_PatientPhone,
M_PatientIDNumber,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as M_PatientDOB,
concat(M_TitleName,' ',IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) M_PatientNameRaw,
M_PatientNote,
M_PatientNIK,
M_PatientJabatan,
@@ -49,78 +44,178 @@ class Screening extends MY_Controller
M_PatientJob,
M_PatientM_SexID,
M_SexName,
M_TitleID, M_TitleName,
M_PatientM_TitleID,
M_TitleName,
M_PatientM_ReligionID,
M_ReligionName,
IFNULL(M_ReligionName,'-') M_ReligionName,
M_PatientM_IdTypeID,
M_IdTypeName,
M_PatientIDNumber,
IF(ISNULL(M_PatientSuspendID),'active','suspend' ) as status,
M_PatientAddressM_KelurahanID M_KelurahanID,
0 M_DistrictID,
0 M_CityID,
0 M_ProvinceID
FROM one_klinik.order
IF(ISNULL(M_PatientSuspendID),'active','suspend') as status,
M_PatientAddressRegionalCd,
M_PatientName_enc, M_PatientHP_enc, M_PatientDOB_enc,
M_PatientEmail_enc, M_PatientPhone_enc, M_PatientPOB_enc,
M_PatientIDNumber_enc, M_PatientNIK_enc, M_PatientAddressDescription_enc
FROM one_klinik.`order`
JOIN m_patient ON M_PatientID = orderM_PatientID AND M_PatientIsActive = 'Y'
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_sex ON M_PatientM_SexID = M_SexID
JOIN m_branch ON M_BranchIsActive = 'Y' AND M_BranchIsDefault = 'Y'
JOIN m_patientaddress ON M_PatientAddressM_PatientID = M_PatientID AND M_PatientAddressIsActive = 'Y'
LEFT JOIN m_idtype ON M_IdTypeID = M_PatientM_IdTypeID AND M_IdTypeIsActive = 'Y'
LEFT JOIN m_religion ON m_patientm_religionid = m_religionid
LEFT JOIN m_religion ON M_PatientM_ReligionID = M_ReligionID
LEFT JOIN m_patientsuspend ON M_PatientSuspendM_PatientID = M_PatientID AND M_PatientSuspendIsActive = 'Y'
WHERE
orderNumber = '{$id}' AND
M_PatientSuspendID IS NULL
WHERE orderNumber = '{$id}' AND M_PatientSuspendID IS NULL
GROUP BY M_PatientID";
//echo $sql;
$query = $this->db_onedev->query($sql);
if ($query) {
$rows = $query->result_array();
foreach ($rows as $k => $v)
{
$rows[$k]['M_PatientName'] = stripslashes($rows[$k]['M_PatientName']);
$rows[$k]['M_PatientAddressDescription'] = stripslashes($v['M_PatientAddressDescription']);
$patient_name = str_replace("'", "\\'", $prm['M_PatientName']);
$sql = "SELECT *, concat('{$rows[$k]['M_PatientAddressDescription'] }', '\n\n',
m_kelurahanname, ', ',
m_districtname,'\n',
m_cityname, ', ',
m_provincename) as xaddress
FROM m_kelurahan
JOIN m_district ON M_KelurahanM_DistrictID = M_DistrictID
JOIN m_city ON M_DistrictM_CityID = M_CityID
JOIN m_province ON M_CityM_ProvinceID = M_ProvinceID
WHERE
M_KelurahanID = {$v['M_KelurahanID']} ";
//echo $sql;
$row_address = $this->db_onedev->query($sql)->row_array();
$rows[$k]['M_PatientAddress'] = stripslashes($row_address['xaddress']);
$rows[$k]['M_DistrictID'] = $row_address['M_DistrictID'];
$rows[$k]['M_CityID'] = $row_address['M_CityID'];
$rows[$k]['M_ProvinceID'] = $row_address['M_ProvinceID'];
$info = $this->db_onedev->query("SELECT fn_fo_patient_visit(?) info", [$v['M_PatientID']])->row();
$rows[$k]['info'] = json_decode($info->info);
}
$result = array("total" => 1, "records" => $rows, "sql"=> $this->db_onedev->last_query());
$this->sys_ok($result);
}
else {
$this->sys_error_db("m_patient rows",$this->db_onedev);
exit;
if (!$query) {
$this->sys_error_db("order rows", $this->db_onedev);
return;
}
}
$rows = $query->result_array();
$enc = $this->ibl_encryptor;
foreach ($rows as $k => $v) {
$rows[$k]['M_PatientName'] = $enc->decrypt($v['M_PatientName_enc']) ?? $v['M_PatientNameRaw'];
$rows[$k]['M_PatientHP'] = $enc->decrypt($v['M_PatientHP_enc']) ?? '';
$rows[$k]['M_PatientEmail'] = $enc->decrypt($v['M_PatientEmail_enc']) ?? '';
$rows[$k]['M_PatientPOB'] = $enc->decrypt($v['M_PatientPOB_enc']) ?? '';
$rows[$k]['M_PatientPhone'] = $enc->decrypt($v['M_PatientPhone_enc']) ?? '';
$rows[$k]['M_PatientIDNumber'] = $enc->decrypt($v['M_PatientIDNumber_enc']) ?? '';
$rows[$k]['M_PatientNIK'] = $enc->decrypt($v['M_PatientNIK_enc']) ?? '';
$rows[$k]['M_PatientDOB'] = $enc->decrypt($v['M_PatientDOB_enc']) ?? '';
$rows[$k]['dob_ina'] = $rows[$k]['M_PatientDOB'];
$rows[$k]['M_PatientAddressDescription'] = $enc->decrypt($v['M_PatientAddressDescription_enc']) ?? '';
$rows[$k]['M_PatientAddress'] = $rows[$k]['M_PatientAddressDescription'];
function get_data(){
foreach (array_keys($rows[$k]) as $col) {
if (substr($col, -4) === '_enc') unset($rows[$k][$col]);
}
unset($rows[$k]['M_PatientNameRaw']);
$rows[$k]['info'] = $this->build_patient_visit_info($v['M_PatientID'], $rows[$k]['M_PatientDOB']);
// Screening template berdasarkan poli order
$cu_id = $v['orderM_ClinicUnitID'] ?? null;
$rows[$k]['screening_template'] = null;
$rows[$k]['screening_forms'] = null;
$rows[$k]['order_screening'] = null;
if ($cu_id) {
$tpl = $this->db_oneklinik->query(
"SELECT st.M_ScreeningTemplateID, st.M_ScreeningTemplateCode, st.M_ScreeningTemplateName
FROM one_klinik.m_clinic_unit cu
JOIN one_klinik.m_screening_template st
ON st.M_ScreeningTemplateID = cu.M_ClinicUnitM_ScreeningTemplateID
WHERE cu.M_ClinicUnitID = ?", [$cu_id]
)->row_array();
$rows[$k]['screening_template'] = $tpl ?: null;
if ($tpl && $tpl['M_ScreeningTemplateCode'] !== 'DEFAULT') {
// Template dinamis (VAKSINASI / KHITAN): ambil form + jawaban yang sudah ada
$forms = $this->db_oneklinik->query(
"SELECT sf.M_ScreeningFormID,
sf.M_ScreeningFormQuestion,
sf.M_ScreeningFormAnswerType,
sf.M_ScreeningFormOptions,
sf.M_ScreeningFormSortOrder,
sf.M_ScreeningFormIsRequired,
sa.T_ScreeningAnswerValue AS answer
FROM one_klinik.m_screening_form sf
LEFT JOIN one_klinik.t_screening_answer sa
ON sa.T_ScreeningAnswerM_ScreeningFormID = sf.M_ScreeningFormID
AND sa.T_ScreeningAnswerOrderID = ?
AND sa.T_ScreeningAnswerIsActive = 'Y'
WHERE sf.M_ScreeningFormM_ScreeningTemplateID = ?
AND sf.M_ScreeningFormIsActive = 'Y'
ORDER BY sf.M_ScreeningFormSortOrder",
[$v['orderID'], $tpl['M_ScreeningTemplateID']]
)->result_array();
foreach ($forms as &$f) {
$f['M_ScreeningFormOptions'] = $f['M_ScreeningFormOptions']
? json_decode($f['M_ScreeningFormOptions'], true)
: null;
$f['answer'] = $f['answer'] !== null
? json_decode($f['answer'], true)
: null;
// Tandai option terpilih dengan value:true agar FE bisa render form pre-filled
if ($f['answer'] && $f['M_ScreeningFormOptions'] && $f['M_ScreeningFormAnswerType'] !== 'text') {
if ($f['M_ScreeningFormAnswerType'] === 'single') {
$selected = [$f['answer']['id'] ?? ''];
} else {
$selected = array_column((array)$f['answer'], 'id');
}
foreach ($f['M_ScreeningFormOptions'] as &$opt) {
$opt['value'] = in_array($opt['id'], $selected);
}
unset($opt);
}
}
unset($f);
$rows[$k]['screening_forms'] = $forms;
} else {
// DEFAULT: pakai order_screening lama
$rows[$k]['order_screening'] = $this->db_oneklinik->query(
"SELECT * FROM one_klinik.order_screening
WHERE orderScreeningOrderID = ? AND orderScreeningIsActive = 'Y'",
[$v['orderID']]
)->row_array() ?: null;
}
}
}
$this->sys_ok(["total" => count($rows), "records" => $rows]);
}
protected function build_patient_visit_info($patient_id, $patient_dob)
{
$visit = 1;
$birthday = 'N';
$visit_query = $this->db_onedev->query(
"SELECT COUNT(DISTINCT T_OrderHeaderID) AS n
FROM t_orderheader
JOIN t_orderdetail ON T_OrderHeaderID = T_OrderDetailT_OrderHeaderID AND T_OrderDetailIsActive = 'Y'
WHERE T_OrderHeaderIsActive = 'Y'
AND T_OrderHeaderM_PatientID = ?",
[$patient_id]
);
if ($visit_query) {
$visit_row = $visit_query->row_array();
$visit += (int) ($visit_row['n'] ?? 0);
}
$init_visit_query = $this->db_onedev->query(
"SELECT M_PatientInitialVisit
FROM m_patient
WHERE M_PatientID = ?",
[$patient_id]
);
if ($init_visit_query) {
$init_visit_row = $init_visit_query->row_array();
if (!empty($init_visit_row['M_PatientInitialVisit'])) {
$visit += (int) $init_visit_row['M_PatientInitialVisit'];
}
}
$dob_time = empty($patient_dob) ? false : strtotime($patient_dob);
if ($dob_time !== false) {
$birthday = date('m-d', $dob_time) === date('m-d') ? 'Y' : 'N';
}
return json_decode(json_encode([
'visit' => $visit,
'birthday' => $birthday,
]));
}
function get_data(){
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
@@ -154,6 +249,48 @@ class Screening extends MY_Controller
}
function getsexreg()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$rows = [];
$rows['default_location'] = [];
$rows['doctors'] = $this->db_onedev->query(
"SELECT M_DoctorID as id, M_DoctorCode as code, M_DoctorName as name,
M_DoctorMcuDefaultKlinik as is_default, M_DoctorMcuPriceKlinik as price
FROM m_doctormcu JOIN m_doctor ON M_DoctorMcuM_DoctorID = M_DoctorID
WHERE M_DoctorMcuIsActive = 'Y'"
)->result_array();
$rows['default_doctor'] = [];
foreach ($rows['doctors'] as $value) {
if ($value['is_default'] == 'Y') { $rows['default_doctor'] = $value; break; }
}
$rows['titles'] = $this->db_onedev->query("SELECT * FROM m_title WHERE M_TitleIsActive = 'Y'")->result_array();
$rows['sexes'] = $this->db_onedev->query("SELECT * FROM m_sex WHERE M_SexIsActive = 'Y'")->result_array();
$rows['religions'] = $this->db_onedev->query("SELECT * FROM m_religion WHERE M_ReligionIsActive = 'Y'")->result_array();
$rows['kartuidentitass'] = $this->db_onedev->query("SELECT * FROM m_idtype WHERE M_IdTypeIsActive = 'Y'")->result_array();
$branch = $this->db_onedev->query("SELECT * FROM m_branch WHERE M_BranchIsDefault = 'Y' AND M_BranchIsActive = 'Y'")->row_array();
if ($branch) {
$rows['default_location']['city_address'] = $this->db_onedev->query("SELECT * FROM m_city WHERE M_CityIsActive = 'Y' AND M_CityID = ?", [$branch['M_BranchM_CityID']])->row_array();
$rows['default_location']['cities'] = $this->db_onedev->query("SELECT * FROM m_city WHERE M_CityIsActive = 'Y' AND M_CityM_ProvinceID = ?", [$rows['default_location']['city_address']['M_CityM_ProvinceID']])->result_array();
$rows['default_location']['province_address'] = $this->db_onedev->query("SELECT * FROM m_province WHERE M_ProvinceIsActive = 'Y' AND M_ProvinceID = ?", [$rows['default_location']['city_address']['M_CityM_ProvinceID']])->row_array();
$rows['default_location']['provinces'] = $this->db_onedev->query("SELECT * FROM m_province WHERE M_ProvinceIsActive = 'Y'")->result_array();
$rows['default_location']['districts'] = $this->db_onedev->query("SELECT * FROM m_district WHERE M_DistrictIsActive = 'Y' AND M_DistrictM_CityID = ?", [$branch['M_BranchM_CityID']])->result_array();
$rows['default_location']['district_address'] = $this->db_onedev->query("SELECT * FROM m_district WHERE M_DistrictIsActive = 'Y' AND M_DistrictID = ?", [$branch['M_BranchM_DistrictID']])->row_array();
$rows['default_location']['kelurahans'] = $this->db_onedev->query("SELECT * FROM m_kelurahan WHERE M_KelurahanIsActive = 'Y' AND M_KelurahanM_DistrictID = ?", [$branch['M_BranchM_DistrictID']])->result_array();
$rows['default_location']['kelurahan_address'] = $this->db_onedev->query("SELECT * FROM m_kelurahan WHERE M_KelurahanIsActive = 'Y' AND M_KelurahanID = ?", [$branch['M_BranchM_KelurahanID']])->row_array();
}
$this->sys_ok(["total" => count($rows), "records" => $rows]);
exit;
}
protected function objToArray($obj)
{
// Not an object or array
@@ -184,26 +321,47 @@ class Screening extends MY_Controller
$status = $prm['status'];
$sql = "SELECT 'N' divider,
CONCAT(IF(ISNULL(M_TitleName),'',CONCAT(M_TitleName,'. ')),M_PatientName) as patient_name,
M_PatientName, M_PatientName_enc,
M_PatientHP, M_PatientHP_enc,
M_PatientDOB, M_PatientDOB_enc,
M_PatientEmail, M_PatientEmail_enc,
M_PatientPhone, M_PatientPhone_enc,
M_PatientPOB, M_PatientPOB_enc,
M_PatientIDNumber, M_PatientIDNumber_enc,
M_PatientNIK, M_PatientNIK_enc,
M_PatientPhoto, M_PatientPhotoThumb,
`order`.*,DATE_FORMAT(orderDate,'%d-%m-%Y') as date_order,
'' as kode_status, '' as status
'' as kode_status, '' as status,
M_TitleName, M_PatientNoReg, M_PatientM_SexID
FROM one_klinik.`order`
JOIN m_patient ON orderM_PatientID = M_PatientID
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
WHERE
orderIsActive = 'Y' AND DATE(orderDate) = ? AND orderIsScreening = ?
WHERE
orderIsActive = 'Y' AND DATE(orderDate) = ? AND orderIsScreening = ?
LIMIT $number_limit offset $number_offset";
//echo $sql;
$query = $this->db_oneklinik->query($sql,array($xdate,$status));
//echo $this->db_oneklinik->last_query();
if ($query) {
if ($query) {
$rows = $query->result_array();
$result = array("total" => $tot_page, "records" => $rows, "sql"=> $this->db_onedev->last_query());
$enc = $this->ibl_encryptor;
foreach ($rows as $k => $v) {
$rows[$k]['M_PatientName'] = $enc->decrypt($v['M_PatientName_enc'] ?? '') ?: $v['M_PatientName'];
$rows[$k]['M_PatientHP'] = $enc->decrypt($v['M_PatientHP_enc'] ?? '') ?: $v['M_PatientHP'];
$rows[$k]['M_PatientDOB'] = $enc->decrypt($v['M_PatientDOB_enc'] ?? '') ?: $v['M_PatientDOB'];
$rows[$k]['M_PatientEmail'] = $enc->decrypt($v['M_PatientEmail_enc'] ?? '') ?: $v['M_PatientEmail'];
$rows[$k]['M_PatientPhone'] = $enc->decrypt($v['M_PatientPhone_enc'] ?? '') ?: $v['M_PatientPhone'];
$rows[$k]['M_PatientPOB'] = $enc->decrypt($v['M_PatientPOB_enc'] ?? '') ?: $v['M_PatientPOB'];
$rows[$k]['M_PatientIDNumber'] = $enc->decrypt($v['M_PatientIDNumber_enc'] ?? '') ?: $v['M_PatientIDNumber'];
$rows[$k]['M_PatientNIK'] = $enc->decrypt($v['M_PatientNIK_enc'] ?? '') ?: $v['M_PatientNIK'];
$rows[$k]['patient_name'] = trim(($v['M_TitleName'] ?? '') . ' ' . $rows[$k]['M_PatientName']);
}
$result = array("total" => $tot_page, "records" => $rows, "sql"=> $this->db_oneklinik->last_query());
$this->sys_ok($result);
}
}
else {
$this->sys_error_db("m_patient rows",$this->db_onedev);
$this->sys_error_db("m_patient rows",$this->db_oneklinik);
exit;
}
@@ -297,124 +455,7 @@ class Screening extends MY_Controller
}
function end_session(){
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$userID = $this->sys_user['M_UserID'];
//print_r($prm['subgroup']);
$sql = "SELECT COUNT(*) as xcount
FROM one_klinik.order_screening
WHERE
orderScreeningOrderID = ? AND orderScreeningIsActive = 'Y'";
$query = $this->db_oneklinik->query($sql,array($prm['orderID']));
if(!$query){
$this->sys_error("count exist");
echo $this->db_oneklinik->last_query();
}
$check_exist = $query->row()->xcount;
if($check_exist == 0){
$sql = "INSERT one_klinik.order_screening (
orderScreeningOrderID,
orderScreeningKesanUmum,
orderScreeningValueKesadaran,
orderScreeningValuePernafasan,
orderScreeningValueResikoJatuh,
orderScreeningValueNyeriDada,
orderScreeningValueSkalaNyeri,
orderScreeningValueBatuk,
orderScreeningValueKeputusan,
orderScreeningCreated,
orderScreeningUserID
)
VALUES(
?,?,?,?,?,?,?,?,?,NOW(),?
)";
$query = $this->db_oneklinik->query($sql,array(
$prm['orderID'],
$prm['kesan_umum'],
$prm['kesadaran'],
$prm['pernafasan'],
$prm['resiko_jatuh'],
$prm['nyeri_dada'],
$prm['skala_nyeri'],
$prm['batuk'],
$prm['keputusan'],
$userID)
);
if(!$query){
$this->sys_error("Gagal insert");
}
}else{
$sql = "UPDATE one_klinik.order_screening SET
orderScreeningKesanUmum = ?,
orderScreeningValueKesadaran = ?,
orderScreeningValuePernafasan = ?,
orderScreeningValueResikoJatuh = ?,
orderScreeningValueNyeriDada = ?,
orderScreeningValueSkalaNyeri = ?,
orderScreeningValueBatuk = ?,
orderScreeningValueKeputusan = ?,
orderScreeningUserID = ?
WHERE
orderScreeningOrderID = ?
";
$query = $this->db_oneklinik->query($sql,[
$prm['kesan_umum'],
$prm['kesadaran'],
$prm['pernafasan'],
$prm['resiko_jatuh'],
$prm['nyeri_dada'],
$prm['skala_nyeri'],
$prm['batuk'],
$prm['keputusan'],
$userID,
$prm['orderID']
]);
if(!$query){
echo $this->db_oneklinik->last_query();
$this->sys_error("Gagal Update");
}
}
$sql = "INSERT INTO one_klinik.order_status (
orderStatusOrderID,
orderStatusCode,
orderStatusValue,
orderStatusUserID
)
VALUES(
?,?,?,?
)";
$query = $this->db_oneklinik->query($sql,array($prm['orderID'],'S','D',$userID));
if(!$query){
$this->sys_error("Gagal End");
}
$sql = "UPDATE one_klinik.`order` SET orderIsScreening = 'D', orderUserID = ?
WHERE
orderID = ?";
$query = $this->db_oneklinik->query($sql,array($userID,$prm['orderID']));
if(!$query){
$this->sys_error("Gagal ENd");
}
$result = array('process'=>'OK');
$this->sys_ok($result);
exit;
$this->endsession();
}
@@ -422,4 +463,149 @@ class Screening extends MY_Controller
public function endsession()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$prm = $this->sys_input;
$userID = $this->sys_user['M_UserID'];
$orderID = intval($prm['orderID'] ?? 0);
if (!$orderID) {
$this->sys_error("orderID required");
return;
}
// 1. Tandai order selesai screening + catat status
$ok = $this->db_oneklinik->query(
"UPDATE one_klinik.`order` SET orderIsScreening = 'D', orderUserID = ? WHERE orderID = ?",
[$userID, $orderID]
);
if (!$ok) {
$this->sys_error_db("update order", $this->db_oneklinik);
return;
}
$this->db_oneklinik->query(
"INSERT INTO one_klinik.order_status
(orderStatusOrderID, orderStatusCode, orderStatusValue, orderStatusUserID)
VALUES (?, 'S', 'D', ?)",
[$orderID, $userID]
);
// 2. Tentukan template: ada screening_template_id dan bukan DEFAULT?
$template_id = intval($prm['screening_template_id'] ?? 0);
$is_default = true;
if ($template_id) {
$tpl = $this->db_oneklinik->query(
"SELECT M_ScreeningTemplateCode FROM one_klinik.m_screening_template
WHERE M_ScreeningTemplateID = ?",
[$template_id]
)->row_array();
if ($tpl && $tpl['M_ScreeningTemplateCode'] !== 'DEFAULT') {
$is_default = false;
}
}
if ($is_default) {
// 3. DEFAULT: simpan ke order_screening (INSERT atau UPDATE)
$exists = $this->db_oneklinik->query(
"SELECT COUNT(*) AS c FROM one_klinik.order_screening
WHERE orderScreeningOrderID = ? AND orderScreeningIsActive = 'Y'",
[$orderID]
)->row()->c;
if ($exists == 0) {
$ins = $this->db_oneklinik->query(
"INSERT INTO one_klinik.order_screening
(orderScreeningOrderID, orderScreeningKesanUmum,
orderScreeningValueKesadaran, orderScreeningValuePernafasan,
orderScreeningValueResikoJatuh, orderScreeningValueNyeriDada,
orderScreeningValueSkalaNyeri, orderScreeningValueBatuk,
orderScreeningValueKeputusan, orderScreeningCreated, orderScreeningUserID)
VALUES (?,?,?,?,?,?,?,?,?,NOW(),?)",
[$orderID,
$prm['kesan_umum'] ?? '',
$prm['kesadaran'] ?? '',
$prm['pernafasan'] ?? '',
$prm['resiko_jatuh'] ?? null,
$prm['nyeri_dada'] ?? '',
$prm['skala_nyeri'] ?? '',
$prm['batuk'] ?? '',
$prm['keputusan'] ?? '',
$userID]
);
if (!$ins) {
$this->sys_error_db("insert order_screening", $this->db_oneklinik);
return;
}
} else {
$upd = $this->db_oneklinik->query(
"UPDATE one_klinik.order_screening SET
orderScreeningKesanUmum = ?,
orderScreeningValueKesadaran = ?,
orderScreeningValuePernafasan = ?,
orderScreeningValueResikoJatuh = ?,
orderScreeningValueNyeriDada = ?,
orderScreeningValueSkalaNyeri = ?,
orderScreeningValueBatuk = ?,
orderScreeningValueKeputusan = ?,
orderScreeningUserID = ?
WHERE orderScreeningOrderID = ?",
[$prm['kesan_umum'] ?? '',
$prm['kesadaran'] ?? '',
$prm['pernafasan'] ?? '',
$prm['resiko_jatuh'] ?? null,
$prm['nyeri_dada'] ?? '',
$prm['skala_nyeri'] ?? '',
$prm['batuk'] ?? '',
$prm['keputusan'] ?? '',
$userID,
$orderID]
);
if (!$upd) {
$this->sys_error_db("update order_screening", $this->db_oneklinik);
return;
}
}
} else {
// 4. Template dinamis (VAKSINASI/KHITAN): replace semua jawaban
$this->db_oneklinik->query(
"DELETE FROM one_klinik.t_screening_answer WHERE T_ScreeningAnswerOrderID = ?",
[$orderID]
);
$answers = is_array($prm['screening_answers']) ? $prm['screening_answers'] : [];
foreach ($answers as $item) {
$form_id = intval($item['M_ScreeningFormID'] ?? 0);
if (!$form_id) continue;
// Simpan sebagai JSON object
$answer_type = $item['answer_type'] ?? 'single';
if ($answer_type === 'text') {
$stored_value = json_encode(['value' => $item['answer_label'] ?? '']);
} else {
$stored_value = json_encode(['id' => $item['answer_id'] ?? '', 'label' => $item['answer_label'] ?? '']);
}
$this->db_oneklinik->query(
"INSERT INTO one_klinik.t_screening_answer
(T_ScreeningAnswerOrderID, T_ScreeningAnswerM_ScreeningFormID,
T_ScreeningAnswerValue, T_ScreeningAnswerUserID)
VALUES (?,?,?,?)",
[$orderID, $form_id, $stored_value, $userID]
);
}
}
$this->sys_ok(['process' => 'OK']);
}
}

View File

@@ -513,14 +513,29 @@ module.exports = {
this.urlprintnote = "/birt/run?__report=report/one/fo/rpt_t_002.rptdesign&__format=pdf&username="+user.M_UserUsername+"&PID="+idx
this.$store.commit("payment/update_open_print_note",true)
},
printInvoice(){
this.printwidth = 800
this.printtitle = ""
let idx = this.$store.state.patient.selected_patient.T_OrderHeaderID
let user = one_user()
this.urlprintnote = "/birt/run?__report=report/one/fo/rpt_t_001.rptdesign&__format=pdf&username="+user.M_UserUsername+"&PID="+idx
this.$store.commit("payment/update_open_print_note",true)
}
async printInvoice(){
this.printwidth = 800
this.printtitle = ""
let idx = this.$store.state.patient.selected_patient.T_OrderHeaderID
let user = one_user()
this.urlprintnote = "/birt/run?__report=report/one/fo/rpt_t_001.rptdesign&__format=pdf&username="+user.M_UserUsername+"&PID="+idx
try{
let resp = await axios.post('/one-api/mockup/fo/cashier/payment/get_report_url_by_code', {
token: one_token(),
code_report: 'FO-INV-P-INA-02',
params: {
PUsername: user.M_UserUsername,
PT_OrderHeaderID: idx,
TS: '#TS'
}
})
if(resp.status === 200 && resp.data.status === "OK" && resp.data.data.url){
this.urlprintnote = resp.data.data.url
}
}
catch(e){}
this.$store.commit("payment/update_open_print_note",true)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,11 +6,12 @@ class Patient extends MY_Controller
{
echo "Patient API";
}
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
}
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
public function add_notes($orderid)
{
@@ -112,15 +113,17 @@ class Patient extends MY_Controller
}
$sql = "SELECT t_orderheader.*,
M_PatientNoReg,
DATE_FORMAT(T_OrderHeaderDate,'%d-%m-%Y %H:%i') as order_date,
CONCAT(M_TitleName,'. ',M_PatientName) as M_PatientName,
CONCAT(M_TitleLangName,'. ',M_PatientName) as M_PatientName_eng,
M_PatientIDNumber,
M_TitleName,
M_CompanyName,
M_MouName,
$sql = "SELECT t_orderheader.*,
M_PatientNoReg,
DATE_FORMAT(T_OrderHeaderDate,'%d-%m-%Y %H:%i') as order_date,
CONCAT(M_TitleName,'. ',M_PatientName) as M_PatientName,
CONCAT(M_TitleLangName,'. ',M_PatientName) as M_PatientName_eng,
M_PatientName_enc,
M_PatientIDNumber,
M_TitleName,
M_TitleLangName,
M_CompanyName,
M_MouName,
T_OrderHeaderTotal as totalbill,
IFNULL(Last_StatusPaymentPaid,0) as paid,
(T_OrderHeaderTotal + fn_fo_chasier_get_admin_charge(T_OrderHeaderID) )- ifnull(fn_fo_chasier_get_total_payment(T_OrderHeaderID),0) as unpaid,
@@ -152,12 +155,20 @@ class Patient extends MY_Controller
// echo $sql;
$query = $this->db_onedev->query($sql, $sql_param);
$rows = $query->result_array();
$qrySrc = $this->db_onedev->last_query();
if ($rows) {
foreach ($rows as $k => $v) {
$rows[$k]['notes'] = $this->add_notes($v['T_OrderHeaderID']);
}
}
$qrySrc = $this->db_onedev->last_query();
if ($rows) {
foreach ($rows as $k => $v) {
$plainName = trim($this->ibl_encryptor->decrypt($v['M_PatientName_enc'] ?? '') ?? '');
if ($plainName === '') {
$plainName = trim((string)($v['M_PatientName'] ?? ''));
}
$rows[$k]['M_PatientName'] = $plainName;
$rows[$k]['M_PatientName_print'] = trim(($v['M_TitleName'] ?? '') . '. ' . $plainName);
$rows[$k]['M_PatientName_print_eng'] = trim(($v['M_TitleLangName'] ?? '') . '. ' . $plainName);
unset($rows[$k]['M_PatientName_enc']);
$rows[$k]['notes'] = $this->add_notes($v['T_OrderHeaderID']);
}
}
$result = array("total" => $tot_page, "records" => $rows, "sql" => $qrySrc);

View File

@@ -28,3 +28,24 @@ Content-Type: application/json
{
"token": "{{token}}"
}
###
POST https://{{url}}/one-api-lab/mockup/fo/cashiernewpayment-v27/payment/get_report_url_by_code
Content-Type: application/json
{
"token": "{{token}}",
"order_id": 570,
"code": "FO-INV-P-INA-02"
}
###
POST https://{{url}}/one-api-lab/mockup/fo/cashiernewpayment-v27/payment/get_report_url_by_code
Content-Type: application/json
{
"token": "{{token}}",
"order_id": 570,
"payment_id": 123,
"code": "FO-NR-P-INA-01"
}

View File

@@ -755,11 +755,11 @@ class Payment extends MY_Controller
}
}
function get_print_transaction_fo_kk_ina()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
function get_print_transaction_fo_kk_ina()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$sql = "SELECT * FROM print_transaction WHERE Print_TransactionCode = 'FO-KK-INA'";
@@ -771,9 +771,330 @@ class Payment extends MY_Controller
"records" => $rows
);
$this->sys_ok($result);
} else {
$this->sys_error_db("get_print_transaction_fo_kk_ina", $this->db_onedev);
exit;
}
}
}
} else {
$this->sys_error_db("get_print_transaction_fo_kk_ina", $this->db_onedev);
exit;
}
}
function get_report_url_by_code()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$reportCode = trim($prm['code'] ?? $prm['code_report'] ?? '');
$orderId = intval($prm['order_id'] ?? 0);
$paymentId = intval($prm['payment_id'] ?? 0);
if ($reportCode === '') {
$this->sys_error("code wajib diisi");
exit;
}
if ($orderId <= 0 && $paymentId <= 0) {
$this->sys_error("order_id atau payment_id wajib diisi");
exit;
}
if ($orderId <= 0 && $paymentId > 0) {
$orderId = $this->resolve_order_id_by_payment($paymentId);
}
if ($orderId <= 0) {
$this->sys_error("order_id tidak ditemukan");
exit;
}
$token = $this->resolve_request_token();
$url = $this->build_report_proxy_url($token, $reportCode, $orderId, $paymentId);
$this->sys_ok(array(
"url" => $url
));
exit;
}
function stream_report_by_code()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$reportCode = trim($prm['code'] ?? $prm['code_report'] ?? '');
$orderId = intval($prm['order_id'] ?? 0);
$paymentId = intval($prm['payment_id'] ?? 0);
if ($reportCode === '') {
$this->sys_error("code wajib diisi");
exit;
}
if ($orderId <= 0 && $paymentId > 0) {
$orderId = $this->resolve_order_id_by_payment($paymentId);
}
if ($orderId <= 0) {
$this->sys_error("order_id tidak ditemukan");
exit;
}
$this->load->library('ibl_patient_decrypt');
$cacheId = $this->ibl_patient_decrypt->populate_cache_by_order($orderId);
$patientName = $this->resolve_patient_name_by_cache($cacheId);
if ($patientName === '') {
$patientName = $this->resolve_patient_name_by_order($orderId);
}
if ($patientName === '') {
$patientName = $this->resolve_patient_name_from_enc_by_order($orderId);
}
$url = $this->build_birt_url_by_code($reportCode, $orderId, $paymentId, $patientName);
if ($url === false) {
$this->ibl_patient_decrypt->delete_cache($cacheId);
$this->sys_error("print transaction tidak ditemukan: " . $reportCode);
exit;
}
$pdf = @file_get_contents($this->resolve_fetch_url($url), false, stream_context_create(array(
'http' => array(
'timeout' => 120,
'method' => 'GET',
),
)));
$this->ibl_patient_decrypt->delete_cache($cacheId);
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
exit;
}
$filename = $reportCode . '_' . $orderId . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
private function resolve_order_id_by_payment($paymentId)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentT_OrderHeaderID
FROM f_payment
WHERE F_PaymentID = ?
LIMIT 1",
array($paymentId)
)->row_array();
return intval($row['F_PaymentT_OrderHeaderID'] ?? 0);
}
private function resolve_payment_id_by_order($orderId)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentID
FROM f_payment
WHERE F_PaymentT_OrderHeaderID = ?
AND IFNULL(F_PaymentIsActive, 'Y') = 'Y'
ORDER BY F_PaymentID DESC
LIMIT 1",
array($orderId)
)->row_array();
return intval($row['F_PaymentID'] ?? 0);
}
private function resolve_patient_name_by_order($orderId)
{
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_order_id = ?
ORDER BY ppc_id DESC
LIMIT 1",
array($orderId)
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function resolve_patient_name_by_cache($cacheId)
{
if (!$cacheId) {
return '';
}
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_id = ?
LIMIT 1",
array($cacheId)
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function resolve_patient_name_from_enc_by_order($orderId)
{
$this->load->library('ibl_encryptor');
$row = $this->db_onedev->query(
"SELECT M_PatientName_enc
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ?
LIMIT 1",
array($orderId)
)->row_array();
return trim($this->ibl_encryptor->decrypt($row['M_PatientName_enc'] ?? '') ?? '');
}
private function resolve_report_username()
{
if (!empty($this->sys_user['M_StaffName'])) {
return trim($this->sys_user['M_StaffName']);
}
if (!empty($this->sys_user['M_UserUsername'])) {
return trim($this->sys_user['M_UserUsername']);
}
if (!empty($this->sys_user['userName'])) {
return trim($this->sys_user['userName']);
}
return 'ADMIN';
}
private function build_report_proxy_url($token, $reportCode, $orderId, $paymentId)
{
$query = array(
'token' => $token,
'code' => $reportCode,
'order_id' => $orderId,
);
if ($paymentId > 0) {
$query['payment_id'] = $paymentId;
}
return '/one-api-lab/mockup/fo/cashiernewpayment-v27/payment/stream_report_by_code?' . http_build_query($query);
}
private function resolve_request_token()
{
$rawInput = json_decode($this->input->raw_input_stream, true);
if (is_array($rawInput) && !empty($rawInput['token'])) {
return trim($rawInput['token']);
}
$postToken = $this->input->post('token', true);
if (!empty($postToken)) {
return trim($postToken);
}
$getToken = $this->input->get('token', true);
if (!empty($getToken)) {
return trim($getToken);
}
return '';
}
private function build_birt_url_by_code($reportCode, $orderId, $paymentId, $patientName)
{
$printTransaction = $this->db_onedev->query(
"SELECT Print_TransactionUrl
FROM print_transaction
WHERE Print_TransactionCode = ?
LIMIT 1",
array($reportCode)
)->row_array();
if (!$printTransaction) {
return false;
}
$username = $this->resolve_report_username();
$ts = round(microtime(true) * 1000);
$resolvedPaymentId = $paymentId > 0 ? $paymentId : $this->resolve_payment_id_by_order($orderId);
$isInternalAppUrl = $this->is_internal_app_url($printTransaction['Print_TransactionUrl']);
$replacements = array(
'PUsername' => $this->format_report_string_param($username, $isInternalAppUrl),
'PT_OrderHeaderID' => $orderId,
'PPaymentID' => $resolvedPaymentId,
'PAn' => $this->format_report_string_param($patientName, $isInternalAppUrl),
'TS' => $ts,
);
$url = $printTransaction['Print_TransactionUrl'];
foreach ($replacements as $placeholder => $value) {
if ($value === null) {
$value = '';
}
$url = str_replace($placeholder, $value, $url);
}
return $url;
}
private function resolve_fetch_url($url)
{
$url = trim((string) $url);
if ($url === '') {
return '';
}
if (preg_match('#^https?://#i', $url)) {
return $url;
}
if (strpos($url, '/birt/') === 0) {
return 'http://localhost:8080' . $url;
}
if (strpos($url, '/one-api-lab/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . $url;
}
if (strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . '/one-api-lab' . $url;
}
return 'http://localhost:8080' . $url;
}
private function is_internal_app_url($url)
{
$url = (string) $url;
return (
strpos($url, '/one-api-lab/') === 0 ||
strpos($url, '/tools/') === 0 ||
strpos($url, '/index.php/') === 0
);
}
private function format_report_string_param($value, $isInternalAppUrl = false)
{
$value = (string) $value;
if ($isInternalAppUrl) {
return rawurlencode($value);
}
return rawurlencode("'" . $value . "'");
}
}

View File

@@ -10,6 +10,7 @@ class Delivery extends MY_Controller
{
parent::__construct();
$this->db_smartone = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
public function search()
{
@@ -64,7 +65,20 @@ class Delivery extends MY_Controller
}
$prm = $this->sys_input;
$type = $prm['type'];
$id = $prm['id'];
$id = $prm['id'];
if ($type == 'patient') {
$prow = $this->db_smartone->query(
"SELECT M_PatientEmail_enc, M_PatientHP_enc FROM m_patient WHERE M_PatientID = ? LIMIT 1", [$id]
)->row_array();
$patient_email = $this->db_smartone->escape(
$this->ibl_encryptor->decrypt($prow['M_PatientEmail_enc'] ?? '') ?: 'Belum ada email pasien'
);
$patient_hp = $this->db_smartone->escape(
$this->ibl_encryptor->decrypt($prow['M_PatientHP_enc'] ?? '') ?: 'Belum ada WA pasien'
);
}
if($type == 'patient'){
$sql = "
SELECT '' as regionalcd,
@@ -110,7 +124,7 @@ class Delivery extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email pasien') as description,
IFNULL({$patient_email},'Belum ada email pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -129,7 +143,7 @@ class Delivery extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email pasien') as description,
IFNULL({$patient_email},'Belum ada email pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -148,7 +162,7 @@ class Delivery extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada WA pasien') as description,
IFNULL({$patient_hp},'Belum ada WA pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -167,7 +181,7 @@ class Delivery extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada telegram pasien') as description,
IFNULL({$patient_hp},'Belum ada telegram pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -310,6 +324,16 @@ class Delivery extends MY_Controller
foreach($rows as $k => $v){
$xval = $v['chex'] === 'N'?false:true;
//$rows[$k]['chex'] = $xval;
if ($v['delivery_code'] === 'ADDRESS' && $type === 'patient' && !empty($v['address_id'])) {
$addr_row = $this->db_smartone->query(
'SELECT M_PatientAddressDescription_enc, M_PatientAddressVillage, M_PatientAddressDistrict, M_PatientAddressCity FROM m_patientaddress WHERE M_PatientAddressID = ? LIMIT 1',
[$v['address_id']]
)->row_array();
if ($addr_row) {
$desc = $this->ibl_encryptor->decrypt($addr_row['M_PatientAddressDescription_enc'] ?? '') ?: '';
$rows[$k]['description'] = $desc . ' ' . $addr_row['M_PatientAddressVillage'] . ', ' . $addr_row['M_PatientAddressDistrict'] . ', ' . $addr_row['M_PatientAddressCity'];
}
}
}
}
$result = array("records" => $rows);

View File

@@ -12,6 +12,20 @@ class History extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
// Ambil email & HP pasien (sudah didekripsi) untuk dipakai di UNION delivery query
private function _get_patient_contact($patient_id)
{
$row = $this->db_onedev->query(
"SELECT M_PatientEmail_enc, M_PatientHP_enc FROM m_patient WHERE M_PatientID = ? LIMIT 1",
[$patient_id]
)->row_array();
return [
'email' => $this->ibl_encryptor->decrypt($row['M_PatientEmail_enc'] ?? '') ?: '',
'hp' => $this->ibl_encryptor->decrypt($row['M_PatientHP_enc'] ?? '') ?: '',
];
}
public function search()
@@ -329,9 +343,15 @@ class History extends MY_Controller
function search_deliveries($prm)
{
$type = $prm['type'];
$id = $prm['id'];
$id = $prm['id'];
if ($type == 'patient') {
$contact = $this->_get_patient_contact($id);
$patient_email = $this->db_onedev->escape($contact['email'] ?: 'Belum ada email pasien');
$patient_hp = $this->db_onedev->escape($contact['hp'] ?: 'Belum ada WA pasien');
}
if($type == 'patient'){
$sql = "
SELECT 0 as kelurahan,
@@ -377,7 +397,7 @@ class History extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email pasien') as description,
IFNULL({$patient_email},'Belum ada email pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -394,7 +414,7 @@ class History extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email pasien') as description,
IFNULL({$patient_email},'Belum ada email pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -411,7 +431,7 @@ class History extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada WA pasien') as description,
IFNULL({$patient_hp},'Belum ada WA pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -428,7 +448,7 @@ class History extends MY_Controller
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada telegram pasien') as description,
IFNULL({$patient_hp},'Belum ada telegram pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,

View File

@@ -12,6 +12,7 @@ class Order extends MY_Controller
{
parent::__construct();
$this->db_smartone = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
function get_time_start(){
@@ -747,7 +748,8 @@ function endshowtime()
T_OrderHeaderSubTotal as order_subtotal,
T_OrderHeaderRounding as order_rounding,
T_OrderHeaderTotal as order_total,
concat(if(M_TitleID is null, '', concat(M_TitleName, ' ')),IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) as patient_name,
M_PatientName_enc, IFNULL(M_TitleName,'') M_TitleName,
IFNULL(M_PatientPrefix,'') M_PatientPrefix, IFNULL(M_PatientSuffix,'') M_PatientSuffix,
M_PatientNoReg as patient_mr,
M_MouName as order_mou,
CorporateName as order_company,
@@ -767,9 +769,14 @@ function endshowtime()
where T_OrderHeaderID = {$id}";
//echo $sql;
$query = $this->db_smartone->query($sql);
$rows = $query->row();
$rows = $query->row_array();
$pname = $this->ibl_encryptor->decrypt($rows['M_PatientName_enc'] ?? '');
$rows['patient_name'] = trim(implode(' ', array_filter([
$rows['M_TitleName'], $rows['M_PatientPrefix'], $pname, $rows['M_PatientSuffix']
])));
unset($rows['M_PatientName_enc'], $rows['M_TitleName'], $rows['M_PatientPrefix'], $rows['M_PatientSuffix']);
//echo $this->db_smartone->last_query();
return $rows;
return (object) $rows;
}
function get_delivery($id){
@@ -876,9 +883,21 @@ function endshowtime()
function search_deliveries($prm)
{
$type = $prm['type'];
$id = $prm['id'];
$id = $prm['id'];
if ($type == 'patient') {
$prow = $this->db_smartone->query(
"SELECT M_PatientEmail_enc, M_PatientHP_enc FROM m_patient WHERE M_PatientID = ? LIMIT 1", [$id]
)->row_array();
$patient_email = $this->db_smartone->escape(
$this->ibl_encryptor->decrypt($prow['M_PatientEmail_enc'] ?? '') ?: 'Belum ada email pasien'
);
$patient_hp = $this->db_smartone->escape(
$this->ibl_encryptor->decrypt($prow['M_PatientHP_enc'] ?? '') ?: 'Belum ada WA pasien'
);
}
if($type == 'patient'){
$sql = "
SELECT 0 as kelurahan,
@@ -924,7 +943,7 @@ function endshowtime()
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email pasien') as description,
IFNULL({$patient_email},'Belum ada email pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -941,7 +960,7 @@ function endshowtime()
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email pasien') as description,
IFNULL({$patient_email},'Belum ada email pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -958,7 +977,7 @@ function endshowtime()
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada WA pasien') as description,
IFNULL({$patient_hp},'Belum ada WA pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -975,7 +994,7 @@ function endshowtime()
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada telegram pasien') as description,
IFNULL({$patient_hp},'Belum ada telegram pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,

View File

@@ -13,6 +13,7 @@ class Order extends MY_Controller
parent::__construct();
$this->db_smartone = $this->load->database("onedev", true);
$this->db_log = $this->load->database("one_lab_log", true);
$this->load->library('ibl_encryptor');
$this->load->helper("uuid");
}
@@ -54,17 +55,61 @@ class Order extends MY_Controller
return $rst;
}
function convert_to_normal_text($text)
{
function convert_to_normal_text($text)
{
$normal_characters = "a-zA-Z0-9\s`~!@#$%^&*()_+-={}|:;<>?,.\/\"\'\\\[\]";
$normal_text = preg_replace("/[^$normal_characters]/", '', $text);
return $normal_text;
}
function genqrcode($nomorlab, $id)
{
return $normal_text;
}
protected function build_patient_visit_info($patient_id, $patient_dob)
{
$visit = 1;
$birthday = 'N';
$visit_query = $this->db_onedev->query(
"SELECT COUNT(DISTINCT T_OrderHeaderID) AS n
FROM t_orderheader
JOIN t_orderdetail ON T_OrderHeaderID = T_OrderDetailT_OrderHeaderID AND T_OrderDetailIsActive = 'Y'
WHERE T_OrderHeaderIsActive = 'Y'
AND T_OrderHeaderM_PatientID = ?",
[$patient_id]
);
if ($visit_query) {
$visit_row = $visit_query->row_array();
$visit += (int) ($visit_row['n'] ?? 0);
}
$init_visit_query = $this->db_onedev->query(
"SELECT M_PatientInitialVisit
FROM m_patient
WHERE M_PatientID = ?",
[$patient_id]
);
if ($init_visit_query) {
$init_visit_row = $init_visit_query->row_array();
if (!empty($init_visit_row['M_PatientInitialVisit'])) {
$visit += (int) $init_visit_row['M_PatientInitialVisit'];
}
}
$dob_time = empty($patient_dob) ? false : strtotime($patient_dob);
if ($dob_time !== false) {
$birthday = date('m-d', $dob_time) === date('m-d') ? 'Y' : 'N';
}
return json_decode(json_encode([
'visit' => $visit,
'birthday' => $birthday,
]));
}
function genqrcode($nomorlab, $id)
{
$this->load->library('ciqrcode'); //pemanggilan library QR CODE
$home_dir = "/home/one/project/one/";
$target_dir = $home_dir . "one-media/one-qrcontrolcard/";
@@ -163,47 +208,47 @@ class Order extends MY_Controller
return lab_uuid_v4();
}
function generate_code_form($preid, $orderid)
{
$userid = $this->sys_user["M_UserID"];
$sql = "SELECT FormRiwayatPasienID, FormRiwayatPasienCode, FormRiwayatPasienUUID, FormRiwayatPasienT_OrderHeaderID
FROM form_riwayat_pasien
WHERE FormRiwayatPasienPreregisterID = ? AND
FormRiwayatPasienIsActive = 'Y'
ORDER BY FormRiwayatPasienID DESC
LIMIT 1";
$qry = $this->db_onedev->query($sql, [$preid]);
if (!$qry) {
return '';
}
$exist = $qry->row_array();
if ($exist) {
if ((int) $exist['FormRiwayatPasienT_OrderHeaderID'] !== (int) $orderid) {
$sql = "UPDATE form_riwayat_pasien
SET FormRiwayatPasienT_OrderHeaderID = ?,
FormRiwayatPasienLasUpdated = NOW(),
FormRiwayatPasienLasUpdatedUserID = ?
WHERE FormRiwayatPasienID = ?";
$qry = $this->db_onedev->query($sql, [$orderid, $userid, $exist['FormRiwayatPasienID']]);
if (!$qry) {
return '';
}
}
return array('uuid' => $exist['FormRiwayatPasienUUID'], 'code' => $exist['FormRiwayatPasienCode']);
}
$sql = "SELECT COUNT(*) as total
FROM form_riwayat_pasien
WHERE FormRiwayatPasienT_OrderHeaderID = ? AND
FormRiwayatPasienIsActive = 'Y'
";
$qry = $this->db_onedev->query($sql, [$orderid]);
if ($qry) {
$total = $qry->result_array()[0]['total'];
if ($total == 0) {
$code = $this->generate_code_string();
$uuid = $this->generate_uuid();
function generate_code_form($preid, $orderid)
{
$userid = $this->sys_user["M_UserID"];
$sql = "SELECT FormRiwayatPasienID, FormRiwayatPasienCode, FormRiwayatPasienUUID, FormRiwayatPasienT_OrderHeaderID
FROM form_riwayat_pasien
WHERE FormRiwayatPasienPreregisterID = ? AND
FormRiwayatPasienIsActive = 'Y'
ORDER BY FormRiwayatPasienID DESC
LIMIT 1";
$qry = $this->db_onedev->query($sql, [$preid]);
if (!$qry) {
return '';
}
$exist = $qry->row_array();
if ($exist) {
if ((int) $exist['FormRiwayatPasienT_OrderHeaderID'] !== (int) $orderid) {
$sql = "UPDATE form_riwayat_pasien
SET FormRiwayatPasienT_OrderHeaderID = ?,
FormRiwayatPasienLasUpdated = NOW(),
FormRiwayatPasienLasUpdatedUserID = ?
WHERE FormRiwayatPasienID = ?";
$qry = $this->db_onedev->query($sql, [$orderid, $userid, $exist['FormRiwayatPasienID']]);
if (!$qry) {
return '';
}
}
return array('uuid' => $exist['FormRiwayatPasienUUID'], 'code' => $exist['FormRiwayatPasienCode']);
}
$sql = "SELECT COUNT(*) as total
FROM form_riwayat_pasien
WHERE FormRiwayatPasienT_OrderHeaderID = ? AND
FormRiwayatPasienIsActive = 'Y'
";
$qry = $this->db_onedev->query($sql, [$orderid]);
if ($qry) {
$total = $qry->result_array()[0]['total'];
if ($total == 0) {
$code = $this->generate_code_string();
$uuid = $this->generate_uuid();
$sql = "INSERT INTO form_riwayat_pasien (
FormRiwayatPasienPreregisterID,
@@ -216,14 +261,14 @@ class Order extends MY_Controller
$qry = $this->db_onedev->query($sql, [$preid, $code, $uuid, $orderid, $userid]);
//echo $this->db_onedev->last_query();
//exit;
if (!$qry) {
return '';
}
return array('uuid' => $uuid, 'code' => $code);
}
}
return '';
}
if (!$qry) {
return '';
}
return array('uuid' => $uuid, 'code' => $code);
}
}
return '';
}
function check_duplicate_nat_tests($data)
{
@@ -535,6 +580,15 @@ class Order extends MY_Controller
}
}
// Link ke order klinik jika klinik_number dikirim
$klinik_number = trim($header['klinik_number'] ?? '');
if ($klinik_number !== '') {
$this->db_smartone->query(
"UPDATE one_klinik.`order` SET orderT_OrderHeaderID = ? WHERE OrderNumber = ?",
[$header_id, $klinik_number]
);
}
$sql = "SELECT t_orderheader.*
FROM t_orderheader
WHERE
@@ -734,8 +788,13 @@ class Order extends MY_Controller
// Strip PII dari header sebelum masuk log (identitas pasien di m_patient, sudah terenkripsi)
$header_for_log = is_array($order_header) ? $order_header : (array)$order_header;
foreach (['patient_name','patient_address','patient_phone','patient_email','patient_hp'] as $_pii) {
unset($header_for_log[$_pii]);
}
$data_log = $sanitize_for_json(array(
'header' => $order_header,
'header' => $header_for_log,
'detail_order' => $order_detail,
'delivery' => $order_delivery,
'promise' => $order_promise,
@@ -792,13 +851,27 @@ class Order extends MY_Controller
$this->sys_error($url_preregister['message']);
exit;
}
$url_preregister = $url_preregister['data'];
$dt_order = [
'status' => 'OK',
'order_id' => $x_data_array['T_OrderHeaderID'],
'order_date' => $x_data_array['T_OrderHeaderDate'],
$url_preregister = $url_preregister['data'];
$order_klinik = null;
if ($klinik_number !== '') {
$query_order_klinik = $this->db_onedev->query(
"SELECT *
FROM one_klinik.`order`
WHERE OrderNumber = ?
LIMIT 1",
[$klinik_number]
);
if ($query_order_klinik) {
$order_klinik = $query_order_klinik->row_array();
}
}
$dt_order = [
'status' => 'OK',
'order_id' => $x_data_array['T_OrderHeaderID'],
'order_date' => $x_data_array['T_OrderHeaderDate'],
'noreg' => $lab_number,
'qrcode' => $genqrcode,
'patient_qrcode' => $genpatientqrcode,
@@ -812,11 +885,12 @@ class Order extends MY_Controller
'order_detail' => $order_detail,
'order_header' => $order_header,
'inform_consent' => isset($order_header['inform_consent']) ? $order_header['inform_consent'] : [],
'order_promise' => $order_promise,
'order_details' => $order_details,
'report_url' => $report_url,
'location_warning' => $location_warning,
];
'order_promise' => $order_promise,
'order_details' => $order_details,
'order_klinik' => $order_klinik,
'report_url' => $report_url,
'location_warning' => $location_warning,
];
if ($internal)
return ['status' => 'OK', 'data' => $dt_order];
@@ -979,7 +1053,8 @@ class Order extends MY_Controller
$sql = "SELECT M_DeliveryTypeCode as xtype,
M_DeliverySource as source,
M_DeliveryName as label,
IFNULL(T_OrderDeliveryNoteValue,T_OrderDeliveryDestination) as xdesc
T_OrderDeliveryDestination_enc,
IFNULL(T_OrderDeliveryNoteValue, T_OrderDeliveryDestination) as xdesc_fallback
FROM t_orderdelivery
JOIN m_deliverytype ON T_OrderDeliveryM_DeliveryTypeID = M_DeliveryTypeID
JOIN m_delivery ON T_OrderDeliveryM_DeliveryID = M_DeliveryID
@@ -1004,11 +1079,13 @@ class Order extends MY_Controller
$data = $query->result_array();
$rst = [];
foreach ($data as $key => $value) {
$dest_enc = $value['T_OrderDeliveryDestination_enc'] ?? '';
$dest = $dest_enc ? ($this->ibl_encryptor->decrypt($dest_enc) ?: $value['xdesc_fallback']) : $value['xdesc_fallback'];
$rst[] = array(
'type' => $value['xtype'],
'type' => $value['xtype'],
'source' => $value['source'],
'label' => $value['label'],
'desc' => $value['xdesc']
'label' => $value['label'],
'desc' => $dest
);
}
$return['data'] = $rst;
@@ -1039,19 +1116,24 @@ class Order extends MY_Controller
T_OrderHeaderSubTotal as order_subtotal,
T_OrderHeaderTotal as order_total,
M_PatientNoReg as patient_mr,
M_PatientName as patient_name,
CONCAT(M_PatientAddressDescription,'<br>',IF(M_PatientAddressVillage IS NULL,'',CONCAT(M_PatientAddressVillage,', ')),IF(M_PatientAddressDistrict IS NULL,'',CONCAT(M_PatientAddressDistrict,', ')),IF(M_PatientAddressCity IS NULL,'',M_PatientAddressCity)) as patient_address,
M_PatientPhone as patient_phone,
M_PatientEmail as patient_email,
M_PatientName_enc, M_PatientAddressDescription_enc,
M_PatientPhone_enc, M_PatientEmail_enc,
t_orderheader.*,
IFNULL(Nat_CitoName,'') as cito_name,
IFNULL(Mgm_McuNumber,'') as mcu_number,
IFNULL(Mgm_McuLabel,'') as mcu_label,
IFNULL(latest_sig.Patient_SignatureUrl,'') as image_signature
IFNULL(latest_sig.Patient_SignatureUrl,'') as image_signature
FROM `t_orderheader`
JOIN `t_orderheaderaddon` ON T_OrderHeaderAddOnT_OrderHeaderID = T_OrderHeaderID AND T_OrderHeaderAddOnIsActive = 'Y'
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
JOIN m_patientaddress ON M_PatientAddressM_PatientID = M_PatientID AND M_PatientAddressIsActive = 'Y' AND M_PatientAddressNote = 'Utama'
LEFT JOIN m_patientaddress ON M_PatientAddressID = (
SELECT pa.M_PatientAddressID
FROM m_patientaddress pa
WHERE pa.M_PatientAddressM_PatientID = M_PatientID
AND pa.M_PatientAddressIsActive = 'Y'
ORDER BY (pa.M_PatientAddressNote = 'Utama') DESC, pa.M_PatientAddressID DESC
LIMIT 1
)
JOIN m_company ON T_OrderHeaderM_CompanyID = M_CompanyID
JOIN m_mou ON T_OrderHeaderM_MouID = M_MouID
JOIN m_doctor pj1 ON T_OrderHeaderPjM_DoctorID = pj1.M_DoctorID
@@ -1060,16 +1142,16 @@ class Order extends MY_Controller
LEFT JOIN m_doctoraddress sender_address ON T_OrderHeaderSenderM_DoctorAddressID = M_DoctorAddressID
LEFT JOIN nat_cito ON T_OrderHeaderNat_CitoID = Nat_CitoID
LEFT JOIN mgm_mcu ON T_OrderHeaderMgm_McuID = Mgm_McuID
LEFT JOIN (
SELECT ps.Patient_SignatureM_PatientID, ps.Patient_SignatureUrl
FROM patient_signature ps
JOIN (
SELECT Patient_SignatureM_PatientID, MAX(Patient_SignatureID) as Patient_SignatureID
FROM patient_signature
WHERE Patient_SignatureIsActive = 'Y'
GROUP BY Patient_SignatureM_PatientID
) latest_sig_id ON latest_sig_id.Patient_SignatureID = ps.Patient_SignatureID
) latest_sig ON latest_sig.Patient_SignatureM_PatientID = M_PatientID AND latest_sig.Patient_SignatureM_PatientID = T_OrderHeaderM_PatientID
LEFT JOIN (
SELECT ps.Patient_SignatureM_PatientID, ps.Patient_SignatureUrl
FROM patient_signature ps
JOIN (
SELECT Patient_SignatureM_PatientID, MAX(Patient_SignatureID) as Patient_SignatureID
FROM patient_signature
WHERE Patient_SignatureIsActive = 'Y'
GROUP BY Patient_SignatureM_PatientID
) latest_sig_id ON latest_sig_id.Patient_SignatureID = ps.Patient_SignatureID
) latest_sig ON latest_sig.Patient_SignatureM_PatientID = M_PatientID AND latest_sig.Patient_SignatureM_PatientID = T_OrderHeaderM_PatientID
WHERE `T_OrderHeaderID` = ?
GROUP BY T_OrderHeaderID";
$query = $this->db_smartone->query($sql, [$order_id]);
@@ -1093,6 +1175,20 @@ class Order extends MY_Controller
$data = $query->row_array();
if (is_array($data) && count($data) > 0) {
$enc = $this->ibl_encryptor;
$addr_raw = $enc->decrypt($data['M_PatientAddressDescription_enc'] ?? '');
$data['patient_name'] = $enc->decrypt($data['M_PatientName_enc'] ?? '');
$data['patient_address'] = implode('<br>', array_filter([
$addr_raw,
$data['M_PatientAddressVillage'] ?? '',
$data['M_PatientAddressDistrict'] ?? '',
$data['M_PatientAddressCity'] ?? '',
]));
$data['patient_phone'] = $enc->decrypt($data['M_PatientPhone_enc'] ?? '');
$data['patient_email'] = $enc->decrypt($data['M_PatientEmail_enc'] ?? '');
foreach (array_keys($data) as $col) {
if (substr($col, -4) === '_enc') unset($data[$col]);
}
$data['inform_consent'] = $this->get_inform_consent_by_order($order_id);
}
$return['data'] = $data;
@@ -1404,9 +1500,9 @@ class Order extends MY_Controller
$sql = "SELECT
CONCAT(IF(M_TitleID is null, '', concat(M_TitleName, ' ')),IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) as full_patient_name
FROM m_patient
$sql = "SELECT M_PatientName_enc, IFNULL(M_TitleName,'') M_TitleName,
IFNULL(M_PatientPrefix,'') M_PatientPrefix, IFNULL(M_PatientSuffix,'') M_PatientSuffix
FROM m_patient
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
WHERE M_PatientID = ? LIMIT 1";
$query_patient = $this->db_smartone->query($sql, [$header['patient_id']]);
@@ -1417,6 +1513,10 @@ class Order extends MY_Controller
$this->db_smartone->trans_rollback();
}
$rows_patient = $query_patient->row_array();
$pname = $this->ibl_encryptor->decrypt($rows_patient['M_PatientName_enc'] ?? '');
$rows_patient['full_patient_name'] = trim(implode(' ', array_filter([
$rows_patient['M_TitleName'], $rows_patient['M_PatientPrefix'], $pname, $rows_patient['M_PatientSuffix']
])));
$full_patient_name = $rows_patient['full_patient_name'];
$sql = "INSERT INTO t_orderheaderaddon (
@@ -2410,16 +2510,18 @@ class Order extends MY_Controller
T_OrderDeliveryM_DeliveryID,
T_OrderDeliveryM_DeliveryTypeID,
T_OrderDeliveryDestination,
T_OrderDeliveryDestination_enc,
T_OrderDeliveryAddressID,
T_OrderDeliveryRegionalCd,
T_OrderDeliveryCreated,
T_OrderDeliveryCreatedUserID
) VALUES (?,?,?,?,?,?,NOW(),?)";
) VALUES (?,?,?,?,?,?,?,NOW(),?)";
$prm_orderdelivery = [
$header_id,
$delivery['delivery_id'],
$delivery['delivery_type_id'],
$destination,
$this->ibl_encryptor->encrypt($destination),
$delivery['address_id'],
$delivery['regional_cd'],
$userid
@@ -2480,18 +2582,23 @@ class Order extends MY_Controller
// START
if ($delivery['delivery_type_id'] == 3) {
$sql_header_info = "SELECT
CONCAT(IFNULL(M_TitleName,''),'. ', IFNULL(M_PatientPrefix,''),M_PatientName, IFNULL(M_PatientSuffix,'')) as patient_fullname,
$sql_header_info = "SELECT
M_PatientName_enc, IFNULL(M_TitleName,'') M_TitleName,
IFNULL(M_PatientPrefix,'') M_PatientPrefix, IFNULL(M_PatientSuffix,'') M_PatientSuffix,
M_CompanyName as corporate_name,
CONCAT(IFNULL(M_DoctorPrefix,''),IFNULL(M_DoctorPrefix2,''),' ',M_DoctorName,IFNULL(M_DoctorSufix,''),IFNULL(M_DoctorSufix2,''),IFNULL(M_DoctorSufix3,'')) as doctor_fullname
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
JOIN m_title ON M_PatientM_TitleID = M_TitleID
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_company ON T_OrderHeaderM_CompanyID = M_CompanyID
JOIN m_doctor ON T_OrderHeaderSenderM_DoctorID = M_DoctorID
WHERE T_OrderHeaderID = ?";
$q_h = $this->db_smartone->query($sql_header_info, [$header_id]);
$d_h = $q_h->row_array();
$pname_del = $this->ibl_encryptor->decrypt($d_h['M_PatientName_enc'] ?? '');
$d_h['patient_fullname'] = trim(implode(' ', array_filter([
$d_h['M_TitleName'], $d_h['M_PatientPrefix'], $pname_del, $d_h['M_PatientSuffix']
])));
$sql_del_source = "SELECT M_DeliverySource FROM m_delivery WHERE M_DeliveryID = ?";
$q_d = $this->db_smartone->query($sql_del_source, [$delivery['delivery_id']]);
@@ -3490,7 +3597,8 @@ GROUP BY T_SampleStationID ";
T_OrderHeaderSubTotal as order_subtotal,
T_OrderHeaderRounding as order_rounding,
T_OrderHeaderTotal as order_total,
concat(if(M_TitleID is null, '', concat(M_TitleName, ' ')),IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) as patient_name,
M_PatientName_enc, IFNULL(M_TitleName,'') M_TitleName,
IFNULL(M_PatientPrefix,'') M_PatientPrefix, IFNULL(M_PatientSuffix,'') M_PatientSuffix,
M_PatientNoReg as patient_mr,
M_MouName as order_mou,
CorporateName as order_company,
@@ -3510,9 +3618,13 @@ GROUP BY T_SampleStationID ";
where T_OrderHeaderID = {$id}";
//echo $sql;
$query = $this->db_smartone->query($sql);
$rows = $query->row();
//echo $this->db_smartone->last_query();
return $rows;
$rows = $query->row_array();
$pname = $this->ibl_encryptor->decrypt($rows['M_PatientName_enc'] ?? '');
$rows['patient_name'] = trim(implode(' ', array_filter([
$rows['M_TitleName'], $rows['M_PatientPrefix'], $pname, $rows['M_PatientSuffix']
])));
unset($rows['M_PatientName_enc'], $rows['M_TitleName'], $rows['M_PatientPrefix'], $rows['M_PatientSuffix']);
return (object) $rows;
}
function get_delivery($id)
@@ -3619,7 +3731,19 @@ GROUP BY T_SampleStationID ";
{
$type = $prm['type'];
$id = $prm['id'];
$id = $prm['id'];
if ($type == 'patient') {
$prow = $this->db_smartone->query(
"SELECT M_PatientEmail_enc, M_PatientHP_enc FROM m_patient WHERE M_PatientID = ? LIMIT 1", [$id]
)->row_array();
$patient_email = $this->db_smartone->escape(
$this->ibl_encryptor->decrypt($prow['M_PatientEmail_enc'] ?? '') ?: 'Belum ada email'
);
$patient_hp = $this->db_smartone->escape(
$this->ibl_encryptor->decrypt($prow['M_PatientHP_enc'] ?? '') ?: 'Belum ada WA pasien'
);
}
if ($type == 'patient') {
$sql = "SELECT 0 as kelurahan,
'' as regional_cd,
@@ -3664,7 +3788,7 @@ GROUP BY T_SampleStationID ";
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email') as description,
IFNULL({$patient_email},'Belum ada email') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -3682,7 +3806,7 @@ GROUP BY T_SampleStationID ";
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientEmail,'Belum ada email') as description,
IFNULL({$patient_email},'Belum ada email') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -3700,7 +3824,7 @@ GROUP BY T_SampleStationID ";
M_DeliveryM_DeliveryTypeID as delivery_type,
M_DeliveryID as delivery_id,
M_DeliveryName as delivery_name,
IFNULL(M_PatientHP,'Belum ada WA pasien') as description,
IFNULL({$patient_hp},'Belum ada WA pasien') as description,
'N' as chex,
'' as note,
'origin' as typeform,
@@ -3937,8 +4061,7 @@ GROUP BY T_SampleStationID ";
$patient = $query->row_array();
$patient['M_PatientName'] = stripslashes($patient['M_PatientName']);
$patient['M_PatientAddress'] = stripslashes($patient['full_address']);
$info = $this->db_onedev->query("SELECT fn_fo_patient_visit(?) info", [$patient['M_PatientID']])->row();
$patient['info'] = json_decode($info->info);
$patient['info'] = $this->build_patient_visit_info($patient['M_PatientID'], $patient['M_PatientDOB']);
$rst['patient'] = $patient;
}
@@ -4592,4 +4715,208 @@ GROUP BY T_SampleStationID ";
exit;
}
function load_klinik()
{
if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; }
$prm = $this->sys_input;
$klinik_number = trim($prm['klinik_number'] ?? '');
if (!$klinik_number) { $this->sys_error("klinik_number required"); exit; }
// Ambil header order klinik
$row_header = $this->db_onedev->query(
"SELECT o.*, s.*, od.orderDoctorDiagnosePrimer
FROM one_klinik.`order` o
JOIN one_klinik.setting s ON s.settingIsActive = 'Y'
LEFT JOIN one_klinik.order_doctor od
ON od.orderDoctorOrderID = o.orderID
AND od.orderDoctorIsActive = 'Y'
AND od.orderDoctorType = 'TEXT'
WHERE o.OrderNumber = ?
LIMIT 1",
[$klinik_number]
)->row_array();
if (!$row_header) { $this->sys_error("Order tidak ditemukan"); exit; }
$rst = [];
$rst['klinik'] = $row_header;
$enc = $this->ibl_encryptor;
// Patient
$patient_row = $this->db_onedev->query(
"SELECT m_patient.*,
M_TitleID, M_TitleName,
M_SexID, M_SexName,
M_PatientAddressM_KelurahanID as M_KelurahanID,
M_PatientAddressDescription,
IFNULL(M_ReligionName,'-') as M_ReligionName
FROM m_patient
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_patientaddress ON M_PatientAddressM_PatientID = M_PatientID AND M_PatientAddressIsActive = 'Y'
LEFT JOIN m_religion ON M_PatientM_ReligionID = M_ReligionID
WHERE M_PatientID = ?
GROUP BY M_PatientID
LIMIT 1",
[$row_header['orderM_PatientID']]
)->row_array();
if ($patient_row) {
$p_name = $enc->decrypt($patient_row['M_PatientName_enc'] ?? '') ?: $patient_row['M_PatientName'];
$p_hp = $enc->decrypt($patient_row['M_PatientHP_enc'] ?? '') ?: $patient_row['M_PatientHP'];
$p_email = $enc->decrypt($patient_row['M_PatientEmail_enc'] ?? '') ?: $patient_row['M_PatientEmail'];
$p_idnum = $enc->decrypt($patient_row['M_PatientIDNumber_enc']?? '') ?: $patient_row['M_PatientIDNumber'];
$p_dob_raw = $enc->decrypt($patient_row['M_PatientDOB_enc'] ?? '');
// p_dob_raw is d-m-Y; convert to Y-m-d for M_PatientDOB, keep d-m-Y for dob_ina
$p_dob_ina = $p_dob_raw ?: $patient_row['M_PatientDOB'];
$p_dob_sql = '';
if ($p_dob_raw) {
$parts = explode('-', $p_dob_raw);
$p_dob_sql = count($parts) === 3 ? "{$parts[2]}-{$parts[1]}-{$parts[0]}" : '';
}
$title = $patient_row['M_TitleName'] ? $patient_row['M_TitleName'] . ' ' : '';
$prefix = $patient_row['M_PatientPrefix'] ? $patient_row['M_PatientPrefix'] . ' ': '';
$suffix = $patient_row['M_PatientSuffix'] ? ' ' . $patient_row['M_PatientSuffix']: '';
$patient_row['M_PatientName'] = trim($title . $prefix . $p_name . $suffix);
$patient_row['M_PatientRealName'] = $p_name;
$patient_row['M_PatientHP'] = $p_hp;
$patient_row['M_PatientEmail'] = $p_email;
$patient_row['M_PatientIDNumber'] = $p_idnum;
$patient_row['M_PatientDOB'] = $p_dob_sql ?: $patient_row['M_PatientDOB'];
$patient_row['dob_ina'] = $p_dob_ina;
$patient_row['divider'] = 'N';
$patient_row['hp'] = $p_hp;
$patient_row['M_PatientAddress'] = '';
$patient_row['M_DistrictID'] = 0;
$patient_row['M_CityID'] = 0;
$patient_row['M_ProvinceID'] = 0;
if ($patient_row['M_KelurahanID']) {
$addr = $this->db_onedev->query(
"SELECT *, CONCAT(IFNULL(?,''),'\n\n',M_KelurahanName,', ',M_DistrictName,'\n',M_CityName,', ',M_ProvinceName) as xaddress
FROM m_kelurahan
JOIN m_district ON M_KelurahanM_DistrictID = M_DistrictID
JOIN m_city ON M_DistrictM_CityID = M_CityID
JOIN m_province ON M_CityM_ProvinceID = M_ProvinceID
WHERE M_KelurahanID = ?",
[$patient_row['M_PatientAddressDescription'], $patient_row['M_KelurahanID']]
)->row_array();
if ($addr) {
$patient_row['M_PatientAddress'] = stripslashes($addr['xaddress']);
$patient_row['M_DistrictID'] = $addr['M_DistrictID'];
$patient_row['M_CityID'] = $addr['M_CityID'];
$patient_row['M_ProvinceID'] = $addr['M_ProvinceID'];
}
}
$patient_row['info'] = $this->build_patient_visit_info($patient_row['M_PatientID'], $patient_row['M_PatientDOB']);
// Hapus kolom enc sebelum return
foreach (['M_PatientName_enc','M_PatientName_bidx','M_PatientHP_enc','M_PatientHP_bidx',
'M_PatientEmail_enc','M_PatientIDNumber_enc','M_PatientNIK_bidx',
'M_PatientDOB_enc','M_PatientDOB_bidx'] as $col) {
unset($patient_row[$col]);
}
$rst['patient'] = $patient_row;
} else {
$rst['patient'] = [];
}
// MOU & Company dari order
$mou_id = intval($row_header['orderM_MouID'] ?? 0);
$row_mou = $this->db_onedev->query(
"SELECT M_MouM_CompanyID, M_MouStatus, M_MouEmail, M_MouEmailIsDefault,
M_MouEndDate, M_MouID, M_MouIsBill, M_MouIsDefault, M_MouName,
M_MouNote, M_MouStartDate
FROM m_mou WHERE M_MouID = ?",
[$mou_id]
)->row_array();
$row_company = [];
if ($row_mou) {
$row_company = $this->db_onedev->query(
"SELECT * FROM m_company WHERE M_CompanyID = ?",
[$row_mou['M_MouM_CompanyID']]
)->row_array();
$row_company['mou'] = $this->db_onedev->query(
"SELECT M_MouStatus, M_MouEmail, M_MouEmailIsDefault, M_MouEndDate, M_MouID,
M_MouIsBill, M_MouIsDefault, M_MouName, M_MouNote, M_MouStartDate
FROM m_mou
WHERE M_MouM_CompanyID = ? AND M_MouStatus = 'R' AND M_MouIsActive = 'Y'",
[$row_company['M_CompanyID']]
)->result_array();
}
$rst['selected_mou'] = $row_mou ?: [];
$rst['selected_company'] = $row_company ?: [];
$rst['companies'] = $row_company ? [$row_company] : [];
$rst['tests'] = [];
$sql = "SELECT ss_price_mou.*
FROM one_klinik.order_penunjang
JOIN t_test
ON orderPenunjangT_TestID = t_test.T_TestID
AND t_test.T_TestIsActive = 'Y'
JOIN ss_price_mou
ON Ss_PriceMouM_MouID = ?
AND t_test.T_TestID = ss_price_mou.T_TestID
AND is_packet = 'N'
WHERE orderPenunjangOrderID = ?
AND orderPenunjangIsActive = 'Y'
GROUP BY ss_price_mou.T_TestID";
$qry_tests = $this->db_onedev->query($sql, [
$row_header['settingM_MouID'],
$row_header['orderID']
]);
if (!$qry_tests) {
$this->sys_error_db("tests query: " . $this->db_onedev->error()['message'], $this->db_onedev);
exit;
}
$get_tests_from_detail = $qry_tests->result_array();
if ($get_tests_from_detail) {
foreach ($get_tests_from_detail as $value) {
$data_ss_price = $value;
$data_ss_price['requirement'] = [];
if ($data_ss_price['px_type'] == "PX") {
$x = $this->db_smartone->query(
"SELECT fn_fo_requirement_get(?) x",
[$data_ss_price['T_TestID']]
)->row();
if ($x && $x->x != null) {
$data_ss_price['requirement'] = json_decode($x->x);
}
}
if ($data_ss_price['is_packet'] == 'N') {
$tests = $data_ss_price['T_PriceT_TestID'];
$panels = '';
} else {
$tests = '';
$panels = $data_ss_price['T_PriceT_TestID'];
}
$x = $this->db_smartone->query(
"SELECT fn_fo_find_promise_by_px(?, ?) as x",
[$tests, $panels]
)->row();
if ($x && $x->x != null) {
$data_ss_price['promise'] = $x->x;
}
$data_ss_price['nat_test'] = json_decode($data_ss_price['nat_test']);
$data_ss_price['child_test'] = json_decode($data_ss_price['child_test']);
$rst['tests'][] = $data_ss_price;
}
}
$rst['diagnose'] = $row_header['orderDoctorDiagnosePrimer'] ?? '';
$this->sys_ok(['records' => $rst]);
exit;
}
}

View File

@@ -28,7 +28,34 @@ class Patient extends MY_Controller
{
parent::__construct();
$this->db_smartone = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
// Masking untuk kolom plaintext lama (data asli di _enc)
private function _mask_name($v) {
if (!$v) return $v;
$v = trim($v);
$words = preg_split('/\s+/', $v);
if (count($words) === 1) {
$l = mb_strlen($v, 'UTF-8');
if ($l <= 2) return $v;
return mb_substr($v, 0, 2, 'UTF-8') . str_repeat('*', $l - 2);
}
// Multi kata: kata pertama penuh + inisial kata berikutnya + *
$first = $words[0];
$rest = array_slice($words, 1);
$masked = array_map(function($w) {
if (!$w) return '';
$init = mb_substr($w, 0, 1, 'UTF-8');
return $init . str_repeat('*', max(3, mb_strlen($w, 'UTF-8') - 1));
}, $rest);
return $first . ' ' . implode(' ', $masked);
}
private function _mask_phone($v) { if (!$v) return $v; $d=preg_replace('/[^0-9]/','',trim($v)); $l=strlen($d); if($l<=4) return '****'; if($l<=8) return substr($d,0,4).str_repeat('*',$l-4); return substr($d,0,4).str_repeat('*',$l-7).substr($d,-3); }
private function _mask_email($v) { if (!$v||strpos($v,'@')===false) return $v; [$loc,$dom]=explode('@',$v,2); return mb_substr($loc,0,min(2,mb_strlen($loc,'UTF-8')),'UTF-8').'***@'.$dom; }
private function _mask_short($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=2) return '***'; return mb_substr($v,0,2,'UTF-8').'***'; }
private function _mask_id($v) { if (!$v) return $v; $v=trim($v); $l=strlen($v); if($l<=4) return '****'; return substr($v,0,4).str_repeat('*',max(3,$l-6)).($l>6?substr($v,-2):''); }
private function _mask_address($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=5) return '***'; return mb_substr($v,0,5,'UTF-8').'***'; }
function _add_address(&$pat) {
if (count($pat) == "0") {
return array();
@@ -76,113 +103,194 @@ class Patient extends MY_Controller
{
$prm = $this->sys_input;
$max_rst = 100;
$tot_count =0;
$number_limit = 10;
$number_offset = (!isset($prm['current_page'])?1:$prm['current_page'] - 1) * $number_limit ;
$number_limit = 10;
$number_offset = (!isset($prm['current_page']) ? 1 : $prm['current_page'] - 1) * $number_limit;
$q = [
'noreg' => "",
'name' => '',
'hp' => '',
'dob' => '',
'address' => ''
];
$where_noreg = '';
$where_name = '';
$where_hp = '';
$where_dob = '';
$where_nik = '';
$search_address = '';
if (!empty($prm['noreg'])) {
$noreg = $this->db_smartone->escape_like_str($prm['noreg']);
$where_noreg = "AND M_PatientNoReg LIKE '%{$noreg}%'";
}
if ($prm['noreg'] != '')
$q['noreg'] = "AND M_PatientNoReg like '%{$prm['noreg']}%'";
if ($prm['search'] != '')
{
if (!empty($prm['search'])) {
$e = explode('+', $prm['search']);
if (isset($e[0])){
$e[0] = str_replace("'", "\\'", $e[0]);
$q['name'] = "AND M_PatientName LIKE '%{$e[0]}%'";
}
if (isset($e[1]))
$q['hp'] = "AND ((M_PatientHP LIKE '%{$e[1]}%' and M_PatientHP IS NOT NULL) OR (M_PatientHP IS NULL AND '{$e[1]}' = ''))";
if (isset($e[2]))
$q['dob'] = "AND ((DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$e[2]}%' and M_PatientDOB IS NOT NULL) OR (M_PatientDOB IS NULL AND '{$e[2]}' = ''))";
if (isset($e[3]))
$q['address'] = "AND M_PatientAddressDescription LIKE '%{$e[3]}%'";
// nama — trigram blind index (min 3 karakter)
if (!empty($e[0]) && mb_strlen(trim($e[0])) >= 3) {
$toks = $this->ibl_encryptor->query_tokens($e[0]);
$conds = [];
foreach ($toks as $tok) {
$tok_esc = $this->db_smartone->escape_str($tok);
$conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')";
}
if ($conds) $where_name = 'AND (' . implode(' AND ', $conds) . ')';
}
// hp — trigram blind index
if (!empty($e[1]) && mb_strlen(trim($e[1])) >= 3) {
$toks = $this->ibl_encryptor->query_tokens($e[1]);
$conds = [];
foreach ($toks as $tok) {
$tok_esc = $this->db_smartone->escape_str($tok);
$conds[] = "JSON_CONTAINS(M_PatientHP_bidx, '\"$tok_esc\"')";
}
if ($conds) $where_hp = 'AND (' . implode(' AND ', $conds) . ')';
}
// dob — trigram blind index (format dd-mm-yyyy)
if (!empty($e[2]) && mb_strlen(trim($e[2])) >= 3) {
$toks = $this->ibl_encryptor->query_tokens($e[2]);
$conds = [];
foreach ($toks as $tok) {
$tok_esc = $this->db_smartone->escape_str($tok);
$conds[] = "JSON_CONTAINS(M_PatientDOB_bidx, '\"$tok_esc\"')";
}
if ($conds) $where_dob = 'AND (' . implode(' AND ', $conds) . ')';
}
// nik — trigram blind index
if (!empty($e[3]) && mb_strlen(trim($e[3])) >= 3) {
$toks = $this->ibl_encryptor->query_tokens($e[3]);
$conds = [];
foreach ($toks as $tok) {
$tok_esc = $this->db_smartone->escape_str($tok);
$conds[] = "JSON_CONTAINS(M_PatientNIK_bidx, '\"$tok_esc\"')";
}
if ($conds) $where_nik = 'AND (' . implode(' AND ', $conds) . ')';
}
}
if($q['address'] == ''){
$q['address'] = "AND M_PatientAddressNote = 'Utama'";
}
$sql = "SELECT 'N' divider,M_PatientID, M_PatientNoReg,M_PatientEmail,M_PatientPrefix,M_PatientSuffix,
concat(M_TitleName,' ',IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) M_PatientName,
M_PatientName M_PatientRealName, M_TitleID, M_TitleName, M_SexID, M_SexName,
M_PatientHP, M_PatientPOB, M_PatientDOB, DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as dob_ina,
$sql = "SELECT 'N' divider, M_PatientID, M_PatientNoReg, M_PatientPrefix, M_PatientSuffix,
concat(M_TitleName,' ',IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) M_PatientNameRaw,
M_TitleID, M_TitleName, M_SexID, M_SexName,
M_PatientDOB,
'' M_PatientAddress,
M_PatientAddressID,
M_PatientAddressDescription, M_PatientM_IdTypeID, M_PatientIDNumber,
M_PatientAddressRegionalCd,
M_PatientAddressLocation,
M_PatientAddressCity,
M_PatientAddressVillage,
M_PatientAddressDistrict,
M_PatientAddressState,
M_PatientAddressCountry,
M_PatientAddressCountryCode,
IFNULL(M_PatientNote, '') M_PatientNote, M_PatientPhoto, IF(M_PatientPhone IS NULL OR M_PatientPhone = '', M_PatientHP, M_PatientPhone) hp,
-- fn_fo_patient_visit(M_PatientID) info,
M_PatientAddressM_KelurahanID M_KelurahanID, 0 M_DistrictID, 0 M_CityID, 0 M_ProvinceID, M_PatientM_ReligionID,
IFNULL(M_ReligionName, '-') M_ReligionName,
M_PatientAddressRegionalCd, M_PatientAddressLocation, M_PatientAddressCity,
M_PatientAddressVillage, M_PatientAddressDistrict, M_PatientAddressState,
M_PatientAddressCountry, M_PatientAddressCountryCode,
M_PatientAddressM_KelurahanID M_KelurahanID, 0 M_DistrictID, 0 M_CityID, 0 M_ProvinceID,
M_PatientM_ReligionID, IFNULL(M_ReligionName, '-') M_ReligionName,
IFNULL(Patient_SignatureUrl, '') image_signature,
M_PatientNote
FROM m_patient
join m_title on M_PatientM_TitleID = M_TitleID
join m_sex on M_PatientM_SexID = M_SexID
join m_patientaddress on M_PatientAddressM_PatientID = M_PatientID and M_PatientAddressIsActive = 'Y' {$q['address']}
left join m_religion on m_patientm_religionid = m_religionid
left join patient_signature on Patient_SignatureM_PatientID = M_PatientID and Patient_SignatureIsActive = 'Y'
where M_PatientIsActive = 'Y'
{$q['noreg']}
{$q['name']}
{$q['hp']}
{$q['dob']}
group by M_PatientID
limit $number_limit offset $number_offset";
//echo $sql;
IFNULL(M_PatientNote, '') M_PatientNote, M_PatientPhoto,
M_PatientM_IdTypeID,
M_PatientName_enc, M_PatientHP_enc, M_PatientDOB_enc,
M_PatientEmail_enc, M_PatientPhone_enc, M_PatientPOB_enc,
M_PatientIDNumber_enc, M_PatientNIK_enc, M_PatientAddressDescription_enc
FROM m_patient
JOIN m_title ON M_PatientM_TitleID = M_TitleID
JOIN m_sex ON M_PatientM_SexID = M_SexID
JOIN m_patientaddress ON M_PatientAddressM_PatientID = M_PatientID
AND M_PatientAddressIsActive = 'Y' AND M_PatientAddressNote = 'Utama'
LEFT JOIN m_religion ON M_PatientM_ReligionID = M_ReligionID
LEFT JOIN patient_signature ON Patient_SignatureM_PatientID = M_PatientID
AND Patient_SignatureIsActive = 'Y'
WHERE M_PatientIsActive = 'Y'
{$where_noreg}
{$where_name}
{$where_hp}
{$where_dob}
{$where_nik}
GROUP BY M_PatientID
LIMIT {$number_limit} OFFSET {$number_offset}";
$query = $this->db_smartone->query($sql);
if ($query) {
$rows = $query->result_array();
foreach ($rows as $k => $v)
{
$rows[$k]['M_PatientName'] = stripslashes($rows[$k]['M_PatientName']);
$rows[$k]['M_PatientAddress'] = stripslashes($rows[$k]['M_PatientAddressDescription']);
$info = $this->db_smartone->query("SELECT fn_fo_patient_visit(?) info", [$v['M_PatientID']])->row();
$rows[$k]['info'] = json_decode($info->info);
$references = [];
$sql = "SELECT M_ReferenceID, M_ReferenceName
FROM m_patient_reference
join m_reference on M_PatientReferenceM_ReferenceID = M_ReferenceID
WHERE M_PatientReferenceM_PatientID = ? AND M_PatientReferenceIsActive = 'Y'";
$query = $this->db_smartone->query($sql, [$v['M_PatientID']]);
if ($query) {
$references = $query->result_array();
}
$rows[$k]['references'] = $references;
}
$result = array("total" => $tot_page, "records" => $rows, "sql"=> $this->db_smartone->last_query());
$this->sys_ok($result);
}
else {
$this->sys_error_db("m_patient rows",$this->db_smartone);
exit;
if (!$query) {
$this->sys_error_db("m_patient rows", $this->db_smartone);
return;
}
$rows = $query->result_array();
$enc = $this->ibl_encryptor;
foreach ($rows as $k => $v) {
$name = $enc->decrypt($v['M_PatientName_enc']) ?? $v['M_PatientNameRaw'];
$hp = $enc->decrypt($v['M_PatientHP_enc']) ?? '';
$phone = $enc->decrypt($v['M_PatientPhone_enc']) ?? '';
$dob_dec = $enc->decrypt($v['M_PatientDOB_enc']) ?? date('d-m-Y', strtotime($v['M_PatientDOB']));
$addr = $enc->decrypt($v['M_PatientAddressDescription_enc']) ?? '';
$rows[$k]['M_PatientName'] = $name;
$rows[$k]['M_PatientAddress'] = $addr;
$rows[$k]['M_PatientAddressDescription'] = $addr;
$rows[$k]['M_PatientHP'] = $hp;
$rows[$k]['M_PatientEmail'] = $enc->decrypt($v['M_PatientEmail_enc']) ?? '';
$rows[$k]['M_PatientPhone'] = $phone;
$rows[$k]['M_PatientPOB'] = $enc->decrypt($v['M_PatientPOB_enc']) ?? '';
$rows[$k]['M_PatientIDNumber'] = $enc->decrypt($v['M_PatientIDNumber_enc']) ?? '';
$rows[$k]['M_PatientNIK'] = $enc->decrypt($v['M_PatientNIK_enc']) ?? '';
$rows[$k]['dob_ina'] = $dob_dec;
$rows[$k]['hp'] = $phone ?: $hp;
// bersihkan kolom _enc dari response
foreach (array_keys($rows[$k]) as $col) {
if (substr($col, -4) === '_enc') unset($rows[$k][$col]);
}
unset($rows[$k]['M_PatientNameRaw'], $rows[$k]['M_PatientDOB']);
$rows[$k]['info'] = $this->build_patient_visit_info($v['M_PatientID'], $dob_dec);
$ref_query = $this->db_smartone->query(
"SELECT M_ReferenceID, M_ReferenceName
FROM m_patient_reference
JOIN m_reference ON M_PatientReferenceM_ReferenceID = M_ReferenceID
WHERE M_PatientReferenceM_PatientID = ? AND M_PatientReferenceIsActive = 'Y'",
[$v['M_PatientID']]
);
$rows[$k]['references'] = $ref_query ? $ref_query->result_array() : [];
}
$this->sys_ok(["total" => 0, "records" => $rows]);
}
protected function build_patient_visit_info($patient_id, $patient_dob)
{
$visit = 1;
$birthday = 'N';
$visit_query = $this->db_smartone->query(
"SELECT COUNT(DISTINCT T_OrderHeaderID) AS n
FROM t_orderheader
JOIN t_orderdetail ON T_OrderHeaderID = T_OrderDetailT_OrderHeaderID AND T_OrderDetailIsActive = 'Y'
WHERE T_OrderHeaderIsActive = 'Y'
AND T_OrderHeaderM_PatientID = ?",
[$patient_id]
);
if ($visit_query) {
$visit_row = $visit_query->row_array();
$visit += (int) ($visit_row['n'] ?? 0);
}
$init_visit_query = $this->db_smartone->query(
"SELECT M_PatientInitialVisit
FROM m_patient
WHERE M_PatientID = ?",
[$patient_id]
);
if ($init_visit_query) {
$init_visit_row = $init_visit_query->row_array();
if (!empty($init_visit_row['M_PatientInitialVisit'])) {
$visit += (int) $init_visit_row['M_PatientInitialVisit'];
}
}
$dob_time = empty($patient_dob) ? false : strtotime($patient_dob);
if ($dob_time !== false) {
$birthday = date('m-d', $dob_time) === date('m-d') ? 'Y' : 'N';
}
return json_decode(json_encode([
'visit' => $visit,
'birthday' => $birthday,
]));
}
@@ -199,24 +307,36 @@ class Patient extends MY_Controller
$M_IdTypeID = $prm['M_PatientM_IdTypeID'];
}
$patient_name = str_replace("'", "\\'", $prm['M_PatientName']);
$dob_str = date('d-m-Y', strtotime($prm['M_PatientDOB']));
$ptn = [
'M_PatientName' => $patient_name,
'M_PatientM_TitleID' => $prm['M_PatientM_TitleID'],
'M_PatientPrefix' => $prm['M_PatientPrefix'],
'M_PatientSuffix' => $prm['M_PatientSuffix'],
'M_PatientM_SexID' => $prm['M_PatientM_SexID'],
'M_PatientM_ReligionID' => $prm['M_PatientM_ReligionID'],
'M_PatientDOB' => $prm['M_PatientDOB'],
'M_PatientPOB' => $prm['M_PatientPOB'],
'M_PatientHP' => $prm['M_PatientHP'],
'M_PatientPhone' => $prm['M_PatientPhone'],
'M_PatientEmail' => $prm['M_PatientEmail'],
'M_PatientM_IdTypeID' => $M_IdTypeID ,
'M_PatientIDNumber' => $prm['M_PatientIDNumber'],
'M_PatientNote' => $prm['M_PatientNote'],
'M_PatientUserID' => $userid,
'M_PatientCreated' => date('Y-m-d H:i:s'),
'M_PatientCreatedUserID' => $userid
'M_PatientName' => $this->_mask_name($patient_name),
'M_PatientName_enc' => $this->ibl_encryptor->encrypt($patient_name),
'M_PatientName_bidx' => $this->ibl_encryptor->search_bidx($patient_name),
'M_PatientM_TitleID' => $prm['M_PatientM_TitleID'],
'M_PatientPrefix' => $prm['M_PatientPrefix'],
'M_PatientSuffix' => $prm['M_PatientSuffix'],
'M_PatientM_SexID' => $prm['M_PatientM_SexID'],
'M_PatientM_ReligionID' => $prm['M_PatientM_ReligionID'],
'M_PatientDOB' => $prm['M_PatientDOB'],
'M_PatientDOB_enc' => $this->ibl_encryptor->encrypt($dob_str),
'M_PatientDOB_bidx' => $this->ibl_encryptor->search_bidx($dob_str),
'M_PatientPOB' => $this->_mask_short($prm['M_PatientPOB']),
'M_PatientPOB_enc' => $this->ibl_encryptor->encrypt($prm['M_PatientPOB']),
'M_PatientHP' => $this->_mask_phone($prm['M_PatientHP']),
'M_PatientHP_enc' => $this->ibl_encryptor->encrypt($prm['M_PatientHP']),
'M_PatientHP_bidx' => $this->ibl_encryptor->search_bidx($prm['M_PatientHP']),
'M_PatientPhone' => $this->_mask_phone($prm['M_PatientPhone']),
'M_PatientPhone_enc' => $this->ibl_encryptor->encrypt($prm['M_PatientPhone']),
'M_PatientEmail' => $this->_mask_email($prm['M_PatientEmail']),
'M_PatientEmail_enc' => $this->ibl_encryptor->encrypt($prm['M_PatientEmail']),
'M_PatientM_IdTypeID' => $M_IdTypeID,
'M_PatientIDNumber' => $this->_mask_id($prm['M_PatientIDNumber']),
'M_PatientIDNumber_enc' => $this->ibl_encryptor->encrypt($prm['M_PatientIDNumber']),
'M_PatientNIK_bidx' => $this->ibl_encryptor->search_bidx($prm['M_PatientNIK'] ?? ''),
'M_PatientNote' => $prm['M_PatientNote'],
'M_PatientUserID' => $userid,
'M_PatientCreated' => date('Y-m-d H:i:s'),
'M_PatientCreatedUserID' => $userid
];
$this->db_smartone->insert('m_patient', $ptn);
@@ -235,21 +355,21 @@ class Patient extends MY_Controller
$address_description = str_replace("'", "\\'", $prm['M_PatientAddressDescription']);
// save address
$add = [
'M_PatientAddressM_PatientID' => $id,
'M_PatientAddressDescription' => $address_description,
'M_PatientAddressUserID'=> $userid,
'M_PatientAddressRegionalCd' => $prm['M_PatientAddressRegionalCd'],
'M_PatientAddressLocation' => $prm['M_PatientAddressLocation'],
'M_PatientAddressCity' => $prm['M_PatientAddressCity'],
'M_PatientAddressVillage' => $prm['M_PatientAddressVillage'],
'M_PatientAddressDistrict' => $prm['M_PatientAddressDistrict'],
'M_PatientAddressState' => $prm['M_PatientAddressState'],
'M_PatientAddressCountry' => $prm['M_PatientAddressCountry'],
'M_PatientAddressCountryCode' => $prm['M_PatientAddressCountryCode'],
'M_PatientAddressNote' => isset($prm['M_PatientAddressNote']) ? $prm['M_PatientAddressNote'] : 'Utama',
'M_PatientAddressCreated' => date('Y-m-d H:i:s'),
'M_PatientAddressCreatedUserID' => $userid
'M_PatientAddressM_PatientID' => $id,
'M_PatientAddressDescription' => $this->_mask_address($address_description),
'M_PatientAddressDescription_enc' => $this->ibl_encryptor->encrypt($address_description),
'M_PatientAddressUserID' => $userid,
'M_PatientAddressRegionalCd' => $prm['M_PatientAddressRegionalCd'],
'M_PatientAddressLocation' => $prm['M_PatientAddressLocation'],
'M_PatientAddressCity' => $prm['M_PatientAddressCity'],
'M_PatientAddressVillage' => $prm['M_PatientAddressVillage'],
'M_PatientAddressDistrict' => $prm['M_PatientAddressDistrict'],
'M_PatientAddressState' => $prm['M_PatientAddressState'],
'M_PatientAddressCountry' => $prm['M_PatientAddressCountry'],
'M_PatientAddressCountryCode' => $prm['M_PatientAddressCountryCode'],
'M_PatientAddressNote' => isset($prm['M_PatientAddressNote']) ? $prm['M_PatientAddressNote'] : 'Utama',
'M_PatientAddressCreated' => date('Y-m-d H:i:s'),
'M_PatientAddressCreatedUserID' => $userid
];
$this->db_smartone->insert('m_patientaddress', $add);
$err = $this->db_smartone->error();
@@ -290,23 +410,35 @@ class Patient extends MY_Controller
$prm = $this->sys_input;
$userid = $this->sys_user["M_UserID"];
$prm['M_PatientDOB'] = date('Y-m-d', strtotime($prm['M_PatientDOB']));
$patient_name = str_replace("'", "\\'", $prm['M_PatientName']);
$this->db_smartone->set('M_PatientName', $patient_name)
$patient_name = str_replace("'", "\\'", $prm['M_PatientName']);
$dob_str = date('d-m-Y', strtotime($prm['M_PatientDOB']));
$this->db_smartone->set('M_PatientName', $this->_mask_name($patient_name))
->set('M_PatientName_enc', $this->ibl_encryptor->encrypt($patient_name))
->set('M_PatientName_bidx', $this->ibl_encryptor->search_bidx($patient_name))
->set('M_PatientM_TitleID', $prm['M_PatientM_TitleID'])
->set('M_PatientPrefix', $prm['M_PatientPrefix'])
->set('M_PatientSuffix', $prm['M_PatientSuffix'])
->set('M_PatientSuffix', $prm['M_PatientSuffix'])
->set('M_PatientM_SexID', $prm['M_PatientM_SexID'])
->set('M_PatientM_ReligionID', $prm['M_PatientM_ReligionID'])
->set('M_PatientDOB', $prm['M_PatientDOB'])
->set('M_PatientPOB', $prm['M_PatientPOB'])
->set('M_PatientHP', $prm['M_PatientHP'])
->set('M_PatientPhone', $prm['M_PatientPhone'])
->set('M_PatientEmail', $prm['M_PatientEmail'])
->set('M_PatientDOB_enc', $this->ibl_encryptor->encrypt($dob_str))
->set('M_PatientDOB_bidx', $this->ibl_encryptor->search_bidx($dob_str))
->set('M_PatientPOB', $this->_mask_short($prm['M_PatientPOB']))
->set('M_PatientPOB_enc', $this->ibl_encryptor->encrypt($prm['M_PatientPOB']))
->set('M_PatientHP', $this->_mask_phone($prm['M_PatientHP']))
->set('M_PatientHP_enc', $this->ibl_encryptor->encrypt($prm['M_PatientHP']))
->set('M_PatientHP_bidx', $this->ibl_encryptor->search_bidx($prm['M_PatientHP']))
->set('M_PatientPhone', $this->_mask_phone($prm['M_PatientPhone']))
->set('M_PatientPhone_enc', $this->ibl_encryptor->encrypt($prm['M_PatientPhone']))
->set('M_PatientEmail', $this->_mask_email($prm['M_PatientEmail']))
->set('M_PatientEmail_enc', $this->ibl_encryptor->encrypt($prm['M_PatientEmail']))
->set('M_PatientM_IdTypeID', $prm['M_PatientM_IdTypeID'])
->set('M_PatientIDNumber', $prm['M_PatientIDNumber'])
->set('M_PatientIDNumber', $this->_mask_id($prm['M_PatientIDNumber']))
->set('M_PatientIDNumber_enc', $this->ibl_encryptor->encrypt($prm['M_PatientIDNumber']))
->set('M_PatientNIK_bidx', $this->ibl_encryptor->search_bidx($prm['M_PatientIDNumber'] ?? ''))
->set('M_PatientNote', $prm['M_PatientNote'])
->set('M_PatientUserID', $userid)
->set('M_PatientUserID', $userid)
->set('M_PatientLastUpdatedUserID', $userid)
->where('M_PatientID', $prm['id'])
->update('m_patient');
@@ -325,7 +457,7 @@ class Patient extends MY_Controller
$ptn = json_encode($prm);
$id_address = isset($prm['M_PatientAddressID']) && $prm['M_PatientAddressID'] > 0 ? $prm['M_PatientAddressID']:0;
$address_description = str_replace("'", "\\'", $prm['M_PatientAddressDescription']);
$address_description = str_replace("'", "\\'", $prm['M_PatientAddressDescription']);
$this->db_smartone->set('M_PatientAddressRegionalCd', $prm['M_PatientAddressRegionalCd'])
->set('M_PatientAddressLocation', $prm['M_PatientAddressLocation'])
->set('M_PatientAddressCity', $prm['M_PatientAddressCity'])
@@ -334,8 +466,9 @@ class Patient extends MY_Controller
->set('M_PatientAddressState', $prm['M_PatientAddressState'])
->set('M_PatientAddressCountry', $prm['M_PatientAddressCountry'])
->set('M_PatientAddressCountryCode', $prm['M_PatientAddressCountryCode'])
->set('M_PatientAddressDescription', $address_description )
->set('M_PatientAddressUserID', $userid )
->set('M_PatientAddressDescription', $this->_mask_address($address_description))
->set('M_PatientAddressDescription_enc', $this->ibl_encryptor->encrypt($address_description))
->set('M_PatientAddressUserID', $userid)
->set('M_PatientAddressLastUpdatedUserID', $userid)
->where('M_PatientAddressID', $id_address)
->update('m_patientaddress');

View File

@@ -12,6 +12,279 @@ class Payment extends MY_Controller
{
parent::__construct();
$this->db_smartone = $this->load->database("onedev", true);
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
private function get_payment_type_code($payment_type_id)
{
$query = $this->db_onedev->query(
"SELECT M_PaymentTypeCode
FROM m_paymenttype
WHERE M_PaymentTypeID = ?
LIMIT 1",
[intval($payment_type_id)]
);
if (!$query) {
return '';
}
return strtoupper($query->row()->M_PaymentTypeCode ?? '');
}
private function normalize_payment($payment)
{
if (isset($payment['leftvalue']) || isset($payment['rightvalue']) || isset($payment['code'])) {
$payment['id'] = strval($payment['id'] ?? $payment['type'] ?? 0);
$payment['code'] = strtoupper($payment['code'] ?? $this->get_payment_type_code($payment['id']));
$payment['leftvalue'] = floatval($payment['leftvalue'] ?? 0);
$payment['rightvalue'] = floatval($payment['rightvalue'] ?? 0);
$payment['chex'] = isset($payment['chex']) ? (bool) $payment['chex'] : ($this->payment_amount_net($payment) > 0);
return $payment;
}
$normalized = $payment;
$normalized['id'] = strval($payment['type'] ?? $payment['id'] ?? 0);
$normalized['code'] = $this->get_payment_type_code($normalized['id']);
$normalized['leftvalue'] = floatval($payment['actual'] ?? $payment['amount'] ?? 0);
$normalized['rightvalue'] = floatval($payment['changes'] ?? 0);
$normalized['chex'] = $normalized['leftvalue'] > 0;
$normalized['selected_card'] = ['id' => intval($payment['card'] ?? 0), 'name' => ''];
$normalized['selected_edc'] = ['id' => intval($payment['edc'] ?? 0), 'name' => ''];
$normalized['selected_account'] = ['id' => intval($payment['account'] ?? 0), 'name' => ''];
return $normalized;
}
private function denormalize_payment($original_payment, $normalized_payment)
{
if (isset($original_payment['leftvalue']) || isset($original_payment['rightvalue']) || isset($original_payment['code'])) {
$original_payment['id'] = $normalized_payment['id'];
$original_payment['code'] = $normalized_payment['code'];
$original_payment['leftvalue'] = $normalized_payment['leftvalue'];
$original_payment['rightvalue'] = $normalized_payment['rightvalue'];
$original_payment['chex'] = !empty($normalized_payment['chex']);
return $original_payment;
}
$original_payment['type'] = strval($normalized_payment['id']);
$original_payment['amount'] = floatval($this->payment_amount_net($normalized_payment));
$original_payment['actual'] = floatval($normalized_payment['leftvalue']);
$original_payment['changes'] = floatval($normalized_payment['rightvalue']);
$original_payment['card'] = strval($this->get_selected_lookup_id($normalized_payment['selected_card'] ?? null));
$original_payment['edc'] = strval($this->get_selected_lookup_id($normalized_payment['selected_edc'] ?? null));
$original_payment['account'] = strval($this->get_selected_lookup_id($normalized_payment['selected_account'] ?? null));
return $original_payment;
}
private function payment_amount_net($payment)
{
$left = floatval($payment['leftvalue'] ?? 0);
$right = floatval($payment['rightvalue'] ?? 0);
$code = strtoupper($payment['code'] ?? '');
if ($code === 'CASH') {
return max(0, $left - $right);
}
return max(0, $left);
}
private function build_zero_payment($payment)
{
$payment['chex'] = false;
$payment['leftvalue'] = 0;
$payment['rightvalue'] = 0;
return $payment;
}
private function get_selected_lookup_id($value)
{
if (is_array($value)) {
return intval($value['id'] ?? 0);
}
return 0;
}
private function split_payments_for_klinik($payments, $clinic_amount)
{
$remaining_clinic = max(0, floatval($clinic_amount));
$clinic_payments = [];
$ibl_payments = [];
foreach ($payments as $payment) {
$normalized_payment = $this->normalize_payment($payment);
$code = strtoupper($normalized_payment['code'] ?? '');
$net_amount = $this->payment_amount_net($normalized_payment);
$allocated = min($remaining_clinic, $net_amount);
$clinic_payment = $this->build_zero_payment($normalized_payment);
$ibl_payment = $normalized_payment;
if ($allocated > 0) {
$clinic_payment['chex'] = true;
if ($code === 'CASH') {
$clinic_payment['leftvalue'] = $allocated;
$clinic_payment['rightvalue'] = 0;
$ibl_payment['leftvalue'] = max(0, floatval($normalized_payment['leftvalue']) - $allocated);
$ibl_payment['chex'] = ($this->payment_amount_net($ibl_payment) > 0);
} else {
$clinic_payment['leftvalue'] = $allocated;
$ibl_payment['leftvalue'] = max(0, floatval($normalized_payment['leftvalue']) - $allocated);
$ibl_payment['chex'] = (floatval($ibl_payment['leftvalue']) > 0);
}
$remaining_clinic -= $allocated;
} else {
$ibl_payment['chex'] = ($net_amount > 0) ? !empty($normalized_payment['chex']) : false;
}
$clinic_payments[] = $clinic_payment;
$ibl_payments[] = $this->denormalize_payment($payment, $ibl_payment);
}
return [
'clinic_payments' => $clinic_payments,
'ibl_payments' => $ibl_payments,
'allocated_total' => max(0, floatval($clinic_amount) - $remaining_clinic)
];
}
private function get_order_klinik_outstanding($order_klinik_id)
{
$sql = "SELECT
o.orderID,
o.orderTotal,
IFNULL(SUM(CASE WHEN p.PaymentIsActive = 'Y' THEN p.PaymentTotal ELSE 0 END), 0) AS paid_total
FROM one_klinik.`order` o
LEFT JOIN one_klinik.`payment` p ON p.PaymentOrderID = o.orderID
WHERE o.orderID = ?
GROUP BY o.orderID, o.orderTotal
LIMIT 1";
$query = $this->db_onedev->query($sql, [$order_klinik_id]);
if (!$query) {
return [false, "Gagal mengambil data order klinik"];
}
$row = $query->row_array();
if (!$row) {
return [false, "Order klinik tidak ditemukan"];
}
$outstanding = max(0, floatval($row['orderTotal']) - floatval($row['paid_total']));
$row['outstanding_total'] = $outstanding;
return [true, $row];
}
private function save_payment_klinik($orderid, $payments, $xuserid)
{
$sql = "INSERT INTO one_klinik.`payment`(PaymentOrderID,PaymentDate,PaymentCreated,PaymentM_UserID) VALUES (?,CURDATE(),NOW(),?)";
$query = $this->db_onedev->query($sql, [$orderid, $xuserid]);
if (!$query) {
return [false, "payment klinik insert"];
}
$headerid = $this->db_onedev->insert_id();
foreach ($payments as $v) {
if (empty($v['chex'])) {
continue;
}
$actual = 0;
$change = 0;
$amount = floatval($v['leftvalue'] ?? 0);
if (($v['code'] ?? '') == 'CASH') {
$actual = floatval($v['leftvalue'] ?? 0);
$change = floatval($v['rightvalue'] ?? 0);
$amount = ($actual > 0) ? ($actual - $change) : $actual;
$sql = "INSERT INTO one_klinik.`paymentdetail`(
PaymentDetailPaymentID,
PaymentDetailM_PaymentTypeID,
PaymentDetailAmount,
PaymentDetailActual,
PaymentDetailChange,
PaymentDetailCreated,
PaymentDetailLastUpdated,
PaymentDetailUserID
) VALUES (?, ?, ?, ?, ?, now(), now(), ?)";
$query = $this->db_onedev->query($sql, [
$headerid,
$v['id'],
$amount,
$actual,
$change,
$xuserid
]);
if (!$query) {
return [false, "payment klinik detail cash insert"];
}
} else {
$selected_card = 0;
$selected_edc = 0;
if (($v['code'] ?? '') == 'DEBIT' || ($v['code'] ?? '') == 'CREDIT' || ($v['code'] ?? '') == 'TRANSFER') {
$selected_card = $this->get_selected_lookup_id($v['selected_card'] ?? null);
$selected_edc = $this->get_selected_lookup_id($v['selected_edc'] ?? null);
if (($v['code'] ?? '') == 'TRANSFER') {
$selected_edc = $this->get_selected_lookup_id($v['selected_account'] ?? null);
}
}
$sql = "INSERT INTO one_klinik.`paymentdetail`(
PaymentDetailPaymentID,
PaymentDetailM_PaymentTypeID,
PaymentDetailAmount,
PaymentDetailActual,
PaymentDetailChange,
PaymentDetailCardNat_BankID,
PaymentDetailEDCNat_BankID,
PaymentDetailM_BankAccountID,
PaymentDetailCreated,
PaymentDetailLastUpdated,
PaymentDetailUserID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, now(), now(), ?)";
$query = $this->db_onedev->query($sql, [
$headerid,
$v['id'],
$amount,
0,
0,
$selected_card,
0,
$selected_edc,
$xuserid
]);
if (!$query) {
return [false, "payment klinik detail non cash insert"];
}
}
}
$sql = "SELECT SUM(PaymentDetailAmount) as total
FROM one_klinik.`paymentdetail`
WHERE PaymentDetailPaymentID = ? AND PaymentDetailIsActive = 'Y'";
$total_paid = floatval($this->db_onedev->query($sql, [$headerid])->row()->total ?? 0);
$sql = "UPDATE one_klinik.`payment` SET PaymentTotal = ? WHERE PaymentID = ?";
$this->db_onedev->query($sql, [$total_paid, $headerid]);
$sql = "SELECT SUM(PaymentTotal) as paid, orderTotal as total
FROM one_klinik.`payment`
JOIN one_klinik.`order` ON orderID = PaymentOrderID
WHERE PaymentOrderID = ? AND PaymentIsActive = 'Y'";
$xtotal_all_paid = $this->db_onedev->query($sql, [$orderid])->row_array();
if ($xtotal_all_paid && floatval($xtotal_all_paid['paid']) >= floatval($xtotal_all_paid['total'])) {
$sql = "UPDATE one_klinik.`order` SET orderIsLunas = 'Y' WHERE orderID = ?";
$this->db_onedev->query($sql, [$orderid]);
}
$xdata = $this->db_onedev->query(
"SELECT PaymentID as idx, PaymentNumber as numberx FROM one_klinik.payment WHERE PaymentID = ?",
[$headerid]
)->row();
return [true, ['payment_id' => $headerid, 'payment_total' => $total_paid, 'data' => $xdata]];
}
public function get_order() {
@@ -26,7 +299,7 @@ class Payment extends MY_Controller
T_OrderHeaderSubTotal as order_subtotal,
T_OrderHeaderRounding as order_rounding,
T_OrderHeaderTotal as order_total,
concat(if(M_TitleID is null, '', concat(M_TitleName, ' ')), M_PatientName) as patient_name,
M_PatientName_enc, M_TitleName,
M_PatientNoReg as patient_mr,
M_MouName as order_mou,
M_CompanyName as order_company,
@@ -47,6 +320,10 @@ class Payment extends MY_Controller
$query = $this->db_smartone->query($sql, array($prm['id']));
if ($query) {
$rows = (array) $query->row();
$name = $this->ibl_encryptor->decrypt($rows['M_PatientName_enc']);
$title = !empty($rows['M_TitleName']) ? $rows['M_TitleName'] . ' ' : '';
$rows['patient_name'] = $title . ($name ?? '');
unset($rows['M_PatientName_enc'], $rows['M_TitleName']);
$rst['order_header'] = $rows;
// $result = array("status" => "OK" , "data" => $rst);
// $this->sys_ok($result);
@@ -175,8 +452,55 @@ class Payment extends MY_Controller
function save()
{
$prm = $this->sys_input;
$payment_json = json_encode($prm['payments']);
$payments_ibl = $prm['payments'];
$klinik_payment_result = null;
if (!empty($prm['order_klinik_id'])) {
list($ok_order_klinik, $order_klinik_data) = $this->get_order_klinik_outstanding($prm['order_klinik_id']);
if (!$ok_order_klinik) {
$this->sys_error($order_klinik_data);
exit;
}
$split = $this->split_payments_for_klinik($prm['payments'], $order_klinik_data['outstanding_total']);
if ($split['allocated_total'] > 0) {
list($ok_payment_klinik, $payment_klinik_data) = $this->save_payment_klinik(
$prm['order_klinik_id'],
$split['clinic_payments'],
$this->sys_user['M_UserID']
);
if (!$ok_payment_klinik) {
$this->sys_error_db($payment_klinik_data, $this->db_onedev);
exit;
}
$klinik_payment_result = $payment_klinik_data;
}
$payments_ibl = $split['ibl_payments'];
}
$has_ibl_payment = false;
foreach ($payments_ibl as $payment) {
$normalized_payment = $this->normalize_payment($payment);
if (!empty($normalized_payment['chex']) && $this->payment_amount_net($normalized_payment) > 0) {
$has_ibl_payment = true;
break;
}
}
if (!$has_ibl_payment) {
$result = [
'status' => 'OK',
'data' => [
'status' => 'OK',
'order_klinik_payment' => $klinik_payment_result,
'ibl_payment' => null
]
];
$this->sys_ok($result['data']);
exit;
}
$payment_json = json_encode($payments_ibl);
$sql = "CALL sp_fo_payment('{$prm['order_id']}', '{$payment_json}', '{$this->sys_user['M_UserID']}');";
$query = $this->db_smartone->query($sql);
@@ -184,6 +508,9 @@ class Payment extends MY_Controller
{
$rst = $query->row();
$rst->data = json_decode($rst->data);
if (is_object($rst->data)) {
$rst->data->order_klinik_payment = $klinik_payment_result;
}
echo json_encode($rst);
}
else

View File

@@ -11,6 +11,54 @@ class Patientv4 extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
private function _mask_name($v) {
if (!$v) return $v;
$v = trim($v);
$words = preg_split('/\s+/', $v);
if (count($words) === 1) {
$l = mb_strlen($v, 'UTF-8');
if ($l <= 2) return $v;
return mb_substr($v, 0, 2, 'UTF-8') . str_repeat('*', $l - 2);
}
$first = $words[0];
$rest = array_slice($words, 1);
$masked = array_map(function($w) {
if (!$w) return '';
$init = mb_substr($w, 0, 1, 'UTF-8');
return $init . str_repeat('*', max(3, mb_strlen($w, 'UTF-8') - 1));
}, $rest);
return $first . ' ' . implode(' ', $masked);
}
private function _mask_phone($v) { if (!$v) return $v; $d=preg_replace('/[^0-9]/','',trim($v)); $l=strlen($d); if($l<=4) return '****'; if($l<=8) return substr($d,0,4).str_repeat('*',$l-4); return substr($d,0,4).str_repeat('*',$l-7).substr($d,-3); }
private function _mask_email($v) { if (!$v||strpos($v,'@')===false) return $v; [$loc,$dom]=explode('@',$v,2); return mb_substr($loc,0,min(2,mb_strlen($loc,'UTF-8')),'UTF-8').'***@'.$dom; }
private function _mask_short($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=2) return '***'; return mb_substr($v,0,2,'UTF-8').'***'; }
private function _mask_id($v) { if (!$v) return $v; $v=trim($v); $l=strlen($v); if($l<=4) return '****'; return substr($v,0,4).str_repeat('*',max(3,$l-6)).($l>6?substr($v,-2):''); }
private function _mask_address($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=5) return '***'; return mb_substr($v,0,5,'UTF-8').'***'; }
private function _decrypt_row(array $row): array {
$enc = $this->ibl_encryptor;
if (!empty($row['M_PatientName_enc'])) $row['M_PatientName'] = $enc->decrypt($row['M_PatientName_enc']) ?? $row['M_PatientName'];
if (!empty($row['M_PatientHP_enc'])) $row['M_PatientHP'] = $enc->decrypt($row['M_PatientHP_enc']) ?? '';
if (!empty($row['M_PatientEmail_enc'])) $row['M_PatientEmail'] = $enc->decrypt($row['M_PatientEmail_enc']) ?? '';
if (!empty($row['M_PatientPhone_enc'])) $row['M_PatientPhone'] = $enc->decrypt($row['M_PatientPhone_enc']) ?? '';
if (!empty($row['M_PatientPOB_enc'])) $row['M_PatientPOB'] = $enc->decrypt($row['M_PatientPOB_enc']) ?? '';
if (!empty($row['M_PatientIDNumber_enc'])) $row['M_PatientIDNumber'] = $enc->decrypt($row['M_PatientIDNumber_enc']) ?? '';
if (!empty($row['M_PatientNIK_enc'])) $row['M_PatientNIK'] = $enc->decrypt($row['M_PatientNIK_enc']) ?? '';
if (!empty($row['M_PatientDOB_enc'])) $row['M_PatientDOB'] = $enc->decrypt($row['M_PatientDOB_enc']) ?? $row['M_PatientDOB'];
foreach (array_keys($row) as $k) { if (substr($k,-4)==='_enc'||substr($k,-5)==='_bidx') unset($row[$k]); }
return $row;
}
private function _decrypt_addr_row(array $row): array {
$enc = $this->ibl_encryptor;
if (!empty($row['M_PatientAddressDescription_enc'])) $row['M_PatientAddressDescription'] = $enc->decrypt($row['M_PatientAddressDescription_enc']) ?? $row['M_PatientAddressDescription'];
if (!empty($row['M_PatientAddressEmail_enc'])) $row['M_PatientAddressEmail'] = $enc->decrypt($row['M_PatientAddressEmail_enc']) ?? '';
if (!empty($row['M_PatientAddressPhone_enc'])) $row['M_PatientAddressPhone'] = $enc->decrypt($row['M_PatientAddressPhone_enc']) ?? '';
foreach (array_keys($row) as $k) { if (substr($k,-4)==='_enc'||substr($k,-5)==='_bidx') unset($row[$k]); }
return $row;
}
public function search()
@@ -25,89 +73,53 @@ class Patientv4 extends MY_Controller
// echo $norm;
$sql_where = "WHERE M_PatientIsActive = 'Y' ";
$sql_param = array();
$sql_where = "WHERE M_PatientIsActive = 'Y'";
$sql_param = array();
$number_limit = 100;
$number_offset = max(0, ($prm['current_page'] - 1)) * $number_limit;
// Search nama via trigram blind index (kolom plaintext sudah dimasking)
if ($nama != "") {
if ($sql_where != "") {
$sql_where .=" and ";
}
$sql_where .= " M_PatientName like ? ";
$sql_param[] = "%$nama%";
}
if ($norm != "") {
if ($sql_where != "") {
$sql_where .=" and ";
}
$sql_where .= " M_PatientNoReg like ? ";
$sql_param[] = "%$norm%";
}
$limit = '';
if($all == 'N'){
$limit = ' LIMIT 100';
}
$number_limit = 100;
$number_offset = ($prm['current_page'] - 1) * $number_limit ;
//echo $this->db_onedev->last_query();
$tot_count = 0;
$tot_page = 0;
$sql = "SELECT
M_PatientID,
M_PatientNoReg,
M_PatientPrefix,
M_PatientName,
M_PatientSuffix,
M_PatientHP,
M_PatientEmail,
M_PatientPOB,
M_PatientPhone,
M_PatientIDNumber,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as M_PatientDOB,
M_PatientNote,
M_PatientNIK,
M_PatientJabatan,
M_PatientKedudukan,
M_PatientPJ,
M_PatientLocation,
M_PatientJob,
M_PatientM_SexID,
M_SexName,
M_PatientM_TitleID,
M_TitleName,
M_PatientM_ReligionID,
M_ReligionName,
M_PatientM_IdTypeID,
M_IdTypeName,
M_PatientIDNumber,
IF(ISNULL(M_PatientSuspendID),'active','suspend' ) as status
FROM m_patient
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_religion ON M_PatientM_ReligionID = M_ReligionID
LEFT JOIN m_idtype ON M_PatientM_IdTypeID = M_IdTypeID
LEFT JOIN m_patientsuspend ON M_PatientSuspendM_PatientID = M_PatientID AND M_PatientSuspendIsActive = 'Y'
$sql_where
ORDER BY M_PatientName DESC
limit 100
";
// $sql;
$query = $this->db_onedev->query($sql, $sql_param);
$rows = $query->result_array();
if($rows){
foreach($rows as $k => $v){
$rows[$k]['M_PatientName'] = stripslashes($rows[$k]['M_PatientName']);
$rows[$k]['M_PatientPOB'] = stripslashes($rows[$k]['M_PatientPOB']);
//$rows[$k]['verification_px'] = $this->add_verification_test($v['M_PatientID']);
$toks = $this->ibl_encryptor->query_tokens($nama);
foreach ($toks as $tok) {
$tok_esc = $this->db_onedev->escape_str($tok);
$sql_where .= " AND JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')";
}
}
if ($norm != "") {
$sql_where .= " AND M_PatientNoReg LIKE ?";
$sql_param[] = "%$norm%";
}
$sql = "SELECT
M_PatientID, M_PatientNoReg, M_PatientPrefix, M_PatientSuffix,
M_PatientNote, M_PatientJabatan, M_PatientKedudukan,
M_PatientPJ, M_PatientLocation, M_PatientJob,
M_PatientM_SexID, M_SexName,
M_PatientM_TitleID, M_TitleName,
M_PatientM_ReligionID, M_ReligionName,
M_PatientM_IdTypeID, M_IdTypeName,
IF(ISNULL(M_PatientSuspendID),'active','suspend') as status,
M_PatientName_enc, M_PatientHP_enc, M_PatientEmail_enc,
M_PatientPhone_enc, M_PatientPOB_enc, M_PatientIDNumber_enc,
M_PatientNIK_enc, M_PatientDOB_enc
FROM m_patient
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_religion ON M_PatientM_ReligionID = M_ReligionID
LEFT JOIN m_idtype ON M_PatientM_IdTypeID = M_IdTypeID
LEFT JOIN m_patientsuspend ON M_PatientSuspendM_PatientID = M_PatientID AND M_PatientSuspendIsActive = 'Y'
{$sql_where}
ORDER BY M_PatientID DESC
LIMIT {$number_limit} OFFSET {$number_offset}";
//$this->_add_address($rows);
$result = array("total" => 1, "records" => $rows, "sql"=> $this->db_onedev->last_query());
$query = $this->db_onedev->query($sql, $sql_param);
$rows = $query->result_array();
foreach ($rows as $k => $v) {
$rows[$k] = $this->_decrypt_row($v);
}
$result = array("total" => count($rows), "records" => $rows);
$this->sys_ok($result);
exit;
}
@@ -342,25 +354,38 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
$sql = "SELECT * FROM m_patient WHERE M_PatientID = {$prm['M_PatientID']}";
$rows_before = $this->db_onedev->query($sql)->row_array();
$pdob = date('Y-m-d',strtotime($prm['M_PatientDOB']));
$prm['M_PatientName'] = str_replace("'", "\\'", $prm['M_PatientName']);
$prm['M_PatientPOB'] = str_replace("'", "\\'", $prm['M_PatientPOB']);
$sql ="UPDATE m_patient SET
$pdob = date('Y-m-d', strtotime($prm['M_PatientDOB']));
$dob_str = date('d-m-Y', strtotime($prm['M_PatientDOB']));
$name = $prm['M_PatientName'];
$enc = $this->ibl_encryptor;
$sql = "UPDATE m_patient SET
M_PatientM_TitleID = ?,
M_PatientPrefix = ?,
M_PatientName = ?,
M_PatientName_enc = ?,
M_PatientName_bidx = ?,
M_PatientSuffix = ?,
M_PatientDOB = ?,
M_PatientDOB_enc = ?,
M_PatientDOB_bidx = ?,
M_PatientM_SexID = ?,
M_PatientM_ReligionID = ?,
M_PatientEmail = ?,
M_PatientEmail_enc = ?,
M_PatientPOB = ?,
M_PatientPOB_enc = ?,
M_PatientHP = ?,
M_PatientHP_enc = ?,
M_PatientHP_bidx = ?,
M_PatientPhone = ?,
M_PatientPhone_enc = ?,
M_PatientM_IdTypeID = ?,
M_PatientIDNumber = ?,
M_PatientIDNumber_enc = ?,
M_PatientNote = ?,
M_PatientNIK = ?,
M_PatientNIK_enc = ?,
M_PatientJabatan = ?,
M_PatientKedudukan = ?,
M_PatientPJ = ?,
@@ -369,33 +394,30 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
M_PatientUserID = ?,
M_PatientLastUpdatedUserID = ?,
M_PatientLastUpdated = NOW()
WHERE
M_PatientID = ?
";
//echo $query;
WHERE M_PatientID = ?";
$query = $this->db_onedev->query($sql, array(
$prm['M_PatientM_TitleID'],
$prm['M_PatientPrefix'],
$prm['M_PatientName'],
$this->_mask_name($name), $enc->encrypt($name), $enc->search_bidx($name),
$prm['M_PatientSuffix'],
$pdob,
$pdob, $enc->encrypt($dob_str), $enc->search_bidx($dob_str),
$prm['M_PatientM_SexID'],
$prm['M_PatientM_ReligionID'],
$prm['M_PatientEmail'],
$prm['M_PatientPOB'],
$prm['M_PatientHP'],
$prm['M_PatientPhone'],
$this->_mask_email($prm['M_PatientEmail']), $enc->encrypt($prm['M_PatientEmail']),
$this->_mask_short($prm['M_PatientPOB']), $enc->encrypt($prm['M_PatientPOB']),
$this->_mask_phone($prm['M_PatientHP']), $enc->encrypt($prm['M_PatientHP']), $enc->search_bidx($prm['M_PatientHP']),
$this->_mask_phone($prm['M_PatientPhone']), $enc->encrypt($prm['M_PatientPhone']),
$prm['M_PatientM_IdTypeID'],
$prm['M_PatientIDNumber'],
$this->_mask_id($prm['M_PatientIDNumber']), $enc->encrypt($prm['M_PatientIDNumber']),
$prm['M_PatientNote'],
$prm['M_PatientNIK'],
$this->_mask_id($prm['M_PatientNIK']), $enc->encrypt($prm['M_PatientNIK']),
$prm['M_PatientJabatan'],
$prm['M_PatientKedudukan'],
$prm['M_PatientPJ'],
$prm['M_PatientLocation'],
$prm['M_PatientJob'],
$userid,
$userid,
$userid, $userid,
$prm['M_PatientID']
));
@@ -423,63 +445,65 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
$prm = $this->sys_input;
$userid = $this->sys_user["M_UserID"];
$pdob = date('Y-m-d',strtotime($prm['M_PatientDOB']));
$prm['M_PatientName'] = str_replace("'", "\\'", $prm['M_PatientName']);
$query ="INSERT INTO m_patient (
M_PatientM_TitleID,
M_PatientPrefix,
M_PatientName,
$pdob = date('Y-m-d', strtotime($prm['M_PatientDOB']));
$dob_str = date('d-m-Y', strtotime($prm['M_PatientDOB']));
$name = $prm['M_PatientName'];
$enc = $this->ibl_encryptor;
$query = "INSERT INTO m_patient (
M_PatientM_TitleID, M_PatientPrefix,
M_PatientName, M_PatientName_enc, M_PatientName_bidx,
M_PatientSuffix,
M_PatientDOB,
M_PatientM_SexID,
M_PatientM_ReligionID,
M_PatientEmail,
M_PatientPOB,
M_PatientHP,
M_PatientPhone,
M_PatientDOB, M_PatientDOB_enc, M_PatientDOB_bidx,
M_PatientM_SexID, M_PatientM_ReligionID,
M_PatientEmail, M_PatientEmail_enc,
M_PatientPOB, M_PatientPOB_enc,
M_PatientHP, M_PatientHP_enc, M_PatientHP_bidx,
M_PatientPhone, M_PatientPhone_enc,
M_PatientM_IdTypeID,
M_PatientIDNumber,
M_PatientIDNumber, M_PatientIDNumber_enc,
M_PatientNote,
M_PatientNIK,
M_PatientJabatan,
M_PatientKedudukan,
M_PatientPJ,
M_PatientLocation,
M_PatientJob,
M_PatientUserID,
M_PatientCreatedUserID,
M_PatientCreated
)
VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,NOW()
)
";
//echo $query;
$rows = $this->db_onedev->query($query,array(
$prm['M_PatientM_TitleID'],
$prm['M_PatientPrefix'],
$prm['M_PatientName'],
M_PatientNIK, M_PatientNIK_enc,
M_PatientJabatan, M_PatientKedudukan, M_PatientPJ,
M_PatientLocation, M_PatientJob,
M_PatientUserID, M_PatientCreatedUserID, M_PatientCreated
) VALUES (
?, ?,
?, ?, ?,
?,
?, ?, ?,
?, ?,
?, ?,
?, ?,
?, ?, ?,
?, ?,
?,
?, ?,
?,
?, ?,
?, ?, ?,
?, ?,
?, ?, NOW()
)";
$rows = $this->db_onedev->query($query, array(
$prm['M_PatientM_TitleID'], $prm['M_PatientPrefix'],
$this->_mask_name($name), $enc->encrypt($name), $enc->search_bidx($name),
$prm['M_PatientSuffix'],
$pdob,
$prm['M_PatientM_SexID'],
$prm['M_PatientM_ReligionID'],
$prm['M_PatientEmail'],
$prm['M_PatientPOB'],
$prm['M_PatientHP'],
$prm['M_PatientPhone'],
$pdob, $enc->encrypt($dob_str), $enc->search_bidx($dob_str),
$prm['M_PatientM_SexID'], $prm['M_PatientM_ReligionID'],
$this->_mask_email($prm['M_PatientEmail']), $enc->encrypt($prm['M_PatientEmail']),
$this->_mask_short($prm['M_PatientPOB']), $enc->encrypt($prm['M_PatientPOB']),
$this->_mask_phone($prm['M_PatientHP']), $enc->encrypt($prm['M_PatientHP']), $enc->search_bidx($prm['M_PatientHP']),
$this->_mask_phone($prm['M_PatientPhone']), $enc->encrypt($prm['M_PatientPhone']),
$prm['M_PatientM_IdTypeID'],
$prm['M_PatientIDNumber'],
$this->_mask_id($prm['M_PatientIDNumber']), $enc->encrypt($prm['M_PatientIDNumber']),
$prm['M_PatientNote'],
$prm['M_PatientNIK'],
$prm['M_PatientJabatan'],
$prm['M_PatientKedudukan'],
$prm['M_PatientPJ'],
$prm['M_PatientLocation'],
$prm['M_PatientJob'],
$userid,
$userid
$this->_mask_id($prm['M_PatientNIK']), $enc->encrypt($prm['M_PatientNIK']),
$prm['M_PatientJabatan'], $prm['M_PatientKedudukan'], $prm['M_PatientPJ'],
$prm['M_PatientLocation'], $prm['M_PatientJob'],
$userid, $userid
));
$rows = $this->db_onedev->query($query);
$last_id = $this->db_onedev->insert_id();
$result = array(
"total" => 1 ,
@@ -533,19 +557,13 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
M_PatientAddressIsActive = 'Y' AND M_PatientAddressM_PatientID = ?
";
//echo $query;
$rows = $this->db_onedev->query($query,array($prm['id']))->result_array();
if($rows){
foreach($rows as $k => $v){
$rows[$k]['M_PatientAddressDescription'] = stripslashes($v['M_PatientAddressDescription']);
$rows[$k]['action'] = '<v-icon color="error" @click="deleteAddress(props.item)">delete</v-icon>';
$rows[$k]['action'] .= '<v-icon color="primary" @click="deleteAddress(props.item)">edit</v-icon>';
}
$rows = $this->db_onedev->query($query, array($prm['id']))->result_array();
foreach ($rows as $k => $v) {
$rows[$k] = $this->_decrypt_addr_row($v);
$rows[$k]['action'] = '<v-icon color="error" @click="deleteAddress(props.item)">delete</v-icon>';
$rows[$k]['action'] .= '<v-icon color="primary" @click="deleteAddress(props.item)">edit</v-icon>';
}
$result = array(
"total" => count($rows) ,
"records" => $rows,
);
$result = array("total" => count($rows), "records" => $rows);
$this->sys_ok($result);
exit;
}
@@ -570,46 +588,27 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
$prm['M_PatientAddressNote'] = 'Utama_'.$rx;
}
}
$prm['M_PatientAddressDescription'] = str_replace("'", "\\'", $prm['M_PatientAddressDescription']);
$sql ="INSERT INTO m_patientaddress (
M_PatientAddressM_PatientID,
M_PatientAddressNote,
M_PatientAddressDescription,
M_PatientAddressRegionalCd,
M_PatientAddressState,
M_PatientAddressCity,
M_PatientAddressDistrict,
M_PatientAddressVillage,
M_PatientAddressCreated,
M_PatientAddressUserID,
M_PatientAddressCreatedUserID
)
VALUES(
?,
?,
?,
?,
?,
?,
?,
?,
NOW(),
?,
?
)
";
//echo $query;
$query = $this->db_onedev->query($sql,array(
$addr_desc = $prm['M_PatientAddressDescription'];
$enc = $this->ibl_encryptor;
$sql = "INSERT INTO m_patientaddress (
M_PatientAddressM_PatientID, M_PatientAddressNote,
M_PatientAddressDescription, M_PatientAddressDescription_enc, M_PatientAddressDescription_bidx,
M_PatientAddressRegionalCd, M_PatientAddressState,
M_PatientAddressCity, M_PatientAddressDistrict, M_PatientAddressVillage,
M_PatientAddressCreated, M_PatientAddressUserID, M_PatientAddressCreatedUserID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, ?)";
$query = $this->db_onedev->query($sql, array(
$prm['M_PatientAddressM_PatientID'],
$prm['M_PatientAddressNote'],
$prm['M_PatientAddressDescription'],
$this->_mask_address($addr_desc), $enc->encrypt($addr_desc), $enc->search_bidx($addr_desc),
$prm['region']['id'],
$prm['region']['pro_nm'],
$prm['region']['kab_nm'],
$prm['region']['kec_nm'],
$prm['region']['kel_nm'],
$userid,
$userid
$userid, $userid
));
//echo $this->db_onedev->last_query();
if(!$query){
@@ -633,13 +632,15 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
}
$userid = $this->sys_user["M_UserID"];
$prm = $this->sys_input;
$prm['M_PatientAddressDescription'] = str_replace("'", "\\'", $prm['M_PatientAddressDescription']);
$sql = "SELECT * FROM m_patientaddress WHERE M_PatientAddressID = {$prm['M_PatientAddressID']}";
$rows_before = $this->db_onedev->query($sql)->row_array();
$query ="UPDATE m_patientaddress SET
$addr_desc = $prm['M_PatientAddressDescription'];
$enc = $this->ibl_encryptor;
$query = "UPDATE m_patientaddress SET
M_PatientAddressM_PatientID = ?,
M_PatientAddressNote = ?,
M_PatientAddressDescription = ?,
M_PatientAddressDescription_enc = ?,
M_PatientAddressDescription_bidx = ?,
M_PatientAddressRegionalCd = ?,
M_PatientAddressState = ?,
M_PatientAddressCity = ?,
@@ -648,21 +649,18 @@ $rows['titles'] = $this->db_onedev->query($query)->result_array();
M_PatientAddressUpdated = NOW(),
M_PatientAddressUpdatedUserID = ?,
M_PatientAddressUserID = ?
WHERE
M_PatientAddressID = ?
";
//echo $query;
$rows = $this->db_onedev->query($query,array(
WHERE M_PatientAddressID = ?";
$rows = $this->db_onedev->query($query, array(
$prm['M_PatientAddressM_PatientID'],
$prm['M_PatientAddressNote'],
$prm['M_PatientAddressDescription'],
$this->_mask_address($addr_desc), $enc->encrypt($addr_desc), $enc->search_bidx($addr_desc),
$prm['region']['id'],
$prm['region']['pro_nm'],
$prm['region']['kab_nm'],
$prm['region']['kec_nm'],
$prm['region']['kel_nm'],
$userid,
$userid,
$userid, $userid,
$prm['M_PatientAddressID']
));

View File

@@ -0,0 +1,73 @@
@baseUrl = https://devone.aplikasi.web.id/one-api-lab
@token = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJNX1VzZXJJRCI6IjMiLCJNX1VzZXJVc2VybmFtZSI6ImFkbWluICIsIk1fVXNlckdyb3VwRGFzaGJvYXJkIjoib25lLXVpLWxhYlwvdGVzdFwvdnVleFwvb25lLXBhdGllbnQtbGlzdC1iYXJjb2RlLXZ2LTYtY3BvbmVcLyIsIk1fVXNlckRlZmF1bHRUX1NhbXBsZVN0YXRpb25JRCI6IjAiLCJNX1N0YWZmTmFtZSI6IkFCSVRBIEpVV0lUQSBTQVJJIiwiaXNfY291cmllciI6Ik4iLCJ0aW1lX2F1dG9sb2dvdXQiOiIxMDAwMDAwIiwiaXAiOiIxMDMuMy4yMjAuMjIxIiwiYWdlbnQiOiJNb3ppbGxhXC81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXRcLzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZVwvMTQ5LjAuMC4wIFNhZmFyaVwvNTM3LjM2IiwidmVyc2lvbiI6InYyIiwibGFzdC1sb2dpbiI6IjIwMjYtMDYtMjIgMTE6MjM6MjkiLCJNX1NhdGVsbGl0ZUlEIjowfQ.wkQFPGQ52TeceDQARm8auj6jEb159V46BzTZ9NEE_vM
@poliId = 1
### Search Poli
POST {{baseUrl}}/mockup/masterdata/poli/search
Content-Type: application/json
{
"token": "{{token}}",
"search": "",
"page": 1,
"row_per_page": 10,
"order_by": "id",
"order": "asc"
}
### Search Poli By Name
POST {{baseUrl}}/mockup/masterdata/poli/search
Content-Type: application/json
{
"token": "{{token}}",
"search": "khitan",
"page": 1,
"row_per_page": 10,
"order_by": "name",
"order": "asc"
}
### Get Screening Templates
POST {{baseUrl}}/mockup/masterdata/poli/gettemplates
Content-Type: application/json
{
"token": "{{token}}"
}
### Add Poli
POST {{baseUrl}}/mockup/masterdata/poli/add
Content-Type: application/json
{
"token": "{{token}}",
"code": "POLI_TEST",
"name": "Poli Test",
"description": "Poli untuk test API",
"satusehat_location_id": "",
"screening_template_id": null
}
### Update Poli
POST {{baseUrl}}/mockup/masterdata/poli/update
Content-Type: application/json
{
"token": "{{token}}",
"id": {{poliId}},
"code": "POLI_TEST",
"name": "Poli Test Update",
"description": "Poli untuk test API update",
"satusehat_location_id": "",
"screening_template_id": null
}
### Delete Poli
POST {{baseUrl}}/mockup/masterdata/poli/delete
Content-Type: application/json
{
"token": "{{token}}",
"id": {{poliId}}
}

View File

@@ -0,0 +1,323 @@
<?php
class Poli extends MY_Controller
{
var $db_oneklinik;
public function __construct()
{
parent::__construct();
$this->db_oneklinik = $this->load->database("onedev", true);
}
public function index()
{
echo "POLI API";
}
public function search()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$search = isset($prm['search']) ? trim($prm['search']) : (isset($prm['name']) ? trim($prm['name']) : '');
$like = '%' . $search . '%';
$row_per_page = isset($prm['row_per_page']) && intval($prm['row_per_page']) > 0 ? intval($prm['row_per_page']) : 10;
$page = 1;
if (isset($prm['page']) && intval($prm['page']) > 0) {
$page = intval($prm['page']);
} elseif (isset($prm['current_page']) && intval($prm['current_page']) > 0) {
$page = intval($prm['current_page']);
}
$offset = ($page - 1) * $row_per_page;
$allowed_order_by = array(
'id' => 'cu.M_ClinicUnitID',
'code' => 'cu.M_ClinicUnitCode',
'name' => 'cu.M_ClinicUnitName',
'description' => 'cu.M_ClinicUnitDescription',
'screening_template_name' => 'st.M_ScreeningTemplateName'
);
$order_by = 'cu.M_ClinicUnitID';
if (isset($prm['order_by']) && isset($allowed_order_by[$prm['order_by']])) {
$order_by = $allowed_order_by[$prm['order_by']];
}
$order = isset($prm['order']) && strtolower($prm['order']) === 'desc' ? 'DESC' : 'ASC';
$sql_count = "SELECT COUNT(*) AS total
FROM one_klinik.m_clinic_unit cu
LEFT JOIN one_klinik.m_screening_template st
ON st.M_ScreeningTemplateID = cu.M_ClinicUnitM_ScreeningTemplateID
AND st.M_ScreeningTemplateIsActive = 'Y'
WHERE cu.M_ClinicUnitIsActive = 'Y'
AND (
cu.M_ClinicUnitCode LIKE ?
OR cu.M_ClinicUnitName LIKE ?
OR IFNULL(cu.M_ClinicUnitDescription, '') LIKE ?
OR IFNULL(cu.M_ClinicUnitSatusehatLocationID, '') LIKE ?
OR IFNULL(st.M_ScreeningTemplateName, '') LIKE ?
)";
$query_count = $this->db_oneklinik->query($sql_count, array($like, $like, $like, $like, $like));
if (!$query_count) {
$this->sys_error_db("m_clinic_unit count", $this->db_oneklinik);
exit;
}
$total_filter = intval($query_count->row()->total);
$total_page = ceil($total_filter / $row_per_page);
$sql = "SELECT
cu.M_ClinicUnitID AS id,
cu.M_ClinicUnitCode AS code,
cu.M_ClinicUnitName AS name,
cu.M_ClinicUnitDescription AS description,
cu.M_ClinicUnitSatusehatLocationID AS satusehat_location_id,
cu.M_ClinicUnitM_ScreeningTemplateID AS screening_template_id,
st.M_ScreeningTemplateCode AS screening_template_code,
st.M_ScreeningTemplateName AS screening_template_name,
cu.M_ClinicUnitIsActive AS is_active,
cu.M_ClinicUnitCreated AS created,
cu.M_ClinicUnitLastUpdated AS last_updated
FROM one_klinik.m_clinic_unit cu
LEFT JOIN one_klinik.m_screening_template st
ON st.M_ScreeningTemplateID = cu.M_ClinicUnitM_ScreeningTemplateID
AND st.M_ScreeningTemplateIsActive = 'Y'
WHERE cu.M_ClinicUnitIsActive = 'Y'
AND (
cu.M_ClinicUnitCode LIKE ?
OR cu.M_ClinicUnitName LIKE ?
OR IFNULL(cu.M_ClinicUnitDescription, '') LIKE ?
OR IFNULL(cu.M_ClinicUnitSatusehatLocationID, '') LIKE ?
OR IFNULL(st.M_ScreeningTemplateName, '') LIKE ?
)
ORDER BY {$order_by} {$order}
LIMIT ? OFFSET ?";
$query = $this->db_oneklinik->query($sql, array($like, $like, $like, $like, $like, $row_per_page, $offset));
if (!$query) {
$this->sys_error_db("m_clinic_unit select", $this->db_oneklinik);
exit;
}
$rows = $query->result_array();
$this->sys_ok(array(
"total" => $total_page,
"total_filter" => $total_filter,
"records" => $rows
));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function gettemplates()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$sql = "SELECT
M_ScreeningTemplateID AS id,
M_ScreeningTemplateCode AS code,
M_ScreeningTemplateName AS name,
M_ScreeningTemplateDescription AS description
FROM one_klinik.m_screening_template
WHERE M_ScreeningTemplateIsActive = 'Y'
ORDER BY M_ScreeningTemplateName ASC";
$query = $this->db_oneklinik->query($sql);
if (!$query) {
$this->sys_error_db("m_screening_template select", $this->db_oneklinik);
exit;
}
$rows = $query->result_array();
$this->sys_ok(array("total" => count($rows), "records" => $rows));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function add()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$code = isset($prm['code']) ? trim($prm['code']) : '';
$name = isset($prm['name']) ? trim($prm['name']) : '';
$description = isset($prm['description']) ? trim($prm['description']) : null;
$satusehat_location_id = isset($prm['satusehat_location_id']) ? trim($prm['satusehat_location_id']) : null;
$screening_template_id = isset($prm['screening_template_id']) && $prm['screening_template_id'] !== '' ? intval($prm['screening_template_id']) : null;
$userid = $this->sys_user["M_UserID"];
if ($code === '' || $name === '') {
$this->sys_error("code and name are mandatory");
exit;
}
$duplicate = $this->db_oneklinik->query(
"SELECT COUNT(*) AS total
FROM one_klinik.m_clinic_unit
WHERE M_ClinicUnitCode = ?
OR (M_ClinicUnitIsActive = 'Y' AND M_ClinicUnitName = ?)",
array($code, $name)
);
if (!$duplicate) {
$this->sys_error_db("m_clinic_unit duplicate check", $this->db_oneklinik);
exit;
}
if (intval($duplicate->row()->total) > 0) {
$this->sys_ok(array(
"total" => -1,
"errors" => array(array("field" => "code", "msg" => "Kode atau nama sudah ada")),
"records" => 0
));
exit;
}
$sql = "INSERT INTO one_klinik.m_clinic_unit (
M_ClinicUnitCode,
M_ClinicUnitName,
M_ClinicUnitDescription,
M_ClinicUnitSatusehatLocationID,
M_ClinicUnitM_ScreeningTemplateID,
M_ClinicUnitUserID,
M_ClinicUnitCreated,
M_ClinicUnitLastUpdated
) VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW())";
$query = $this->db_oneklinik->query($sql, array(
$code,
$name,
$description,
$satusehat_location_id,
$screening_template_id,
$userid
));
if (!$query) {
$this->sys_error_db("m_clinic_unit insert", $this->db_oneklinik);
exit;
}
$this->sys_ok(array(
"total" => 1,
"records" => array("xid" => $this->db_oneklinik->insert_id())
));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function update()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
$code = isset($prm['code']) ? trim($prm['code']) : '';
$name = isset($prm['name']) ? trim($prm['name']) : '';
$description = isset($prm['description']) ? trim($prm['description']) : null;
$satusehat_location_id = isset($prm['satusehat_location_id']) ? trim($prm['satusehat_location_id']) : null;
$screening_template_id = isset($prm['screening_template_id']) && $prm['screening_template_id'] !== '' ? intval($prm['screening_template_id']) : null;
$userid = $this->sys_user["M_UserID"];
if (!$id || $code === '' || $name === '') {
$this->sys_error("id, code and name are mandatory");
exit;
}
$duplicate = $this->db_oneklinik->query(
"SELECT COUNT(*) AS total
FROM one_klinik.m_clinic_unit
WHERE M_ClinicUnitID <> ?
AND (M_ClinicUnitCode = ?
OR (M_ClinicUnitIsActive = 'Y' AND M_ClinicUnitName = ?))",
array($id, $code, $name)
);
if (!$duplicate) {
$this->sys_error_db("m_clinic_unit duplicate check", $this->db_oneklinik);
exit;
}
if (intval($duplicate->row()->total) > 0) {
$this->sys_ok(array(
"total" => -1,
"errors" => array(array("field" => "code", "msg" => "Kode atau nama sudah ada")),
"records" => 0
));
exit;
}
$sql = "UPDATE one_klinik.m_clinic_unit SET
M_ClinicUnitCode = ?,
M_ClinicUnitName = ?,
M_ClinicUnitDescription = ?,
M_ClinicUnitSatusehatLocationID = ?,
M_ClinicUnitM_ScreeningTemplateID = ?,
M_ClinicUnitUserID = ?,
M_ClinicUnitLastUpdated = NOW()
WHERE M_ClinicUnitID = ?
AND M_ClinicUnitIsActive = 'Y'";
$query = $this->db_oneklinik->query($sql, array(
$code,
$name,
$description,
$satusehat_location_id,
$screening_template_id,
$userid,
$id
));
if (!$query) {
$this->sys_error_db("m_clinic_unit update", $this->db_oneklinik);
exit;
}
$this->sys_ok(array("total" => 1, "records" => array("xid" => $id)));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function delete()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
if (!$id) {
$this->sys_error("id is mandatory");
exit;
}
$userid = $this->sys_user["M_UserID"];
$sql = "UPDATE one_klinik.m_clinic_unit SET
M_ClinicUnitIsActive = 'N',
M_ClinicUnitUserID = ?,
M_ClinicUnitLastUpdated = NOW()
WHERE M_ClinicUnitID = ?";
$query = $this->db_oneklinik->query($sql, array($userid, $id));
if (!$query) {
$this->sys_error_db("m_clinic_unit delete", $this->db_oneklinik);
exit;
}
$this->sys_ok(array("total" => 1, "records" => array("xid" => $id)));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
}

View File

@@ -0,0 +1,138 @@
@baseUrl = https://devone.aplikasi.web.id/one-api-lab
@token = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJNX1VzZXJJRCI6IjMiLCJNX1VzZXJVc2VybmFtZSI6ImFkbWluICIsIk1fVXNlckdyb3VwRGFzaGJvYXJkIjoib25lLXVpLWxhYlwvdGVzdFwvdnVleFwvb25lLXBhdGllbnQtbGlzdC1iYXJjb2RlLXZ2LTYtY3BvbmVcLyIsIk1fVXNlckRlZmF1bHRUX1NhbXBsZVN0YXRpb25JRCI6IjAiLCJNX1N0YWZmTmFtZSI6IkFCSVRBIEpVV0lUQSBTQVJJIiwiaXNfY291cmllciI6Ik4iLCJ0aW1lX2F1dG9sb2dvdXQiOiIxMDAwMDAwIiwiaXAiOiIxMDMuMy4yMjAuMjIxIiwiYWdlbnQiOiJNb3ppbGxhXC81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXRcLzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZVwvMTQ5LjAuMC4wIFNhZmFyaVwvNTM3LjM2IiwidmVyc2lvbiI6InYyIiwibGFzdC1sb2dpbiI6IjIwMjYtMDYtMjIgMTE6MjM6MjkiLCJNX1NhdGVsbGl0ZUlEIjowfQ.wkQFPGQ52TeceDQARm8auj6jEb159V46BzTZ9NEE_vM
@templateId = 1
@formId = 1
### Search Screening Template
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/search
Content-Type: application/json
{
"token": "{{token}}",
"search": "",
"page": 1,
"row_per_page": 10,
"order_by": "id",
"order": "asc"
}
### Search Screening Template By Name
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/search
Content-Type: application/json
{
"token": "{{token}}",
"search": "khitan",
"page": 1,
"row_per_page": 10,
"order_by": "name",
"order": "asc"
}
### Get Template Detail With Forms
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/getdetail
Content-Type: application/json
{
"token": "{{token}}",
"id": {{templateId}}
}
### Get Forms By Template
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/getforms
Content-Type: application/json
{
"token": "{{token}}",
"template_id": {{templateId}}
}
### Add Screening Template
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/add
Content-Type: application/json
{
"token": "{{token}}",
"code": "SCREENING_TEST",
"name": "Screening Test",
"description": "Template screening untuk test API"
}
### Update Screening Template
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/update
Content-Type: application/json
{
"token": "{{token}}",
"id": {{templateId}},
"code": "SCREENING_TEST",
"name": "Screening Test Update",
"description": "Template screening untuk test API update"
}
### Delete Screening Template
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/delete
Content-Type: application/json
{
"token": "{{token}}",
"id": {{templateId}}
}
### Add Screening Form - Single
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/addform
Content-Type: application/json
{
"token": "{{token}}",
"template_id": {{templateId}},
"question": "Apakah pasien sedang demam?",
"answer_type": "single",
"options": [
{"label": "Ya", "value": false, "id": "o01"},
{"label": "Tidak", "value": false, "id": "o02"}
],
"sort_order": 1,
"is_required": "Y"
}
### Add Screening Form - Text
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/addform
Content-Type: application/json
{
"token": "{{token}}",
"template_id": {{templateId}},
"question": "Catatan petugas screening",
"answer_type": "text",
"options": null,
"sort_order": 2,
"is_required": "N"
}
### Update Screening Form
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/updateform
Content-Type: application/json
{
"token": "{{token}}",
"id": {{formId}},
"template_id": {{templateId}},
"question": "Apakah pasien sedang demam tinggi?",
"answer_type": "single",
"options": [
{"label": "Ya", "value": false, "id": "o01"},
{"label": "Tidak", "value": false, "id": "o02"}
],
"sort_order": 1,
"is_required": "Y"
}
### Delete Screening Form
POST {{baseUrl}}/mockup/masterdata/screeningtemplate/deleteform
Content-Type: application/json
{
"token": "{{token}}",
"id": {{formId}}
}

View File

@@ -0,0 +1,549 @@
<?php
class Screeningtemplate extends MY_Controller
{
var $db_oneklinik;
public function __construct()
{
parent::__construct();
$this->db_oneklinik = $this->load->database("onedev", true);
}
public function index()
{
echo "SCREENING TEMPLATE API";
}
public function search()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$search = isset($prm['search']) ? trim($prm['search']) : '';
$like = '%' . $search . '%';
$row_per_page = isset($prm['row_per_page']) && intval($prm['row_per_page']) > 0 ? intval($prm['row_per_page']) : 10;
$page = isset($prm['page']) && intval($prm['page']) > 0 ? intval($prm['page']) : 1;
$offset = ($page - 1) * $row_per_page;
$allowed_order_by = array(
'id' => 't.M_ScreeningTemplateID',
'code' => 't.M_ScreeningTemplateCode',
'name' => 't.M_ScreeningTemplateName',
'description' => 't.M_ScreeningTemplateDescription'
);
$order_by = 't.M_ScreeningTemplateID';
if (isset($prm['order_by']) && isset($allowed_order_by[$prm['order_by']])) {
$order_by = $allowed_order_by[$prm['order_by']];
}
$order = isset($prm['order']) && strtolower($prm['order']) === 'desc' ? 'DESC' : 'ASC';
$sql_count = "SELECT COUNT(*) AS total
FROM one_klinik.m_screening_template t
WHERE t.M_ScreeningTemplateIsActive = 'Y'
AND (
t.M_ScreeningTemplateCode LIKE ?
OR t.M_ScreeningTemplateName LIKE ?
OR IFNULL(t.M_ScreeningTemplateDescription, '') LIKE ?
)";
$query_count = $this->db_oneklinik->query($sql_count, array($like, $like, $like));
if (!$query_count) {
$this->sys_error_db("m_screening_template count", $this->db_oneklinik);
exit;
}
$total_filter = intval($query_count->row()->total);
$total_page = ceil($total_filter / $row_per_page);
$sql = "SELECT
t.M_ScreeningTemplateID AS id,
t.M_ScreeningTemplateCode AS code,
t.M_ScreeningTemplateName AS name,
t.M_ScreeningTemplateDescription AS description,
t.M_ScreeningTemplateIsActive AS is_active,
t.M_ScreeningTemplateCreated AS created,
t.M_ScreeningTemplateLastUpdated AS last_updated,
COUNT(f.M_ScreeningFormID) AS form_count
FROM one_klinik.m_screening_template t
LEFT JOIN one_klinik.m_screening_form f
ON f.M_ScreeningFormM_ScreeningTemplateID = t.M_ScreeningTemplateID
AND f.M_ScreeningFormIsActive = 'Y'
WHERE t.M_ScreeningTemplateIsActive = 'Y'
AND (
t.M_ScreeningTemplateCode LIKE ?
OR t.M_ScreeningTemplateName LIKE ?
OR IFNULL(t.M_ScreeningTemplateDescription, '') LIKE ?
)
GROUP BY
t.M_ScreeningTemplateID
ORDER BY {$order_by} {$order}
LIMIT ? OFFSET ?";
$query = $this->db_oneklinik->query($sql, array($like, $like, $like, $row_per_page, $offset));
if (!$query) {
$this->sys_error_db("m_screening_template select", $this->db_oneklinik);
exit;
}
$this->sys_ok(array(
"total" => $total_page,
"total_filter" => $total_filter,
"records" => $query->result_array()
));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function getdetail()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
if (!$id) {
$this->sys_error("id is mandatory");
exit;
}
$query = $this->db_oneklinik->query(
"SELECT
M_ScreeningTemplateID AS id,
M_ScreeningTemplateCode AS code,
M_ScreeningTemplateName AS name,
M_ScreeningTemplateDescription AS description,
M_ScreeningTemplateIsActive AS is_active,
M_ScreeningTemplateCreated AS created,
M_ScreeningTemplateLastUpdated AS last_updated
FROM one_klinik.m_screening_template
WHERE M_ScreeningTemplateID = ?
AND M_ScreeningTemplateIsActive = 'Y'",
array($id)
);
if (!$query) {
$this->sys_error_db("m_screening_template select detail", $this->db_oneklinik);
exit;
}
$row = $query->row_array();
if (!$row) {
$this->sys_ok(array("total" => 0, "records" => null));
exit;
}
$row['forms'] = $this->get_form_rows($id);
$this->sys_ok(array("total" => 1, "records" => $row));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function getforms()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$template_id = isset($prm['template_id']) ? intval($prm['template_id']) : (isset($prm['id']) ? intval($prm['id']) : 0);
if (!$template_id) {
$this->sys_error("template_id is mandatory");
exit;
}
$rows = $this->get_form_rows($template_id);
$this->sys_ok(array("total" => count($rows), "records" => $rows));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function add()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$code = isset($prm['code']) ? trim($prm['code']) : '';
$name = isset($prm['name']) ? trim($prm['name']) : '';
$description = isset($prm['description']) ? trim($prm['description']) : null;
$userid = $this->sys_user["M_UserID"];
if ($code === '' || $name === '') {
$this->sys_error("code and name are mandatory");
exit;
}
$duplicate = $this->db_oneklinik->query(
"SELECT COUNT(*) AS total
FROM one_klinik.m_screening_template
WHERE M_ScreeningTemplateCode = ?
OR (M_ScreeningTemplateIsActive = 'Y' AND M_ScreeningTemplateName = ?)",
array($code, $name)
);
if (!$duplicate) {
$this->sys_error_db("m_screening_template duplicate check", $this->db_oneklinik);
exit;
}
if (intval($duplicate->row()->total) > 0) {
$this->sys_ok(array(
"total" => -1,
"errors" => array(array("field" => "code", "msg" => "Kode atau nama sudah ada")),
"records" => 0
));
exit;
}
$sql = "INSERT INTO one_klinik.m_screening_template (
M_ScreeningTemplateCode,
M_ScreeningTemplateName,
M_ScreeningTemplateDescription,
M_ScreeningTemplateUserID,
M_ScreeningTemplateCreated,
M_ScreeningTemplateLastUpdated
) VALUES (?, ?, ?, ?, NOW(), NOW())";
$query = $this->db_oneklinik->query($sql, array($code, $name, $description, $userid));
if (!$query) {
$this->sys_error_db("m_screening_template insert", $this->db_oneklinik);
exit;
}
$this->sys_ok(array(
"total" => 1,
"records" => array("xid" => $this->db_oneklinik->insert_id())
));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function update()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
$code = isset($prm['code']) ? trim($prm['code']) : '';
$name = isset($prm['name']) ? trim($prm['name']) : '';
$description = isset($prm['description']) ? trim($prm['description']) : null;
$userid = $this->sys_user["M_UserID"];
if (!$id || $code === '' || $name === '') {
$this->sys_error("id, code and name are mandatory");
exit;
}
$duplicate = $this->db_oneklinik->query(
"SELECT COUNT(*) AS total
FROM one_klinik.m_screening_template
WHERE M_ScreeningTemplateID <> ?
AND (M_ScreeningTemplateCode = ?
OR (M_ScreeningTemplateIsActive = 'Y' AND M_ScreeningTemplateName = ?))",
array($id, $code, $name)
);
if (!$duplicate) {
$this->sys_error_db("m_screening_template duplicate check", $this->db_oneklinik);
exit;
}
if (intval($duplicate->row()->total) > 0) {
$this->sys_ok(array(
"total" => -1,
"errors" => array(array("field" => "code", "msg" => "Kode atau nama sudah ada")),
"records" => 0
));
exit;
}
$sql = "UPDATE one_klinik.m_screening_template SET
M_ScreeningTemplateCode = ?,
M_ScreeningTemplateName = ?,
M_ScreeningTemplateDescription = ?,
M_ScreeningTemplateUserID = ?,
M_ScreeningTemplateLastUpdated = NOW()
WHERE M_ScreeningTemplateID = ?
AND M_ScreeningTemplateIsActive = 'Y'";
$query = $this->db_oneklinik->query($sql, array($code, $name, $description, $userid, $id));
if (!$query) {
$this->sys_error_db("m_screening_template update", $this->db_oneklinik);
exit;
}
$this->sys_ok(array("total" => 1, "records" => array("xid" => $id)));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function delete()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
if (!$id) {
$this->sys_error("id is mandatory");
exit;
}
$userid = $this->sys_user["M_UserID"];
$this->db_oneklinik->trans_begin();
$query = $this->db_oneklinik->query(
"UPDATE one_klinik.m_screening_form SET
M_ScreeningFormIsActive = 'N',
M_ScreeningFormUserID = ?,
M_ScreeningFormLastUpdated = NOW()
WHERE M_ScreeningFormM_ScreeningTemplateID = ?",
array($userid, $id)
);
if (!$query) {
$this->db_oneklinik->trans_rollback();
$this->sys_error_db("m_screening_form delete by template", $this->db_oneklinik);
exit;
}
$query = $this->db_oneklinik->query(
"UPDATE one_klinik.m_screening_template SET
M_ScreeningTemplateIsActive = 'N',
M_ScreeningTemplateUserID = ?,
M_ScreeningTemplateLastUpdated = NOW()
WHERE M_ScreeningTemplateID = ?",
array($userid, $id)
);
if (!$query) {
$this->db_oneklinik->trans_rollback();
$this->sys_error_db("m_screening_template delete", $this->db_oneklinik);
exit;
}
$this->db_oneklinik->trans_complete();
$this->sys_ok(array("total" => 1, "records" => array("xid" => $id)));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function addform()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$template_id = isset($prm['template_id']) ? intval($prm['template_id']) : 0;
$question = isset($prm['question']) ? trim($prm['question']) : '';
$answer_type = isset($prm['answer_type']) ? trim($prm['answer_type']) : 'single';
$options = isset($prm['options']) ? $this->normalize_options($prm['options'], $answer_type) : null;
$sort_order = isset($prm['sort_order']) ? intval($prm['sort_order']) : 0;
$is_required = isset($prm['is_required']) && $prm['is_required'] === 'N' ? 'N' : 'Y';
$userid = $this->sys_user["M_UserID"];
if (!$template_id || $question === '') {
$this->sys_error("template_id and question are mandatory");
exit;
}
if (!$this->is_valid_answer_type($answer_type)) {
$this->sys_error("answer_type must be single, multi, or text");
exit;
}
$sql = "INSERT INTO one_klinik.m_screening_form (
M_ScreeningFormM_ScreeningTemplateID,
M_ScreeningFormQuestion,
M_ScreeningFormAnswerType,
M_ScreeningFormOptions,
M_ScreeningFormSortOrder,
M_ScreeningFormIsRequired,
M_ScreeningFormUserID,
M_ScreeningFormCreated,
M_ScreeningFormLastUpdated
) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
$query = $this->db_oneklinik->query($sql, array(
$template_id,
$question,
$answer_type,
$options,
$sort_order,
$is_required,
$userid
));
if (!$query) {
$this->sys_error_db("m_screening_form insert", $this->db_oneklinik);
exit;
}
$this->sys_ok(array(
"total" => 1,
"records" => array("xid" => $this->db_oneklinik->insert_id())
));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function updateform()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
$template_id = isset($prm['template_id']) ? intval($prm['template_id']) : 0;
$question = isset($prm['question']) ? trim($prm['question']) : '';
$answer_type = isset($prm['answer_type']) ? trim($prm['answer_type']) : 'single';
$options = isset($prm['options']) ? $this->normalize_options($prm['options'], $answer_type) : null;
$sort_order = isset($prm['sort_order']) ? intval($prm['sort_order']) : 0;
$is_required = isset($prm['is_required']) && $prm['is_required'] === 'N' ? 'N' : 'Y';
$userid = $this->sys_user["M_UserID"];
if (!$id || !$template_id || $question === '') {
$this->sys_error("id, template_id and question are mandatory");
exit;
}
if (!$this->is_valid_answer_type($answer_type)) {
$this->sys_error("answer_type must be single, multi, or text");
exit;
}
$sql = "UPDATE one_klinik.m_screening_form SET
M_ScreeningFormM_ScreeningTemplateID = ?,
M_ScreeningFormQuestion = ?,
M_ScreeningFormAnswerType = ?,
M_ScreeningFormOptions = ?,
M_ScreeningFormSortOrder = ?,
M_ScreeningFormIsRequired = ?,
M_ScreeningFormUserID = ?,
M_ScreeningFormLastUpdated = NOW()
WHERE M_ScreeningFormID = ?
AND M_ScreeningFormIsActive = 'Y'";
$query = $this->db_oneklinik->query($sql, array(
$template_id,
$question,
$answer_type,
$options,
$sort_order,
$is_required,
$userid,
$id
));
if (!$query) {
$this->sys_error_db("m_screening_form update", $this->db_oneklinik);
exit;
}
$this->sys_ok(array("total" => 1, "records" => array("xid" => $id)));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
public function deleteform()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$id = isset($prm['id']) ? intval($prm['id']) : 0;
if (!$id) {
$this->sys_error("id is mandatory");
exit;
}
$userid = $this->sys_user["M_UserID"];
$sql = "UPDATE one_klinik.m_screening_form SET
M_ScreeningFormIsActive = 'N',
M_ScreeningFormUserID = ?,
M_ScreeningFormLastUpdated = NOW()
WHERE M_ScreeningFormID = ?";
$query = $this->db_oneklinik->query($sql, array($userid, $id));
if (!$query) {
$this->sys_error_db("m_screening_form delete", $this->db_oneklinik);
exit;
}
$this->sys_ok(array("total" => 1, "records" => array("xid" => $id)));
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
private function get_form_rows($template_id)
{
$query = $this->db_oneklinik->query(
"SELECT
M_ScreeningFormID AS id,
M_ScreeningFormM_ScreeningTemplateID AS template_id,
M_ScreeningFormQuestion AS question,
M_ScreeningFormAnswerType AS answer_type,
M_ScreeningFormOptions AS options,
M_ScreeningFormSortOrder AS sort_order,
M_ScreeningFormIsRequired AS is_required,
M_ScreeningFormIsActive AS is_active,
M_ScreeningFormCreated AS created,
M_ScreeningFormLastUpdated AS last_updated
FROM one_klinik.m_screening_form
WHERE M_ScreeningFormM_ScreeningTemplateID = ?
AND M_ScreeningFormIsActive = 'Y'
ORDER BY M_ScreeningFormSortOrder ASC, M_ScreeningFormID ASC",
array($template_id)
);
if (!$query) {
$this->sys_error_db("m_screening_form select", $this->db_oneklinik);
exit;
}
$rows = $query->result_array();
foreach ($rows as $k => $row) {
$rows[$k]['options_json'] = $row['options'] ? json_decode($row['options'], true) : null;
}
return $rows;
}
private function is_valid_answer_type($answer_type)
{
return in_array($answer_type, array('single', 'multi', 'text'));
}
private function normalize_options($options, $answer_type)
{
if ($answer_type === 'text') {
return null;
}
if ($options === null) {
return null;
}
if (is_array($options)) {
return json_encode($options);
}
$options = trim($options);
return $options === '' ? null : $options;
}
}

View File

@@ -11,8 +11,33 @@ class Preregisterapp extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
private function _mask_name($v) {
if (!$v) return $v;
$v = trim($v);
$words = preg_split('/\s+/', $v);
if (count($words) === 1) {
$l = mb_strlen($v, 'UTF-8');
if ($l <= 2) return $v;
return mb_substr($v, 0, 2, 'UTF-8') . str_repeat('*', $l - 2);
}
$first = $words[0];
$rest = array_slice($words, 1);
$masked = array_map(function($w) {
if (!$w) return '';
$init = mb_substr($w, 0, 1, 'UTF-8');
return $init . str_repeat('*', max(3, mb_strlen($w, 'UTF-8') - 1));
}, $rest);
return $first . ' ' . implode(' ', $masked);
}
private function _mask_phone($v) { if (!$v) return $v; $d=preg_replace('/[^0-9]/','',trim($v)); $l=strlen($d); if($l<=4) return '****'; if($l<=8) return substr($d,0,4).str_repeat('*',$l-4); return substr($d,0,4).str_repeat('*',$l-7).substr($d,-3); }
private function _mask_email($v) { if (!$v||strpos($v,'@')===false) return $v; [$loc,$dom]=explode('@',$v,2); return mb_substr($loc,0,min(2,mb_strlen($loc,'UTF-8')),'UTF-8').'***@'.$dom; }
private function _mask_short($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=2) return '***'; return mb_substr($v,0,2,'UTF-8').'***'; }
private function _mask_id($v) { if (!$v) return $v; $v=trim($v); $l=strlen($v); if($l<=4) return '****'; return substr($v,0,4).str_repeat('*',max(3,$l-6)).($l>6?substr($v,-2):''); }
private function _mask_address($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=5) return '***'; return mb_substr($v,0,5,'UTF-8').'***'; }
function searchcompany(){
@@ -477,59 +502,78 @@ class Preregisterapp extends MY_Controller
$enc = $this->ibl_encryptor;
if ($prm['search'] != '')
{
$e = explode('+', $prm['search']);
if (isset($e[0]))
$q['name'] = "AND M_PatientName LIKE '%{$e[0]}%'";
if (isset($e[0]) && strlen($e[0]) >= 3) {
$toks = $enc->query_tokens($e[0]);
$conds = [];
foreach ($toks as $tok) { $tok_esc = $this->db_onedev->escape_str($tok); $conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')"; }
if ($conds) $q['name'] = "AND (" . implode(' AND ', $conds) . ")";
}
if (isset($e[1]))
$q['dob'] = "AND ((DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$e[1]}%' and M_PatientDOB IS NOT NULL) OR (M_PatientDOB IS NULL AND '{$e[1]}' = ''))";
if (isset($e[2]))
$q['nik'] = "AND M_PatientNIK LIKE '%{$e[2]}%'";
if (isset($e[2]) && strlen($e[2]) >= 3) {
$toks = $enc->query_tokens($e[2]);
$conds = [];
foreach ($toks as $tok) { $tok_esc = $this->db_onedev->escape_str($tok); $conds[] = "JSON_CONTAINS(M_PatientNIK_bidx, '\"$tok_esc\"')"; }
if ($conds) $q['nik'] = "AND (" . implode(' AND ', $conds) . ")";
}
}
$sql = "SELECT m_patient.*,
$sql = "SELECT m_patient.*,
'N' divider,
concat(M_TitleName,' ',IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) M_PatientName,
M_PatientName M_PatientRealName, M_TitleID, M_TitleName, M_SexID, M_SexName,
concat(M_TitleName,' ',IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) M_PatientNameDisplay,
M_TitleID, M_TitleName, M_SexID, M_SexName,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as dob_ina,
IFNULL(M_ReligionName, '-') M_ReligionName,
M_PatientNoReg as Mcu_PreregisterDetailsPID,
M_PatientNIK as Mcu_PreregisterDetailsNIK,
M_PatientID as Mcu_PreregisterDetailsM_PatientID,
M_TitleID as Mcu_PreregisterDetailsM_TitleID,
M_PatientName as Mcu_PreregisterDetailsPatientName,
M_SexCode as Mcu_PreregisterDetailsM_SexCode,
M_PatientDOB as Mcu_PreregisterDetailsDOB,
IFNULL(M_ReligionID,0) as Mcu_PreregisterDetailsM_ReligionID,
M_PatientJabatan as Mcu_PreregisterDetailsJabatan,
M_PatientEmail as Mcu_PreregisterDetailsEmail,
M_PatientHP as Mcu_PreregisterDetailsHp,
M_PatientKedudukan as Mcu_PreregisterDetailsKedudukan,
M_PatientLocation as Mcu_PreregisterDetailsLocation,
M_PatientJob as Mcu_PreregisterDetailsJob
from
m_patient
from
m_patient
$join_company
join m_title on M_PatientM_TitleID = M_TitleID
join m_sex on M_PatientM_SexID = M_SexID
left join m_religion on m_patientm_religionid = m_religionid
where M_PatientIsActive = 'Y'
where M_PatientIsActive = 'Y'
{$q['name']}
{$q['dob']}
{$q['nik']}
group by M_PatientID
limit $number_limit offset $number_offset";
//echo $sql;
$query = $this->db_onedev->query($sql);
if ($query) {
if ($query) {
$rows = $query->result_array();
if($rows){
$per_divider = 1;
foreach($rows as $k => $v){
$name = $enc->decrypt($v['M_PatientName_enc'] ?? '') ?? $v['M_PatientName'];
$hp = $enc->decrypt($v['M_PatientHP_enc'] ?? '') ?? $v['M_PatientHP'];
$email = $enc->decrypt($v['M_PatientEmail_enc'] ?? '') ?? $v['M_PatientEmail'];
$nik = $enc->decrypt($v['M_PatientNIK_enc'] ?? '') ?? $v['M_PatientNIK'];
$rows[$k]['M_PatientName'] = $name;
$rows[$k]['M_PatientHP'] = $hp;
$rows[$k]['M_PatientEmail'] = $email;
$rows[$k]['M_PatientNIK'] = $nik;
$rows[$k]['Mcu_PreregisterDetailsPatientName'] = $name;
$rows[$k]['Mcu_PreregisterDetailsNIK'] = $nik;
$rows[$k]['Mcu_PreregisterDetailsEmail'] = $email;
$rows[$k]['Mcu_PreregisterDetailsHp'] = $hp;
foreach (array_keys($rows[$k]) as $col) {
if (substr($col, -4) === '_enc' || substr($col, -5) === '_bidx') unset($rows[$k][$col]);
}
if($per_divider == 10){
$rows[$k]['divider'] = 'Y';
}
@@ -569,71 +613,86 @@ class Preregisterapp extends MY_Controller
$name = $prm['name'];
$dob = $prm['dob'];
$nik = $prm['nik'];
$add_where = '';
if($nik != ''){
$add_where = " AND M_PatientNIK = '{$nik}'";
$enc = $this->ibl_encryptor;
$name_where = '1=1';
if (strlen($name) >= 3) {
$toks = $enc->query_tokens($name);
$conds = [];
foreach ($toks as $tok) { $tok_esc = $this->db_onedev->escape_str($tok); $conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')"; }
if ($conds) $name_where = implode(' AND ', $conds);
}
$nik_where = '';
if ($nik != '' && strlen($nik) >= 3) {
$toks = $enc->query_tokens($nik);
$conds = [];
foreach ($toks as $tok) { $tok_esc = $this->db_onedev->escape_str($tok); $conds[] = "JSON_CONTAINS(M_PatientNIK_bidx, '\"$tok_esc\"')"; }
if ($conds) $nik_where = "AND (" . implode(' AND ', $conds) . ")";
}
$setup = $prm['setup'];
$join_company = "";
if(isset($prm['company']) && intval($prm['company']) > 0){
$join_company = "JOIN t_orderheader ON T_OrderHeaderM_PatientID = M_PatientID AND
T_OrderHeaderIsActive = 'Y' AND
$join_company = "JOIN t_orderheader ON T_OrderHeaderM_PatientID = M_PatientID AND
T_OrderHeaderIsActive = 'Y' AND
T_OrderHeaderM_CompanyID = {$prm['company']}";
}
$sql = "SELECT COUNT(*) as total
FROM (
SELECT *
SELECT M_PatientID
FROM m_patient
$join_company
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
WHERE
M_PatientIsActive = 'Y' AND
M_PatientName LIKE CONCAT('%','{$name}','%') AND
(DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$dob}%' and M_PatientDOB IS NOT NULL)
{$add_where}
M_PatientIsActive = 'Y' AND ({$name_where})
AND (DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$dob}%' and M_PatientDOB IS NOT NULL)
{$nik_where}
GROUP BY M_PatientID
) x
";
//echo $sql;
) x";
$countx = $this->db_onedev->query($sql)->row()->total;
$sql = "SELECT *, DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') as dob_ina,
$sql = "SELECT m_patient.*, DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') as dob_ina,
M_PatientNoReg as Mcu_PreregisterDetailsPID,
M_PatientNIK as Mcu_PreregisterDetailsNIK,
M_PatientID as Mcu_PreregisterDetailsM_PatientID,
M_TitleID as Mcu_PreregisterDetailsM_TitleID,
M_PatientName as Mcu_PreregisterDetailsPatientName,
M_SexCode as Mcu_PreregisterDetailsM_SexCode,
M_PatientDOB as Mcu_PreregisterDetailsDOB,
IFNULL(M_ReligionID,0) as Mcu_PreregisterDetailsM_ReligionID,
M_PatientJabatan as Mcu_PreregisterDetailsJabatan,
M_PatientEmail as Mcu_PreregisterDetailsEmail,
M_PatientHP as Mcu_PreregisterDetailsHp,
M_PatientKedudukan as Mcu_PreregisterDetailsKedudukan,
M_PatientLocation as Mcu_PreregisterDetailsLocation,
M_PatientJob as Mcu_PreregisterDetailsJob
FROM m_patient
$join_company
LEFT join m_title on M_PatientM_TitleID = M_TitleID
LEFT join m_sex on M_PatientM_SexID = M_SexID
left join m_religion on m_patientm_religionid = m_religionid
LEFT join m_sex on M_PatientM_SexID = M_SexID
left join m_religion on m_patientm_religionid = m_religionid
WHERE
M_PatientIsActive = 'Y' AND
M_PatientName LIKE CONCAT('%','{$name}','%') AND
(DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$dob}%' and M_PatientDOB IS NOT NULL)
{$add_where}
M_PatientIsActive = 'Y' AND ({$name_where})
AND (DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$dob}%' and M_PatientDOB IS NOT NULL)
{$nik_where}
GROUP BY M_PatientID
LIMIT 10 OFFSET 0
";
//echo $sql;
LIMIT 10 OFFSET 0";
$rows = $this->db_onedev->query($sql)->result_array();
$result = array(
"total" => $countx ,
"records" => $rows
);
foreach ($rows as $k => $v) {
$name_dec = $enc->decrypt($v['M_PatientName_enc'] ?? '') ?? $v['M_PatientName'];
$hp_dec = $enc->decrypt($v['M_PatientHP_enc'] ?? '') ?? $v['M_PatientHP'];
$email_dec = $enc->decrypt($v['M_PatientEmail_enc'] ?? '') ?? $v['M_PatientEmail'];
$nik_dec = $enc->decrypt($v['M_PatientNIK_enc'] ?? '') ?? $v['M_PatientNIK'];
$rows[$k]['M_PatientName'] = $name_dec;
$rows[$k]['M_PatientHP'] = $hp_dec;
$rows[$k]['M_PatientEmail'] = $email_dec;
$rows[$k]['M_PatientNIK'] = $nik_dec;
$rows[$k]['Mcu_PreregisterDetailsPatientName'] = $name_dec;
$rows[$k]['Mcu_PreregisterDetailsNIK'] = $nik_dec;
$rows[$k]['Mcu_PreregisterDetailsEmail'] = $email_dec;
$rows[$k]['Mcu_PreregisterDetailsHp'] = $hp_dec;
foreach (array_keys($rows[$k]) as $col) {
if (substr($col, -4) === '_enc' || substr($col, -5) === '_bidx') unset($rows[$k][$col]);
}
}
$result = array("total" => $countx, "records" => $rows);
$this->sys_ok($result);
exit;
}
@@ -1014,6 +1073,11 @@ class Preregisterapp extends MY_Controller
unset($datas[0]);
foreach($datas as $k => $v){
$pdob = date('Y-m-d',strtotime($v['TANGGAL_LAHIR']));
$m_nama = $this->db_onedev->escape_str($this->_mask_name($v['NAMA']));
$m_ktp = $this->_mask_id($v['KTP']);
$m_nik = $this->_mask_id($v['NIK']);
$m_email = $this->_mask_email($v['EMAIL']);
$m_hp = $this->_mask_phone($v['HP']);
$query = " INSERT INTO mcu_preregister_patients (
Mcu_PreregisterDetailsMcuOfflinePrepareID,
Mcu_PreregisterDetailsPID,
@@ -1036,11 +1100,11 @@ class Preregisterapp extends MY_Controller
VALUES(
'{$prm['xid']}',
'{$v['PID']}',
'{$v['NIK']}',
'{$v['KTP']}',
'{$v['NAMA']}',
'{$v['EMAIL']}',
'{$v['HP']}',
'{$m_nik}',
'{$m_ktp}',
'{$m_nama}',
'{$m_email}',
'{$m_hp}',
'{$pdob}',
'{$v['KEDUDUKAN']}',
'{$v['JABATAN']}',
@@ -1071,31 +1135,29 @@ class Preregisterapp extends MY_Controller
}
}
$enc = $this->ibl_encryptor;
if($v['KTP'] != ''){
$sql = "SELECT *
FROM m_patient
WHERE
M_PatientM_IdTypeID = 1 AND
M_PatientIDNumber = '{$v['KTP']}' AND
M_PatientIsActive = 'Y'
LIMIT 1";
$exist_r = $this->db_onedev->query($sql)->row_array();
if($exist_r){
$patient_id = $exist_r["M_PatientID"];
$ktp_toks = $enc->query_tokens($v['KTP']);
$ktp_conds = [];
foreach ($ktp_toks as $tok) { $tok_esc = $this->db_onedev->escape_str($tok); $ktp_conds[] = "JSON_CONTAINS(M_PatientNIK_bidx, '\"$tok_esc\"')"; }
if ($ktp_conds) {
$sql = "SELECT M_PatientID FROM m_patient
WHERE M_PatientIsActive = 'Y' AND (" . implode(' AND ', $ktp_conds) . ")
LIMIT 1";
$exist_r = $this->db_onedev->query($sql)->row_array();
if($exist_r) $patient_id = $exist_r["M_PatientID"];
}
}
if($patient_id == 0){
$sql = "SELECT *
FROM m_patient
WHERE
M_PatientName = '{$v['NAMA']}' AND
M_PatientDOB = '{$pdob}' AND
M_PatientIsActive = 'Y' LIMIT 1";
$name_toks = $enc->query_tokens($v['NAMA']);
$name_conds = [];
foreach ($name_toks as $tok) { $tok_esc = $this->db_onedev->escape_str($tok); $name_conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')"; }
$name_where = $name_conds ? implode(' AND ', $name_conds) : '0';
$sql = "SELECT M_PatientID FROM m_patient
WHERE ({$name_where}) AND M_PatientDOB = '{$pdob}' AND M_PatientIsActive = 'Y' LIMIT 1";
$exist_r = $this->db_onedev->query($sql)->row_array();
if($exist_r){
$patient_id = $exist_r["M_PatientID"];
}
if($exist_r) $patient_id = $exist_r["M_PatientID"];
}
@@ -1148,28 +1210,40 @@ class Preregisterapp extends MY_Controller
)";
//echo $sql;
$this->db_onedev->query($sql);*/
$data_insert_patient = array(
'M_PatientName' => $v["NAMA"] ,
'M_PatientM_TitleID' => $title_id ,
'M_PatientM_SexID' => $sex_id,
'M_PatientM_ReligionID' => $religion_id ,
'M_PatientPOB' => '-',
'M_PatientDOB' => $pdob,
'M_PatientNIK' => $v["NIK"] ,
'M_PatientJabatan' => $v['JABATAN'],
'M_PatientLocation' => $v['LOKASI'],
'M_PatientKedudukan' => $v['KEDUDUKAN'] ,
'M_PatientJob' => $v['JOB'],
'M_PatientEmail' => $v['EMAIL'],
'M_PatientHP' => $v['HP'],
'M_PatientUserID' => $userid
);
$enc = $this->ibl_encryptor;
$dob_str = date('d-m-Y', strtotime($pdob));
$data_insert_patient = [
'M_PatientName' => $this->_mask_name($v["NAMA"]),
'M_PatientName_enc' => $enc->encrypt($v["NAMA"]),
'M_PatientName_bidx' => $enc->search_bidx($v["NAMA"]),
'M_PatientM_TitleID' => $title_id,
'M_PatientM_SexID' => $sex_id,
'M_PatientM_ReligionID' => $religion_id,
'M_PatientPOB' => '***',
'M_PatientPOB_enc' => $enc->encrypt('-'),
'M_PatientDOB' => $pdob,
'M_PatientDOB_enc' => $enc->encrypt($dob_str),
'M_PatientDOB_bidx' => $enc->search_bidx($dob_str),
'M_PatientNIK' => $v["NIK"],
'M_PatientNIK_bidx' => $enc->search_bidx($v["NIK"] ?? ''),
'M_PatientJabatan' => $v['JABATAN'],
'M_PatientLocation' => $v['LOKASI'],
'M_PatientKedudukan' => $v['KEDUDUKAN'],
'M_PatientJob' => $v['JOB'],
'M_PatientEmail' => $this->_mask_email($v['EMAIL']),
'M_PatientEmail_enc' => $enc->encrypt($v['EMAIL']),
'M_PatientHP' => $this->_mask_phone($v['HP']),
'M_PatientHP_enc' => $enc->encrypt($v['HP']),
'M_PatientHP_bidx' => $enc->search_bidx($v['HP']),
'M_PatientUserID' => $userid,
];
if(isset($v["KTP"]) && $v["KTP"] != ''){
$data_insert_patient['M_PatientM_IdTypeID'] = 1;
$data_insert_patient['M_PatientIDNumber'] = $v["KTP"];
$data_insert_patient['M_PatientM_IdTypeID'] = 1;
$data_insert_patient['M_PatientIDNumber'] = $this->_mask_id($v["KTP"]);
$data_insert_patient['M_PatientIDNumber_enc'] = $enc->encrypt($v["KTP"]);
}
$this->db->insert('m_patient', $data_insert_patient);
$this->db_onedev->insert('m_patient', $data_insert_patient);
$patient_id = $this->db_onedev->insert_id();
$sql = "SELECT * FROM m_patient WHERE M_PatientID = {$patient_id}";
$ptn = $this->db_onedev->query($sql)->row_array();
@@ -1244,56 +1318,46 @@ class Preregisterapp extends MY_Controller
$prm = $this->sys_input;
$userid = $this->sys_user["M_UserID"];
$pdob = date('Y-m-d',strtotime($prm['M_PatientDOB']));
$query ="INSERT INTO m_patient (
M_PatientM_TitleID,
M_PatientPrefix,
M_PatientName,
M_PatientSuffix,
M_PatientDOB,
M_PatientM_SexID,
M_PatientM_ReligionID,
M_PatientEmail,
M_PatientPOB,
M_PatientHP,
M_PatientPhone,
M_PatientM_IdTypeID,
M_PatientIDNumber,
M_PatientNote,
M_PatientNIK,
M_PatientJabatan,
M_PatientKedudukan,
M_PatientPJ,
M_PatientLocation,
M_PatientJob,
M_PatientUserID
)
VALUES(
'{$prm['M_PatientM_TitleID']}',
'{$prm['M_PatientPrefix']}',
'{$prm['M_PatientName']}',
'{$prm['M_PatientSuffix']}',
'{$pdob}',
'{$prm['M_PatientM_SexID']}',
'{$prm['M_PatientM_ReligionID']}',
'{$prm['M_PatientEmail']}',
'{$prm['M_PatientPOB']}',
'{$prm['M_PatientHP']}',
'{$prm['M_PatientPhone']}',
'{$prm['M_PatientM_IdTypeID']}',
'{$prm['M_PatientIDNumber']}',
'{$prm['M_PatientNote']}',
'{$prm['M_PatientNIK']}',
'{$prm['M_PatientJabatan']}',
'{$prm['M_PatientKedudukan']}',
'{$prm['M_PatientPJ']}',
'{$prm['M_PatientLocation']}',
'{$prm['M_PatientJob']}',
$userid
)
";
//echo $query;
$rows = $this->db_onedev->query($query);
$pdob = date('Y-m-d', strtotime($prm['M_PatientDOB']));
$dob_str = date('d-m-Y', strtotime($prm['M_PatientDOB']));
$patient_name = $prm['M_PatientName'];
$enc = $this->ibl_encryptor;
$ptn = [
'M_PatientName' => $this->_mask_name($patient_name),
'M_PatientName_enc' => $enc->encrypt($patient_name),
'M_PatientName_bidx' => $enc->search_bidx($patient_name),
'M_PatientM_TitleID' => $prm['M_PatientM_TitleID'],
'M_PatientPrefix' => $prm['M_PatientPrefix'],
'M_PatientSuffix' => $prm['M_PatientSuffix'],
'M_PatientDOB' => $pdob,
'M_PatientDOB_enc' => $enc->encrypt($dob_str),
'M_PatientDOB_bidx' => $enc->search_bidx($dob_str),
'M_PatientM_SexID' => $prm['M_PatientM_SexID'],
'M_PatientM_ReligionID' => $prm['M_PatientM_ReligionID'],
'M_PatientEmail' => $this->_mask_email($prm['M_PatientEmail']),
'M_PatientEmail_enc' => $enc->encrypt($prm['M_PatientEmail']),
'M_PatientPOB' => $this->_mask_short($prm['M_PatientPOB']),
'M_PatientPOB_enc' => $enc->encrypt($prm['M_PatientPOB']),
'M_PatientHP' => $this->_mask_phone($prm['M_PatientHP']),
'M_PatientHP_enc' => $enc->encrypt($prm['M_PatientHP']),
'M_PatientHP_bidx' => $enc->search_bidx($prm['M_PatientHP']),
'M_PatientPhone' => $this->_mask_phone($prm['M_PatientPhone']),
'M_PatientPhone_enc' => $enc->encrypt($prm['M_PatientPhone']),
'M_PatientM_IdTypeID' => $prm['M_PatientM_IdTypeID'],
'M_PatientIDNumber' => $this->_mask_id($prm['M_PatientIDNumber']),
'M_PatientIDNumber_enc' => $enc->encrypt($prm['M_PatientIDNumber']),
'M_PatientNIK' => $prm['M_PatientNIK'],
'M_PatientNIK_bidx' => $enc->search_bidx($prm['M_PatientNIK'] ?? ''),
'M_PatientNote' => $prm['M_PatientNote'],
'M_PatientJabatan' => $prm['M_PatientJabatan'],
'M_PatientKedudukan' => $prm['M_PatientKedudukan'],
'M_PatientPJ' => $prm['M_PatientPJ'],
'M_PatientLocation' => $prm['M_PatientLocation'],
'M_PatientJob' => $prm['M_PatientJob'],
'M_PatientUserID' => $userid,
];
$this->db_onedev->insert('m_patient', $ptn);
$last_id = $this->db_onedev->insert_id();
$result = array(
"total" => 1 ,
@@ -1502,6 +1566,10 @@ class Preregisterapp extends MY_Controller
$default_tests = $data_prepare['McuOfflinePrepareTests'];
$v['Mcu_PreregisterDetailsPatientName'] = str_replace("'", "\\'", $v['Mcu_PreregisterDetailsPatientName']);
$pdob = date('Y-m-d',strtotime($v['Mcu_PreregisterDetailsDOB']));
$m_nama = $this->db_onedev->escape_str($this->_mask_name($v['Mcu_PreregisterDetailsPatientName']));
$m_nik = $this->_mask_id($v['Mcu_PreregisterDetailsNIK']);
$m_email = $this->_mask_email($v['Mcu_PreregisterDetailsEmail']);
$m_hp = $this->_mask_phone($v['Mcu_PreregisterDetailsHp']);
$query = " INSERT INTO mcu_preregister_patients (
Mcu_PreregisterDetailsMcuOfflinePrepareID,
Mcu_PreregisterDetailsM_PatientID,
@@ -1527,12 +1595,12 @@ class Preregisterapp extends MY_Controller
VALUES(
'{$setup['McuOfflinePrepareID']}',
'{$v['Mcu_PreregisterDetailsM_PatientID']}',
'{$v['Mcu_PreregisterDetailsNIK']}',
'{$m_nik}',
'{$v['Mcu_PreregisterDetailsPatientPrefix']}',
'{$v['Mcu_PreregisterDetailsPatientName']}',
'{$m_nama}',
'{$v['Mcu_PreregisterDetailsPatientSuffix']}',
'{$v['Mcu_PreregisterDetailsEmail']}',
'{$v['Mcu_PreregisterDetailsHp']}',
'{$m_email}',
'{$m_hp}',
'{$pdob}',
'{$v['Mcu_PreregisterDetailsKedudukan']}',
'{$v['Mcu_PreregisterDetailsJabatan']}',

View File

@@ -12,8 +12,26 @@ class Preregisterapp extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
}
private function _mask_name($v) {
if (!$v) return $v;
$v = trim($v);
$words = preg_split('/\s+/', $v);
$out = [];
foreach ($words as $w) {
$l = mb_strlen($w, 'UTF-8');
if ($l <= 2) { $out[] = '***'; continue; }
$out[] = mb_substr($w, 0, 2, 'UTF-8') . str_repeat('*', max(3, $l - 2));
}
return implode(' ', $out);
}
private function _mask_phone($v) { if (!$v) return $v; $d=preg_replace('/[^0-9]/','',trim($v)); $l=strlen($d); if($l<=4) return '****'; if($l<=8) return substr($d,0,4).str_repeat('*',$l-4); return substr($d,0,4).str_repeat('*',$l-7).substr($d,-3); }
private function _mask_email($v) { if (!$v||strpos($v,'@')===false) return $v; [$loc,$dom]=explode('@',$v,2); return mb_substr($loc,0,min(2,mb_strlen($loc,'UTF-8')),'UTF-8').'***@'.$dom; }
private function _mask_id($v) { if (!$v) return $v; $v=trim($v); $l=strlen($v); if($l<=4) return '****'; return substr($v,0,4).str_repeat('*',max(3,$l-6)).($l>6?substr($v,-2):''); }
private function _mask_dob($v) { if (!$v) return $v; $p=explode('-',$v); return (count($p)===3) ? '**-**-'.$p[2] : '****-**-**'; }
public function get_setup_by_id()
{
try {
@@ -270,8 +288,8 @@ class Preregisterapp extends MY_Controller
Mcu_PreregisterPatientsPatientName,
' ',
IFNULL(Mcu_PreregisterPatientsPatientSuffix,'')) as patient_fullname,
DATE_FORMAT(Mcu_PreregisterPatientsDOB,'%d-%m-%Y') as dob,
DATE_FORMAT(Mcu_PreregisterPatientsDOB,'%d-%m-%Y') as Mcu_PreregisterPatientsDOB,
Mcu_PreregisterPatientsDOB as dob,
Mcu_PreregisterPatientsDOB,
IFNULL(M_PatientAddressDescription, '') as M_PatientAddress,
M_PatientAddressCity,
IFNULL(M_PatientAddressCountry, 'ID') as M_PatientAddressCountry,
@@ -440,11 +458,25 @@ class Preregisterapp extends MY_Controller
if ($prm['search'] != '') {
$e = explode('+', $prm['search']);
if (isset($e[0]))
$q['name'] = "AND M_PatientName LIKE '%{$e[0]}%'";
if (isset($e[1]))
$q['dob'] = "AND ((DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$e[1]}%' and M_PatientDOB IS NOT NULL) OR (M_PatientDOB IS NULL AND '{$e[1]}' = ''))";
if (isset($e[2]))
if (isset($e[0]) && $e[0] != '') {
$name_toks = $this->ibl_encryptor->query_tokens($e[0]);
$name_conds = [];
foreach ($name_toks as $tok) {
$tok_esc = $this->db_onedev->escape_str($tok);
$name_conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')";
}
if ($name_conds) $q['name'] = "AND " . implode(' AND ', $name_conds);
}
if (isset($e[1]) && $e[1] != '') {
$dob_toks = $this->ibl_encryptor->query_tokens($e[1]);
$dob_conds = [];
foreach ($dob_toks as $tok) {
$tok_esc = $this->db_onedev->escape_str($tok);
$dob_conds[] = "JSON_CONTAINS(M_PatientDOB_bidx, '\"$tok_esc\"')";
}
if ($dob_conds) $q['dob'] = "AND " . implode(' AND ', $dob_conds);
}
if (isset($e[2]) && $e[2] != '')
$q['nik'] = "AND M_PatientNIP LIKE '%{$e[2]}%'";
}
@@ -453,7 +485,7 @@ class Preregisterapp extends MY_Controller
'N' divider,
concat(IFNULL(M_TitleName,''),' ',IFNULL(M_PatientPrefix,''),' ',M_PatientName,' ',IFNULL(M_PatientSuffix,'')) M_PatientName,
M_PatientName M_PatientRealName, M_TitleID, M_TitleName, M_PatientM_SexID,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as dob_ina,
M_PatientDOB as dob_ina,
IFNULL(M_PatientReligionCode, '-') M_PatientReligionCode,
M_PatientNoReg as Mcu_PreregisterPatientsPID,
M_PatientIdentifierValue as Mcu_PreregisterPatientsKTP,
@@ -601,20 +633,21 @@ class Preregisterapp extends MY_Controller
?
)";
//echo $query;
$m_dob_ptp = $this->_mask_dob(date('d-m-Y', strtotime($pdob)));
$rows = $this->db_onedev->query($query, [
$setup['Mgm_McuID'],
$v['Mcu_PreregisterPatientsPID'],
$v['M_PatientID'],
$v['Mcu_PreregisterPatientsKTP'],
$v['Mcu_PreregisterPatientsKTP'] ? $this->_mask_id($v['Mcu_PreregisterPatientsKTP']) : '',
$v['M_PatientPrefix'],
$v['M_PatientRealName'],
$this->_mask_name($v['M_PatientRealName']),
$v['M_PatientSuffix'],
$v['M_PatientM_SexID'],
$pdob,
$m_dob_ptp,
$v['M_PatientReligionCode'],
$v['Mcu_PreregisterPatientsJob'],
$v['Mcu_PreregisterPatientsEmail'],
$v['Mcu_PreregisterPatientsHp'],
$this->_mask_email($v['Mcu_PreregisterPatientsEmail']),
$this->_mask_phone($v['Mcu_PreregisterPatientsHp']),
$v['Mcu_PreregisterPatientsPosisi'],
$v['Mcu_PreregisterPatientsDivisi'],
$v['Mcu_PreregisterPatientsLocation'],
@@ -660,22 +693,39 @@ class Preregisterapp extends MY_Controller
$IdentifierSystem = 'http://terminology.hl7.org/CodeSystem/v2-0203';
}
$enc_new = $this->ibl_encryptor;
$plain_name_new = $v['Mcu_PreregisterPatientsPatientName'];
$plain_ktp_new = $v['Mcu_PreregisterPatientsKTP'];
$plain_email_new = $v['Mcu_PreregisterPatientsEmail'];
$plain_hp_new = $v['Mcu_PreregisterPatientsHp'];
$dob_str_new = date('d-m-Y', strtotime($pdob));
$sql = "INSERT INTO m_patient (
M_PatientPrefix,
M_PatientName,
M_PatientName_enc,
M_PatientName_bidx,
M_PatientSuffix,
M_PatientM_TitleID,
M_PatientM_SexID,
M_PatientDOB,
M_PatientDOB_enc,
M_PatientDOB_bidx,
M_PatientIdentifierCode,
M_PatientIdentifierSystem,
M_PatientIdentifierValue,
M_PatientIDNumber,
M_PatientIDNumber_enc,
M_PatientNIK_bidx,
M_PatientPosisi,
M_PatientDivisi,
M_PatientLocation,
M_PatientJob,
M_PatientEmail,
M_PatientEmail_enc,
M_PatientHP,
M_PatientHP_enc,
M_PatientHP_bidx,
M_PatientCreatedUserID,
M_PatientNIP,
M_PatientDepartement,
@@ -683,31 +733,39 @@ class Preregisterapp extends MY_Controller
M_PatientCreated,
M_PatientRegisteredByCompanyID
)
VALUES(
'{$v["Mcu_PreregisterPatientsPatientPrefix"]}',
'{$nameNewPn}',
'{$v["Mcu_PreregisterPatientsPatientSuffix"]}',
'{$title_id}',
'{$v["M_PatientM_SexID"]}',
'{$pdob}',
'{$typeIdentifier}',
'{$IdentifierSystem}',
'{$v["Mcu_PreregisterPatientsKTP"]}',
'{$v['Mcu_PreregisterPatientsPosisi']}',
'{$v['Mcu_PreregisterPatientsDivisi']}',
'{$v['Mcu_PreregisterPatientsLocation']}',
'{$v['Mcu_PreregisterPatientsJob']}',
'{$v['Mcu_PreregisterPatientsEmail']}',
'{$v['Mcu_PreregisterPatientsHp']}',
'{$userid}',
'{$v['Mcu_PreregisterPatientsNIK']}',
'{$v['Mcu_PreregisterPatientsDepartment']}',
'{$number}',
NOW(),
'{$setup['Mgm_McuM_CompanyID']}'
)";
//echo $sql;
$rows = $this->db_onedev->query($sql);
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),?)";
$rows = $this->db_onedev->query($sql, [
$v['Mcu_PreregisterPatientsPatientPrefix'],
$this->_mask_name($plain_name_new),
$enc_new->encrypt($plain_name_new),
$enc_new->search_bidx($plain_name_new),
$v['Mcu_PreregisterPatientsPatientSuffix'],
$title_id,
$v['M_PatientM_SexID'],
$this->_mask_dob($dob_str_new),
$enc_new->encrypt($dob_str_new),
$enc_new->search_bidx($dob_str_new),
$typeIdentifier,
$IdentifierSystem,
$plain_ktp_new ? $this->_mask_id($plain_ktp_new) : '',
$plain_ktp_new ? $this->_mask_id($plain_ktp_new) : null,
$plain_ktp_new ? $enc_new->encrypt($plain_ktp_new) : null,
$enc_new->search_bidx($plain_ktp_new ?? ''),
$v['Mcu_PreregisterPatientsPosisi'],
$v['Mcu_PreregisterPatientsDivisi'],
$v['Mcu_PreregisterPatientsLocation'],
$v['Mcu_PreregisterPatientsJob'],
$plain_email_new ? $this->_mask_email($plain_email_new) : '',
$plain_email_new ? $enc_new->encrypt($plain_email_new) : null,
$plain_hp_new ? $this->_mask_phone($plain_hp_new) : '',
$plain_hp_new ? $enc_new->encrypt($plain_hp_new) : null,
$enc_new->search_bidx($plain_hp_new ?? ''),
$userid,
$v['Mcu_PreregisterPatientsNIK'],
$v['Mcu_PreregisterPatientsDepartment'],
$number,
$setup['Mgm_McuM_CompanyID']
]);
if (!$rows) {
$message = $this->db_onedev->error();
$message['qry'] = $this->db_onedev->last_query();
@@ -1039,7 +1097,7 @@ class Preregisterapp extends MY_Controller
}
$pdob = date('Y-m-d', strtotime($v['Mcu_PreregisterPatientsDOB']));
$pdob_input = $v['Mcu_PreregisterPatientsDOB'];
$Mcu_PreregisterPatientsTests = '';
$packettests = array();
@@ -1069,9 +1127,23 @@ class Preregisterapp extends MY_Controller
}
$dataPatientBefore = $rows->row_array();
$enc_upd = $this->ibl_encryptor;
$pdob_ts = strtotime($pdob_input);
if ($pdob_ts && $pdob_ts > 0 && strpos($pdob_input, '*') === false) {
$dob_str_upd = date('d-m-Y', $pdob_ts);
$pdob = date('Y-m-d', $pdob_ts);
} else {
$dob_str_upd = $enc_upd->decrypt($dataPatientBefore['M_PatientDOB_enc'] ?? '') ?: '';
$pdob = $dob_str_upd ? date('Y-m-d', strtotime($dob_str_upd)) : '';
}
$plain_name_upd = $v['Mcu_PreregisterPatientsPatientName'];
$plain_ktp_upd = $v['Mcu_PreregisterPatientsKTP'];
$plain_email_upd = $v['Mcu_PreregisterPatientsEmail'];
$plain_hp_upd = $v['Mcu_PreregisterPatientsHp'];
$this->db_onedev->trans_begin();
$query = " UPDATE mcu_preregister_patients SET
$query = " UPDATE mcu_preregister_patients SET
Mcu_PreregisterPatientsM_PatientID = ?,
Mcu_PreregisterPatientsKTP = ?,
Mcu_PreregisterPatientsNIP = ?,
@@ -1099,14 +1171,14 @@ class Preregisterapp extends MY_Controller
//echo $query;
$rows = $this->db_onedev->query($query, [
$v['Mcu_PreregisterPatientsM_PatientID'],
$v['Mcu_PreregisterPatientsKTP'],
$plain_ktp_upd ? $this->_mask_id($plain_ktp_upd) : '',
$v['Mcu_PreregisterPatientsNIP'],
$v['Mcu_PreregisterPatientsPatientPrefix'],
$v['Mcu_PreregisterPatientsPatientName'],
$this->_mask_name($plain_name_upd),
$v['Mcu_PreregisterPatientsPatientSuffix'],
$v['Mcu_PreregisterPatientsEmail'],
$v['Mcu_PreregisterPatientsHp'],
$pdob,
$plain_email_upd ? $this->_mask_email($plain_email_upd) : '',
$plain_hp_upd ? $this->_mask_phone($plain_hp_upd) : '',
$this->_mask_dob($dob_str_upd),
$v['Mcu_PreregisterPatientsPosisi'],
$v['Mcu_PreregisterPatientsDivisi'],
$v['Mcu_PreregisterPatientsJob'],
@@ -1129,24 +1201,30 @@ class Preregisterapp extends MY_Controller
exit;
}
$sql_ktp = '';
if (isset($v['Mcu_PreregisterPatientsKTP']) && $v['Mcu_PreregisterPatientsKTP'] != '') {
$sql_ktp = "M_PatientIdentifierCode = 'NNIDN', M_PatientIdentifierSystem='http://terminology.hl7.org/CodeSystem/v2-0203', M_PatientIdentifierValue = '{$v['Mcu_PreregisterPatientsKTP']}',";
//echo $sql_ktp;
} else {
$sql_ktp = "M_PatientIdentifierCode = '', M_PatientIdentifierSystem='', M_PatientIdentifierValue = '',";
}
$ktp_mask_upd = $plain_ktp_upd ? $this->_mask_id($plain_ktp_upd) : '';
$sql = "UPDATE m_patient SET
$sql_ktp
M_PatientIdentifierCode = ?,
M_PatientIdentifierSystem = ?,
M_PatientIdentifierValue = ?,
M_PatientIDNumber = ?,
M_PatientIDNumber_enc = ?,
M_PatientNIK_bidx = ?,
M_PatientDOB = ?,
M_PatientDOB_enc = ?,
M_PatientDOB_bidx = ?,
M_PatientM_TitleID = ?,
M_PatientNIP = ?,
M_PatientM_SexID = ?,
M_PatientPrefix = ?,
M_PatientName = ?,
M_PatientName_enc = ?,
M_PatientName_bidx = ?,
M_PatientSuffix = ?,
M_PatientEmail = ?,
M_PatientEmail_enc = ?,
M_PatientHP = ?,
M_PatientHP_enc = ?,
M_PatientHP_bidx = ?,
M_PatientDivisi = ?,
M_PatientPosisi = ?,
M_PatientLocation = ?,
@@ -1159,17 +1237,29 @@ class Preregisterapp extends MY_Controller
WHERE
M_PatientID = ?
";
//echo $sql;
$qry = $this->db_onedev->query($sql, [
$pdob,
$plain_ktp_upd ? 'NNIDN' : '',
$plain_ktp_upd ? 'http://terminology.hl7.org/CodeSystem/v2-0203' : '',
$ktp_mask_upd,
$ktp_mask_upd,
$plain_ktp_upd ? $enc_upd->encrypt($plain_ktp_upd) : null,
$enc_upd->search_bidx($plain_ktp_upd ?? ''),
$this->_mask_dob($dob_str_upd),
$enc_upd->encrypt($dob_str_upd),
$enc_upd->search_bidx($dob_str_upd),
$v['Mcu_PreregisterPatientsM_TitleID'],
$v['Mcu_PreregisterPatientsNIP'],
$v['Mcu_PreregisterPatientsM_SexID'],
$v['Mcu_PreregisterPatientsPatientPrefix'],
$v['Mcu_PreregisterPatientsPatientName'],
$this->_mask_name($plain_name_upd),
$enc_upd->encrypt($plain_name_upd),
$enc_upd->search_bidx($plain_name_upd),
$v['Mcu_PreregisterPatientsPatientSuffix'],
$v['Mcu_PreregisterPatientsEmail'],
$v['Mcu_PreregisterPatientsHp'],
$plain_email_upd ? $this->_mask_email($plain_email_upd) : '',
$plain_email_upd ? $enc_upd->encrypt($plain_email_upd) : null,
$plain_hp_upd ? $this->_mask_phone($plain_hp_upd) : '',
$plain_hp_upd ? $enc_upd->encrypt($plain_hp_upd) : null,
$enc_upd->search_bidx($plain_hp_upd ?? ''),
$v['Mcu_PreregisterPatientsDivisi'],
$v['Mcu_PreregisterPatientsPosisi'],
$v['Mcu_PreregisterPatientsLocation'],
@@ -1725,16 +1815,41 @@ class Preregisterapp extends MY_Controller
$add_where .= " AND M_PatientNIP = '{$nik}'";
}
if ($ktp != '') {
$add_where .= " AND M_PatientIdentifierValue = '{$nik}' AND M_PatientIdentifierCode = 'NNIDN'";
$ktp_toks = $this->ibl_encryptor->query_tokens($ktp);
foreach ($ktp_toks as $ktok) {
$ktok_esc = $this->db_onedev->escape_str($ktok);
$add_where .= " AND JSON_CONTAINS(M_PatientNIK_bidx, '\"$ktok_esc\"')";
}
}
$setup = $prm['setup'];
$join_company = "";
if (isset($prm['company']) && intval($prm['company']) > 0) {
$join_company = "JOIN t_orderheader ON T_OrderHeaderM_PatientID = M_PatientID AND
T_OrderHeaderIsActive = 'Y' AND
$join_company = "JOIN t_orderheader ON T_OrderHeaderM_PatientID = M_PatientID AND
T_OrderHeaderIsActive = 'Y' AND
T_OrderHeaderM_CompanyID = {$prm['company']}";
}
$name_where = '1=1';
if ($name != '') {
$name_toks = $this->ibl_encryptor->query_tokens($name);
$nconds = [];
foreach ($name_toks as $ntok) {
$ntok_esc = $this->db_onedev->escape_str($ntok);
$nconds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$ntok_esc\"')";
}
if ($nconds) $name_where = implode(' AND ', $nconds);
}
$dob_where = '1=1';
if ($dob != '') {
$dob_toks = $this->ibl_encryptor->query_tokens($dob);
$dconds = [];
foreach ($dob_toks as $dtok) {
$dtok_esc = $this->db_onedev->escape_str($dtok);
$dconds[] = "JSON_CONTAINS(M_PatientDOB_bidx, '\"$dtok_esc\"')";
}
if ($dconds) $dob_where = implode(' AND ', $dconds);
}
$sql = "SELECT COUNT(*) as total
FROM (
SELECT *
@@ -1742,16 +1857,15 @@ class Preregisterapp extends MY_Controller
$join_company
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
WHERE
M_PatientIsActive = 'Y'
M_PatientIsActive = 'Y'
AND M_PatientRegisteredByCompanyID = {$setup['Mgm_McuM_CompanyID']}
AND M_PatientName LIKE CONCAT('%',?,'%') AND
(DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$dob}%' and M_PatientDOB IS NOT NULL)
AND ({$name_where})
AND ({$dob_where})
{$add_where}
GROUP BY M_PatientID
) x
";
//echo $sql;
$qry = $this->db_onedev->query($sql, [$name]);
$qry = $this->db_onedev->query($sql);
if (!$qry) {
$message = $this->db_onedev->error();
$message['qry'] = $this->db_onedev->last_query();
@@ -1759,7 +1873,7 @@ class Preregisterapp extends MY_Controller
exit;
}
$countx = $qry->row()->total;
$sql = "SELECT *, DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') as dob_ina,
$sql = "SELECT *, M_PatientDOB as dob_ina,
M_PatientNoReg as Mcu_PreregisterPatientsPID,
M_PatientIdentifierValue as Mcu_PreregisterPatientsKTP,
M_PatientID as Mcu_PreregisterPatientsM_PatientID,
@@ -1781,16 +1895,15 @@ class Preregisterapp extends MY_Controller
LEFT join m_title on M_PatientM_TitleID = M_TitleID
WHERE
M_PatientRegisteredByCompanyID = {$setup['Mgm_McuM_CompanyID']}
AND M_PatientIsActive = 'Y' AND
M_PatientName LIKE CONCAT('%',?,'%') AND
(DATE_FORMAT(M_PatientDOB, '%d-%m-%Y') LIKE '%{$dob}%' and M_PatientDOB IS NOT NULL)
AND M_PatientIsActive = 'Y'
AND ({$name_where})
AND ({$dob_where})
{$add_where}
GROUP BY M_PatientID
LIMIT 10 OFFSET 0
";
//echo $sql;
$qry = $this->db_onedev->query($sql, [$name]);
$qry = $this->db_onedev->query($sql);
if (!$qry) {
$message = $this->db_onedev->error();
$message['qry'] = $this->db_onedev->last_query();

View File

@@ -69,13 +69,8 @@ class Re_px extends MY_Controller
return array(0,"");
}
function get_normal_value($methodeID, $natTestID, $sexID, $ageInDay ) {
$sql = "select fn_sampling_get_normal($methodeID, $natTestID, $sexID, $ageInDay) as normalValueID";
$qry = $this->db->query($sql);
if ($qry) {
$rows = $qry->result_array();
if (count($rows) > 0 ) return $rows[0]["normalValueID"];
}
return 0;
$this->load->library('ibl_sampling_normal');
return $this->ibl_sampling_normal->get_normal_value_id($methodeID, $natTestID, $sexID, $ageInDay);
}
function fn_fix_normal($order_id) {

View File

@@ -69,13 +69,8 @@ class Re_px extends MY_Controller
return array(0,"");
}
function get_normal_value($methodeID, $natTestID, $sexID, $ageInDay ) {
$sql = "select fn_sampling_get_normal($methodeID, $natTestID, $sexID, $ageInDay) as normalValueID";
$qry = $this->db->query($sql);
if ($qry) {
$rows = $qry->result_array();
if (count($rows) > 0 ) return $rows[0]["normalValueID"];
}
return 0;
$this->load->library('ibl_sampling_normal');
return $this->ibl_sampling_normal->get_normal_value_id($methodeID, $natTestID, $sexID, $ageInDay);
}
function fn_fix_normal($order_id) {
$sql = "select

View File

@@ -69,13 +69,8 @@ class Re_px extends MY_Controller
return array(0,"");
}
function get_normal_value($methodeID, $natTestID, $sexID, $ageInDay ) {
$sql = "select fn_sampling_get_normal($methodeID, $natTestID, $sexID, $ageInDay) as normalValueID";
$qry = $this->db->query($sql);
if ($qry) {
$rows = $qry->result_array();
if (count($rows) > 0 ) return $rows[0]["normalValueID"];
}
return 0;
$this->load->library('ibl_sampling_normal');
return $this->ibl_sampling_normal->get_normal_value_id($methodeID, $natTestID, $sexID, $ageInDay);
}
function fn_fix_normal($order_id) {
$sql = "select

View File

@@ -440,59 +440,63 @@ class Rv_patient extends MY_Controller
foreach ($rows['groups'] as $k => $v) {
$ready_print = $v['ready_print'];
$gn = $v['group_name'];
// MAPPING GROUP NAME KE REPORT CODE
$report_codes = $this->get_report_codes_by_group($gn);
if ($report_codes) {
// Gunakan database untuk ambil report name
$rpt = $this->get_report_name_from_db($report_codes['P'], $ready_print);
$e_rpt = $this->get_report_name_from_db($report_codes['E'], $ready_print);
$rptqrcode = isset($report_codes['Q']) ? $this->get_report_name_from_db($report_codes['Q'], $ready_print) : '';
// Jika QR tidak ada, gunakan P
if (empty($rptqrcode)) {
$rptqrcode = $rpt;
}
$rpt_code = $ready_print == 'Y' ? $report_codes['P'] : ($report_codes['NP'] ?? $report_codes['P']);
$e_rpt_code = $ready_print == 'Y' ? ($report_codes['E'] ?? $report_codes['P']) : ($report_codes['NE'] ?? $report_codes['NP'] ?? $report_codes['E'] ?? $report_codes['P']);
$rptq_code = $ready_print == 'Y' ? ($report_codes['Q'] ?? $report_codes['P']) : ($report_codes['NQ'] ?? $report_codes['NP'] ?? $report_codes['P']);
$rpt = $this->get_report_name_from_code($rpt_code);
$e_rpt = $this->get_report_name_from_code($e_rpt_code);
$rptqrcode = $this->get_report_name_from_code($rptq_code);
if (empty($rptqrcode)) $rptqrcode = $rpt;
} else {
// FALLBACK untuk group yang belum ada di database
$rpt = $this->get_fallback_report($gn, 'P', $ready_print);
$e_rpt = $this->get_fallback_report($gn, 'E', $ready_print);
$rpt_code = $e_rpt_code = '';
$rpt = $this->get_fallback_report($gn, 'P', $ready_print);
$e_rpt = $this->get_fallback_report($gn, 'E', $ready_print);
$rptqrcode = $this->get_fallback_report($gn, 'Q', $ready_print);
if (empty($rptqrcode)) $rptqrcode = $rpt;
}
// LOGIC KHUSUS MIKRO
if ($gn == 'Mikro') {
$sql = "SELECT T_OrderDetailResult as result
FROM t_orderdetail
$sql = "SELECT T_OrderDetailResult as result
FROM t_orderdetail
JOIN t_test ON T_OrderDetailT_TestID = T_TestID AND T_TestIsResult = 'Y'
JOIN group_resultdetail ON Group_ResultDetailT_TestID = T_OrderDetailT_TestID AND Group_ResultDetailIsActive = 'Y'
JOIN group_result ON Group_ResultDetailGroup_ResultID = Group_ResultID AND Group_ResultFlagNonLab = 'N' AND
JOIN group_result ON Group_ResultDetailGroup_ResultID = Group_ResultID AND Group_ResultFlagNonLab = 'N' AND
Group_ResultName = 'Mikro'
WHERE T_OrderDetailID = {$v['id']} AND T_OrderDetailIsActive = 'Y' LIMIT 1";
$xresult = $this->db_smartone->query($sql)->row()->result;
if ($xresult != 'Terlampir') {
$rpt = $this->get_report_name_from_db('LAB-RESULT-P-01', $ready_print);
$e_rpt = $this->get_report_name_from_db('LAB-RESULT-E-01', $ready_print);
$rpt_code = $ready_print == 'Y' ? 'LAB-RESULT-P-01' : 'LAB-RESULT-NP-01';
$e_rpt_code = $ready_print == 'Y' ? 'LAB-RESULT-P-02' : 'LAB-RESULT-NP-02';
$rpt = $this->get_report_name_from_code($rpt_code);
$e_rpt = $this->get_report_name_from_code($e_rpt_code);
}
if ($xresult == 'Terlampir' && $exist_lab == 'N') {
$rows['groups'][$k]['group_name'] = 'Lampiran Mikro';
$new_group = $rows['groups'][$k];
$new_group['group_name'] = 'Mikro';
$new_group['rpt'] = $this->get_report_name_from_db('LAB-RESULT-P-01', 'Y');
$new_group['e_rpt'] = $this->get_report_name_from_db('LAB-RESULT-E-01', 'Y');
$new_group['rptqrcode'] = $new_group['rpt'];
$new_group['rpt_code'] = 'LAB-RESULT-P-01';
$new_group['e_rpt_code'] = 'LAB-RESULT-P-02';
$new_group['rpt'] = $this->get_report_name_from_code('LAB-RESULT-P-01');
$new_group['e_rpt'] = $this->get_report_name_from_code('LAB-RESULT-P-02');
$new_group['rptqrcode'] = $new_group['rpt'];
array_push($rows['groups'], $new_group);
}
}
$rows['groups'][$k]['rpt'] = $rpt;
$rows['groups'][$k]['e_rpt'] = $e_rpt;
$rows['groups'][$k]['rptqrcode'] = $rptqrcode;
$rows['groups'][$k]['rpt'] = $rpt;
$rows['groups'][$k]['e_rpt'] = $e_rpt;
$rows['groups'][$k]['rptqrcode'] = $rptqrcode;
$rows['groups'][$k]['rpt_code'] = $rpt_code;
$rows['groups'][$k]['e_rpt_code'] = $e_rpt_code;
}
$rows['groups'][0]['selected'] = 'Y';
@@ -529,43 +533,55 @@ class Rv_patient extends MY_Controller
}
private function get_report_codes_by_group($group_name)
{
$mapping = [
'LAB' => ['P' => 'LAB-RESULT-P-01', 'E' => 'LAB-RESULT-E-01', 'Q' => 'LAB-RESULT-Q-01'],
'LAB_EN' => ['P' => 'LAB-RESULT-EN-P-01', 'E' => 'LAB-RESULT-EN-E-01'],
'Mikro_EN' => ['P' => 'LAB-RESULT-EN-P-01', 'E' => 'LAB-RESULT-EN-E-01'],
'FNA' => ['P' => 'FNA-P-01', 'E' => 'FNA-E-01'],
'Patologi Anatomi' => ['P' => 'PA-P-01', 'E' => 'PA-E-01'],
'Papsmear' => ['P' => 'PAP-P-01', 'E' => 'PAP-E-01'],
'Pap Smear (Liquid C Prep)' => ['P' => 'LCP-P-01', 'E' => 'LCP-E-01', 'Q' => 'LCP-Q-01'],
'Pap Smear (Liquid C Prep)_EN' => ['P' => 'LCP-P-01', 'E' => 'LCP-E-01', 'Q' => 'LCP-Q-01'],
'Papsmear_EN' => ['P' => 'LCP-P-01', 'E' => 'LCP-E-01', 'Q' => 'LCP-Q-01']
];
return isset($mapping[$group_name]) ? $mapping[$group_name] : null;
}
{
// P = siap cetak, NP = belum siap cetak, E = email siap, NE = email belum siap
$mapping = [
'LAB' => ['P' => 'LAB-RESULT-P-01', 'NP' => 'LAB-RESULT-NP-01', 'E' => 'LAB-RESULT-P-02', 'NE' => 'LAB-RESULT-NP-02'],
'LAB_EN' => ['P' => 'LABEN-RESULT-P-01', 'NP' => 'LABEN-RESULT-NP-01', 'E' => 'LABEN-RESULT-P-02'],
'Mikro' => ['P' => 'MIKRO-RESULT-P-01', 'NP' => 'MIKRO-RESULT-NP-01', 'E' => 'MIKRO-RESULT-P-02'],
'Mikro_EN' => ['P' => 'MIKROEN-RESULT-P-01', 'NP' => 'MIKROEN-RESULT-NP-01', 'E' => 'MIKROEN-RESULT-P-02'],
'FNA' => ['P' => 'FNA-RESULT-P-01', 'NP' => 'FNA-RESULT-NP-01', 'E' => 'FNA-RESULT-P-02'],
'Patologi Anatomi' => ['P' => 'PA-RESULT-P-01', 'NP' => 'PA-RESULT-NP-01', 'E' => 'PA-RESULT-P-02'],
'Papsmear' => ['P' => 'PAP-RESULT-P-01', 'NP' => 'PAP-RESULT-NP-01', 'E' => 'PAP-RESULT-P-02'],
'Pap Smear (Liquid C Prep)' => ['P' => 'PAPLCP-RESULT-P-01', 'NP' => 'PAPLCP-RESULT-NP-01', 'E' => 'PAPLCP-RESULT-P-02'],
'Pap Smear (Liquid C Prep)_EN' => ['P' => 'PAPLEN-RESULT-P-01', 'NP' => 'PAPLEN-RESULT-NP-01', 'E' => 'PAPLEN-RESULT-P-02'],
'Papsmear_EN' => ['P' => 'PAPLEN-RESULT-P-01', 'NP' => 'PAPLEN-RESULT-NP-01', 'E' => 'PAPLEN-RESULT-P-02'],
'Preparasi Sperma' => ['P' => 'PS-RESULT-P-01', 'NP' => 'PS-RESULT-NP-01', 'E' => 'PS-RESULT-P-02'],
'dfi' => ['P' => 'DFI-RESULT-P-01', 'NP' => 'DFI-RESULT-NP-01', 'E' => 'DFI-RESULT-P-02'],
'DFI' => ['P' => 'DFI-RESULT-P-01', 'NP' => 'DFI-RESULT-NP-01', 'E' => 'DFI-RESULT-P-02'],
'Cytologi' => ['P' => 'CT-RESULT-P-01', 'NP' => 'CT-RESULT-NP-01', 'E' => 'CT-RESULT-P-02'],
];
private function get_report_name_from_db($report_code, $ready_print = 'Y')
{
if (empty($report_code)) return '';
$sql = "SELECT Print_TransactionUrl FROM print_transaction WHERE Print_TransactionCode = ? LIMIT 1";
$row = $this->db_smartone->query($sql, [$report_code])->row();
if (!$row) return '';
// Extract report name from URL
$url = $row->Print_TransactionUrl;
preg_match('/\/([^\/]+)\.rptdesign/', $url, $matches);
$report_name = isset($matches[1]) ? $matches[1] : '';
// Tambahkan suffix jika belum ready print
if ($ready_print != 'Y' && !empty($report_name)) {
$report_name .= '_not_print';
}
return $report_name;
}
return $mapping[$group_name] ?? null;
}
private function get_report_name_from_code($report_code)
{
if (empty($report_code)) return '';
$row = $this->db_smartone->query(
"SELECT Print_TransactionUrl FROM print_transaction WHERE Print_TransactionCode = ? LIMIT 1",
[$report_code]
)->row();
if (!$row) return '';
preg_match('/\/([^\/]+)\.rptdesign/', $row->Print_TransactionUrl, $matches);
return $matches[1] ?? '';
}
private function get_report_name_from_db($report_code, $ready_print = 'Y')
{
if (empty($report_code)) return '';
$row = $this->db_smartone->query(
"SELECT Print_TransactionUrl FROM print_transaction WHERE Print_TransactionCode = ? LIMIT 1",
[$report_code]
)->row();
if (!$row) return '';
preg_match('/\/([^\/]+)\.rptdesign/', $row->Print_TransactionUrl, $matches);
$report_name = $matches[1] ?? '';
if ($ready_print != 'Y' && !empty($report_name)) {
$report_name .= '_not_print';
}
return $report_name;
}
private function get_fallback_report($group_name, $type, $ready_print)
{
@@ -790,6 +806,10 @@ private function get_fallback_report($group_name, $type, $ready_print)
$url = "/birt/{$mode}?__report=report/onelab/{$folder}/{$rpt_name}.rptdesign&__format=pdf&username={$username}&PID={$order_id}&tm={$tm}";
}
// Populate decrypt cache sebelum frontend buka URL ke BIRT
$this->load->library('ibl_patient_decrypt');
$this->ibl_patient_decrypt->pre_cache_and_get_url($url);
$this->sys_ok($url);
}
@@ -871,6 +891,17 @@ private function get_fallback_report($group_name, $type, $ready_print)
$final_url = str_replace($key, $value, $final_url);
}
// Populate patient_print_cache agar sp_rpt_hasil_header baca DOB/name dari cache
$order_id = intval($params['PT_OrderHeaderID'] ?? 0);
if ($order_id <= 0) {
parse_str(parse_url($final_url, PHP_URL_QUERY) ?? '', $_url_params);
$order_id = intval($_url_params['PID'] ?? 0);
}
if ($order_id > 0) {
$this->load->library('ibl_patient_decrypt');
$this->ibl_patient_decrypt->populate_cache_by_order($order_id);
}
$this->sys_ok([
'status' => true,
'url' => $final_url,

View File

@@ -99,17 +99,17 @@ class Rv_patient extends MY_Controller
);
exit;
}
$sql = "INSERT INTO mcu_resume_results(
Mcu_ResumeResultsType,
Mcu_ResumeResultsT_OrderHeaderID,
Mcu_ResumeResultsGroupResultID,
Mcu_ResumeResultsT_TestID,
Mcu_ResumeResultsName,
Mcu_ResumeResultsJSON,
Mcu_ResumeResultsCreated,
Mcu_ResumeResultsUserID)
VALUES('KHUSUS',?,?,?,?,?,NOW(),?)";
$qry = $this->db_smartone->query($sql, array($order_id, $groupResultID, 0, $groupResultName, $j_data_result_lab, $userID));
$sql = "INSERT INTO mcu_resume_results(
Mcu_ResumeResultsType,
Mcu_ResumeResultsT_OrderHeaderID,
Mcu_ResumeResultsGroupResultID,
Mcu_ResumeResultsT_TestID,
Mcu_ResumeResultsName,
Mcu_ResumeResultsJSON,
Mcu_ResumeResultsCreated,
Mcu_ResumeResultsUserID)
VALUES('KHUSUS',?,?,?,?,?,NOW(),?)";
$qry = $this->db_smartone->query($sql, array($order_id, $groupResultID, 0, $groupResultName, $j_data_result_lab, $userID));
if (!$qry) {
echo json_encode(
array("status" => "ERR", "message" => "Error: insert mcu results")
@@ -183,7 +183,8 @@ class Rv_patient extends MY_Controller
$sql_get = "SELECT T_OrderHeaderGroupResultID,
T_OrderHeaderGroupResultGroup_ResultID,
T_OrderHeaderGroupResultT_TestID,
T_OrderHeaderGroupResultGroup_ResultName
T_OrderHeaderGroupResultGroup_ResultName,
Group_ResultName
FROM t_orderheader_group_result
JOIN group_result ON T_OrderHeaderGroupResultGroup_ResultID = Group_ResultID
AND Group_ResultFlagNonLab = 'N'
@@ -196,25 +197,12 @@ class Rv_patient extends MY_Controller
}
$data = $qry_get->result_array();
$sql_url = "SELECT Print_TransactionID,
Print_TransactionType,
Print_TransactionCode,
Print_TransactionName,
Print_TransactionUrl,
Print_TransactionUrlWatermark,
Print_TransactionUrlEletronic,
Print_TransactionParams
FROM print_transaction
WHERE Print_TransactionCode = 'LAB-RESULT-P-01'
LIMIT 1";
$qry_url = $this->db_smartone->query($sql_url);
if (!$qry_url) {
$this->sys_error_db('error print_transaction', $this->db_smartone);
exit;
}
$rst_url = $qry_url->row_array();
$template_url = $rst_url['Print_TransactionUrlWatermark'];
$template_url_electronic = isset($rst_url['Print_TransactionUrlEletronic']) ? $rst_url['Print_TransactionUrlEletronic'] : '';
// mapping test kode print
$map = [
'LAB' => 'LAB-RESULT-P-01',
'Papsmear' => 'PAP-P-01',
'FNA' => 'FNA-P-01'
];
$sql_branch = "SELECT M_BranchID,
M_BranchCode,
@@ -228,42 +216,8 @@ class Rv_patient extends MY_Controller
$this->sys_error_db('error m_branch', $this->db_smartone);
exit;
}
$branch = $qry_branch->row_array();
$base = "http://localhost/";
// Tambah base_url di depan
$full_url = $base . ltrim($template_url, '/');
// Ganti nama report jadi _watermark
$full_url = str_replace(
'rpt_test.rptdesign',
'rpt_test_watermark.rptdesign',
$full_url
);
// Ganti parameter
$full_url = str_replace(
['PUsername', 'PT_OrderHeaderID', 'TS'],
[
trim($this->sys_user['M_UserUsername']),
$order_id,
time()
],
$full_url
);
$full_url_electronic = '';
if ($template_url_electronic !== '') {
$full_url_electronic = $base . ltrim($template_url_electronic, '/');
$full_url_electronic = str_replace(
['PUsername', 'PT_OrderHeaderID', 'TS'],
[
trim($this->sys_user['M_UserUsername']),
$order_id,
time()
],
$full_url_electronic
);
}
$branch = $qry_branch->row_array();
$base = "http://localhost/";
// ambil endpoint URL dari DB
$sql_url = "SELECT QR_ReportEndpointID,
@@ -288,6 +242,65 @@ class Rv_patient extends MY_Controller
foreach ($data as $key => $value) {
$group_result_name = $value['Group_ResultName'];
// kalau tidak ada di mapping skip
if (!isset($map[$group_result_name])) {
continue;
}
$printCode = $map[$group_result_name];
// ambil URL print
$sql_url = "SELECT Print_TransactionUrlWatermark, Print_TransactionUrlEletronic
FROM print_transaction
WHERE Print_TransactionCode = ?
LIMIT 1";
$qry_url = $this->db_onedev->query($sql_url, [$printCode]);
if (!$qry_url) {
$this->sys_error_db('error print_transaction', $this->db_onedev);
exit;
}
$rst_url = $qry_url->row_array();
$template_url = $rst_url['Print_TransactionUrlWatermark'];
$template_url_electronic = isset($rst_url['Print_TransactionUrlEletronic']) ? $rst_url['Print_TransactionUrlEletronic'] : '';
// Tambah base_url di depan
$full_url = $base . ltrim($template_url, '/');
// ubah semua report jadi versi watermark
$full_url = str_replace(
'.rptdesign',
'_watermark.rptdesign',
$full_url
);
// Ganti parameter
$full_url = str_replace(
['PUsername', 'PT_OrderHeaderID', 'TS'],
[
trim($this->sys_user['M_UserUsername']),
$order_id,
time()
],
$full_url
);
$full_url_electronic = '';
if ($template_url_electronic !== '') {
$full_url_electronic = $base . ltrim($template_url_electronic, '/');
$full_url_electronic = str_replace(
['PUsername', 'PT_OrderHeaderID', 'TS'],
[
trim($this->sys_user['M_UserUsername']),
$order_id,
time()
],
$full_url_electronic
);
}
// cek sudah ada atau belum
$sql_check = "SELECT QR_PrintOutID, QR_PrintOutUploadStatus
FROM qr_printout
@@ -308,19 +321,19 @@ class Rv_patient extends MY_Controller
$params = [
'orderHeaderID' => $order_id,
'groupResultID' => $value['T_OrderHeaderGroupResultGroup_ResultID'],
'testID' => $value['T_OrderHeaderGroupResultT_TestID'],
'groupResultName' => $value['T_OrderHeaderGroupResultGroup_ResultName'],
'verifyBaseURL' => $verify_url,
'QR_PrintOutReportURL' => $full_url,
'QR_PrintOutReportURLElectronic' => $full_url_electronic,
'createdByUserID' => $userID
];
'testID' => $value['T_OrderHeaderGroupResultT_TestID'],
'groupResultName' => $value['T_OrderHeaderGroupResultGroup_ResultName'],
'verifyBaseURL' => $verify_url,
'QR_PrintOutReportURL' => $full_url,
'QR_PrintOutReportURLElectronic' => $full_url_electronic,
'createdByUserID' => $userID
];
$this->generateqrreport->saveQRPrintout($params);
}
}
echo $this->sys_ok(array("status" => "OK"));
$this->sys_ok(array("status" => "OK"));
}
/**
@@ -369,17 +382,17 @@ class Rv_patient extends MY_Controller
// Insert new record
$j_data_result = json_encode($rows_result);
$sql = "INSERT INTO mcu_resume_results(
Mcu_ResumeResultsType,
Mcu_ResumeResultsT_OrderHeaderID,
Mcu_ResumeResultsGroupResultID,
Mcu_ResumeResultsT_TestID,
Mcu_ResumeResultsName,
Mcu_ResumeResultsJSON,
Mcu_ResumeResultsCreated,
Mcu_ResumeResultsUserID)
VALUES('KHUSUS',?,?,?,?,?,NOW(),?)";
$qry = $this->db_smartone->query($sql, array($order_id, $groupResultID, 0, $groupResultName, $j_data_result, $userID));
$sql = "INSERT INTO mcu_resume_results(
Mcu_ResumeResultsType,
Mcu_ResumeResultsT_OrderHeaderID,
Mcu_ResumeResultsGroupResultID,
Mcu_ResumeResultsT_TestID,
Mcu_ResumeResultsName,
Mcu_ResumeResultsJSON,
Mcu_ResumeResultsCreated,
Mcu_ResumeResultsUserID)
VALUES('KHUSUS',?,?,?,?,?,NOW(),?)";
$qry = $this->db_smartone->query($sql, array($order_id, $groupResultID, 0, $groupResultName, $j_data_result, $userID));
if (!$qry) {
echo json_encode(
array("status" => "ERR", "message" => "Error: insert into mcu result 2")

View File

@@ -12,6 +12,7 @@ class Patient extends MY_Controller
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('Nonlabtemplate');
$this->load->library('ibl_encryptor');
$this->IP_SOCKET_IO = "127.0.0.1";
}
@@ -127,16 +128,16 @@ class Patient extends MY_Controller
SELECT DATE_FORMAT(T_OrderHeaderDate,'%d-%m-%Y %H:%i') as order_date,
T_OrderHeaderLabNumber as labnumber,
T_OrderHeaderM_PatientAge as patient_age,
M_PatientName as patient_name,
M_PatientName_enc as patient_name_enc,
M_PatientNoReg as noreg,
M_SexName as gender,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as dob,
M_PatientDOB_enc as dob_enc, M_PatientDOB as dob_raw,
M_PatientJob as job,
M_PatientPosisi as posisi,
IF(M_PatientDivisi = '','-',M_PatientDivisi) as divisi,
M_PatientHp as hp,
M_PatientNIP as nip,
M_PatientEmail as email,
M_PatientHP_enc as hp_enc,
M_PatientNIP_enc as nip_enc,
M_PatientEmail_enc as email_enc,
M_PatientPhoto as photo,
T_OrderHeaderID as xid,
0 as testid,
@@ -208,6 +209,15 @@ class Patient extends MY_Controller
}
$data_patient = $query->row_array();
if ($data_patient) {
$enc = $this->ibl_encryptor;
$data_patient['patient_name'] = $enc->decrypt($data_patient['patient_name_enc'] ?? '') ?? '';
$data_patient['hp'] = $enc->decrypt($data_patient['hp_enc'] ?? '') ?? '';
$data_patient['email'] = $enc->decrypt($data_patient['email_enc'] ?? '') ?? '';
$data_patient['nip'] = $enc->decrypt($data_patient['nip_enc'] ?? '') ?? '';
$data_patient['dob'] = $enc->decrypt($data_patient['dob_enc'] ?? '') ?? date('d-m-Y', strtotime($data_patient['dob_raw'] ?? 'now'));
unset($data_patient['patient_name_enc'], $data_patient['hp_enc'], $data_patient['email_enc'], $data_patient['nip_enc'], $data_patient['dob_enc'], $data_patient['dob_raw']);
}
if (intval($stationid) == 11 || intval($stationid) == 35) {
$sql = "SELECT
T_SamplingAdditionalFisikBBTBID,

View File

@@ -11,6 +11,7 @@ class Samplingcall extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
$this->load->library('ibl_encryptor');
// $this->IP_SOCKET_IO = "devone.aplikasi.web.id";
$this->IP_SOCKET_IO = "localhost";
}
@@ -181,17 +182,17 @@ class Samplingcall extends MY_Controller
$sql_where = "WHERE T_OrderHeaderIsActive = 'Y' AND ( DATE(T_OrderHeaderAddonIsComingDate) = '{$xdate}' OR DATE(T_OrderHeaderDate) = '{$xdate}' ) {$where_status}";
//$sql_param = array();
if ($name != "") {
if ($sql_where != "") {
$sql_where .= " and ";
if ($name != "" && mb_strlen(trim($name)) >= 3) {
$toks = $this->ibl_encryptor->query_tokens($name);
foreach ($toks as $tok) {
$tok_esc = $this->db_onedev->escape_str($tok);
$sql_where .= " AND JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')";
}
$sql_where .= " M_PatientName like '%$name%' ";
//$sql_param[] = "%$nama%";
}
$filter_search = '';
if ($nolab != "") {
$filter_search = "WHERE ( T_OrderHeaderLabNumber like '%$nolab%' OR M_PatientName like '%$nolab%' OR T_OrderHeaderLabNumberExt like '%$nolab%' )";
// Hanya cari by nomor lab — nama pasien sudah dimasking
$filter_search = "WHERE ( T_OrderHeaderLabNumber like '%$nolab%' OR T_OrderHeaderLabNumberExt like '%$nolab%' )";
}
if ($search != '') {
@@ -207,11 +208,10 @@ class Samplingcall extends MY_Controller
IFNULL(M_PatientPhotoThumb,'') as M_PatientPhotoThumb,
M_SexName as M_SexName,
M_TitleName as M_TitleName,
CONCAT(M_TitleName,' ',M_PatientName) as patient_fullname,
M_PatientName as M_PatientName,
M_PatientName_enc, M_PatientDOB_enc,
M_CompanyName,
fn_sampling_queue_status_name(T_OrderHeaderID,T_SampleStationID) as status,
DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as patient_dob,
M_PatientDOB as patient_dob_raw,
fn_sampling_queue_status_id(T_OrderHeaderID,T_SampleStationID) as statusid, T_SampleStationID, T_SampleTypeID,
T_SampleStationID as stationid,
fn_fo_get_laststatus(T_OrderHeaderID) as last_status_fo,
@@ -257,13 +257,17 @@ class Samplingcall extends MY_Controller
$query = $this->db_onedev->query($sql);
//echo $this->db_onedev->last_query();
$rows = $query->result_array();
//$rst = array_merge($rows_cito,$rows_not_cito);
//$this->_add_address($rows);
if($rows){
if ($rows) {
$enc = $this->ibl_encryptor;
$count_arr = count($rows);
foreach ($rows as $key => $value) {
if($key+1 != $count_arr){
$rows[$key]['skip_time'] = $rows[$key+1]['antri_time'];
$name = $enc->decrypt($value['M_PatientName_enc']) ?? '';
$rows[$key]['M_PatientName'] = $name;
$rows[$key]['patient_fullname'] = trim(($value['M_TitleName'] ? $value['M_TitleName'] . ' ' : '') . $name);
$rows[$key]['patient_dob'] = $enc->decrypt($value['M_PatientDOB_enc']) ?? date('d-m-Y', strtotime($value['patient_dob_raw']));
unset($rows[$key]['M_PatientName_enc'], $rows[$key]['M_PatientDOB_enc'], $rows[$key]['patient_dob_raw']);
if ($key + 1 != $count_arr) {
$rows[$key]['skip_time'] = $rows[$key + 1]['antri_time'];
}
}
}
@@ -400,9 +404,9 @@ class Samplingcall extends MY_Controller
$sql_where = "WHERE T_OrderHeaderLabNumber LIKE '{$search}' AND T_OrderHeaderIsActive = 'Y' {$where_status}";
$rows = [];
$query = "SELECT t_orderheader.*,m_patient.*, IFNULL(M_PatientPhoto,'') as M_PatientPhotoThumb,
M_SexName, M_TitleName, CONCAT(M_TitleName,' ',M_PatientName) as patient_fullname, M_CompanyName,
IF(ISNULL(T_SamplingQueueLastStatusID), 'New',T_SamplingQueueStatusName) as status, DATE_FORMAT(M_PatientDOB,'%d-%m-%Y') as patient_dob,
$query = "SELECT t_orderheader.*, IFNULL(M_PatientPhoto,'') as M_PatientPhotoThumb,
M_SexName, M_TitleName, M_PatientName_enc, M_PatientDOB_enc, M_PatientDOB as patient_dob_raw, M_CompanyName,
IF(ISNULL(T_SamplingQueueLastStatusID), 'New',T_SamplingQueueStatusName) as status,
IF(ISNULL(T_SamplingQueueLastStatusID), 0,T_SamplingQueueLastStatusT_SamplingQueueStatusID) as statusid, T_SampleStationID, T_SampleTypeID,
{$stationid} as stationid,
fn_global_check_is_cito(T_OrderHeaderID) as iscito
@@ -431,12 +435,16 @@ class Samplingcall extends MY_Controller
ORDER BY T_OrderHeaderID DESC
limit 1";
//echo $query;
$rows = $this->db_onedev->query($query)->row();
$result = array(
"total" => count($rows),
"records" => $rows,
);
$row = $this->db_onedev->query($query)->row_array();
if ($row) {
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($row['M_PatientName_enc']) ?? '';
$row['M_PatientName'] = $name;
$row['patient_fullname'] = trim(($row['M_TitleName'] ? $row['M_TitleName'] . ' ' : '') . $name);
$row['patient_dob'] = $enc->decrypt($row['M_PatientDOB_enc']) ?? date('d-m-Y', strtotime($row['patient_dob_raw']));
unset($row['M_PatientName_enc'], $row['M_PatientDOB_enc'], $row['patient_dob_raw']);
}
$result = array("total" => 1, "records" => $row);
$this->sys_ok($result);
exit;
}
@@ -562,7 +570,7 @@ class Samplingcall extends MY_Controller
$userid = $prm['staff']['userid'];
}
$sql = "SELECT T_OrderHeaderQueue AS queueNumber ,
M_LocationID AS locationID,
@@ -578,7 +586,7 @@ class Samplingcall extends MY_Controller
$splitedLocationName = explode(" ", $locationName);
$locationName = $splitedLocationName[0];
if ($prm['act'] == 'call') {
$sql = "SELECT T_SamplingQueueLastStatusID, T_SamplingQueueStatusName, T_SampleStationName, T_SampleStationID, T_SampleStationIsNonLab
@@ -1002,18 +1010,18 @@ class Samplingcall extends MY_Controller
$rows = $this->db_onedev->query($query);
}
if ($status_call['status'] == 'NOTCALL') {
$rst_data = $status_call;
}
if ($prm['act'] == 'skip' || $status_call['status'] == 'NOTCALL') {
$skip_time = date('Y-m-d H:i:s', strtotime($prm['skiptime'])+10);
$skip_time = date('Y-m-d H:i:s', strtotime($prm['skiptime']) + 10);
$sql = "UPDATE antrian_samplestation SET AntrianSampleStationIsActive = 'N'
WHERE
AntrianSampleStationT_OrderLocationID = ?";
$query = $this->db_onedev->query($sql,array($prm['orderlocationid']));
$query = $this->db_onedev->query($sql, array($prm['orderlocationid']));
/* start dipaggil 3 kali skpi ururtan jd ke bawah */
/*$sql = "SELECT COUNT(*) as x_count
FROM antrian_samplestation
@@ -1037,8 +1045,7 @@ class Samplingcall extends MY_Controller
VALUES(
?,?,?,NOW()
)";
$query = $this->db_onedev->query($sql,array($prm['orderlocationid'],$skip_time,$userid));
$query = $this->db_onedev->query($sql, array($prm['orderlocationid'], $skip_time, $userid));
}
if (intval($next_status) == 1) {

View File

@@ -8,9 +8,35 @@ class Preregister extends MY_Controller
public function __construct()
{
parent::__construct();
$this->load->library('ibl_encryptor');
// $this->db = $this->load->database("cpone", true);
}
private function _mask_name($v) {
if (!$v) return $v;
$v = trim($v);
$words = preg_split('/\s+/', $v);
if (count($words) === 1) {
$l = mb_strlen($v, 'UTF-8');
if ($l <= 2) return $v;
return mb_substr($v, 0, 2, 'UTF-8') . str_repeat('*', $l - 2);
}
$first = $words[0];
$rest = array_slice($words, 1);
$masked = array_map(function($w) {
if (!$w) return '';
$init = mb_substr($w, 0, 1, 'UTF-8');
return $init . str_repeat('*', max(3, mb_strlen($w, 'UTF-8') - 1));
}, $rest);
return $first . ' ' . implode(' ', $masked);
}
private function _mask_phone($v) { if (!$v) return $v; $d=preg_replace('/[^0-9]/','',trim($v)); $l=strlen($d); if($l<=4) return '****'; if($l<=8) return substr($d,0,4).str_repeat('*',$l-4); return substr($d,0,4).str_repeat('*',$l-7).substr($d,-3); }
private function _mask_email($v) { if (!$v||strpos($v,'@')===false) return $v; [$loc,$dom]=explode('@',$v,2); return mb_substr($loc,0,min(2,mb_strlen($loc,'UTF-8')),'UTF-8').'***@'.$dom; }
private function _mask_short($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=2) return '***'; return mb_substr($v,0,2,'UTF-8').'***'; }
private function _mask_id($v) { if (!$v) return $v; $v=trim($v); $l=strlen($v); if($l<=4) return '****'; return substr($v,0,4).str_repeat('*',max(3,$l-6)).($l>6?substr($v,-2):''); }
private function _mask_address($v) { if (!$v) return $v; $v=trim($v); $l=mb_strlen($v,'UTF-8'); if($l<=5) return '***'; return mb_substr($v,0,5,'UTF-8').'***'; }
private function _mask_dob($v) { if (!$v) return $v; $p=explode('-',$v); return (count($p)===3) ? '**-**-'.$p[2] : '****-**-**'; }
public function index()
{
// $cek = $this->db->query("select database() as current_db")->result();
@@ -59,8 +85,8 @@ class Preregister extends MY_Controller
}
}
function cekKTP($nik, $tanggal, $bulan, $tahun)
{
function cekKTP($nik, $tanggal, $bulan, $tahun)
{
if (strlen($nik) != 16) {
return false;
}
@@ -88,30 +114,30 @@ class Preregister extends MY_Controller
return false;
}
//setelah berhasil melewati rintangan, berarti nomornya valid (tidak 100% valid)
return true;
}
function normalize_schedule_date($rawDate)
{
$rawDate = trim((string) $rawDate);
if ($rawDate === '') {
return '';
}
$formats = array('d-m-Y', 'Y-m-d', 'd/m/Y', 'Y/m/d');
foreach ($formats as $format) {
$dt = DateTime::createFromFormat($format, $rawDate);
if ($dt && $dt->format($format) === $rawDate) {
return $dt->format('Y-m-d');
}
}
$timestamp = strtotime($rawDate);
if ($timestamp === false) {
return '';
}
return date('Y-m-d', $timestamp);
}
return true;
}
function normalize_schedule_date($rawDate)
{
$rawDate = trim((string) $rawDate);
if ($rawDate === '') {
return '';
}
$formats = array('d-m-Y', 'Y-m-d', 'd/m/Y', 'Y/m/d');
foreach ($formats as $format) {
$dt = DateTime::createFromFormat($format, $rawDate);
if ($dt && $dt->format($format) === $rawDate) {
return $dt->format('Y-m-d');
}
}
$timestamp = strtotime($rawDate);
if ($timestamp === false) {
return '';
}
return date('Y-m-d', $timestamp);
}
function savecsv()
{
@@ -149,7 +175,7 @@ class Preregister extends MY_Controller
$exist_patients_arr = [];
$exist_pat = [];
foreach ($datas as $k => $v) {
foreach ($datas as $k => $v) {
$timestamp = strtotime($v['TANGGAL_LAHIR']);
$pdob = date('Y-m-d', $timestamp);
$v['NAMA'] = trim(str_replace("'", "\\'", $v['NAMA']));
@@ -205,15 +231,16 @@ class Preregister extends MY_Controller
if ($exist_r) {
$enc_csv = $this->ibl_encryptor;
$patient_id = $exist_r["M_PatientID"];
$v['NAMA'] = addslashes($exist_r["M_PatientName"]);
$v['NAMA'] = $enc_csv->decrypt($exist_r['M_PatientName_enc'] ?? '') ?: $exist_r["M_PatientName"];
$pdob = date('Y-m-d', strtotime($exist_r['M_PatientDOB']));
$title_id = $exist_r["M_PatientM_TitleID"];
$sex_id = $exist_r["M_PatientM_SexID"];
$religion_id = $exist_r["M_PatientReligionCode"];
$v['NIK'] = $v['NIK'] ? $v['NIK'] : $exist_r["M_PatientNIK"];
$v['EMAIL'] = $v['EMAIL'] ? $v['EMAIL'] : $exist_r["M_PatientEmail"];
$v['HP'] = $v['HP'] ? $v['HP'] : $exist_r["M_PatientHP"];
$v['KTP'] = $v['KTP'] ? $v['KTP'] : ($enc_csv->decrypt($exist_r['M_PatientIDNumber_enc'] ?? '') ?: '');
$v['EMAIL'] = $v['EMAIL'] ? $v['EMAIL'] : ($enc_csv->decrypt($exist_r['M_PatientEmail_enc'] ?? '') ?: '');
$v['HP'] = $v['HP'] ? $v['HP'] : ($enc_csv->decrypt($exist_r['M_PatientHP_enc'] ?? '') ?: '');
$v['JOB'] = $v['JOB'] ? addslashes($v['JOB']) : addslashes($exist_r["M_PatientJob"]);
$v['POSISI'] = $v['POSISI'] ? addslashes($v['POSISI']) : addslashes($exist_r["M_PatientPosisi"]);
$v['DIVISI'] = $v['DIVISI'] ? addslashes($v['DIVISI']) : addslashes($exist_r["M_PatientDivisi"]);
@@ -302,11 +329,17 @@ class Preregister extends MY_Controller
$this->sys_error("select mcu_preregister_patients : " . $last_qry);
exit;
}
$exist_r = $qry_pre->result_array();
$preregister_patient_id = 0;
if (count($exist_r) == 0) {
$query = " INSERT INTO mcu_preregister_patients (
$exist_r = $qry_pre->result_array();
$preregister_patient_id = 0;
if (count($exist_r) == 0) {
$m_nama = $this->db->escape_str($this->_mask_name($v['NAMA']));
$m_ktp = $this->_mask_id($v['KTP']);
$m_nip = $this->_mask_id($v['NIP']);
$m_email = $this->_mask_email($v['EMAIL']);
$m_hp = $this->_mask_phone($v['HP']);
$m_dob = $this->_mask_dob(date('d-m-Y', strtotime($pdob)));
$query = " INSERT INTO mcu_preregister_patients (
Mcu_PreregisterPatientsMgm_McuID,
Mcu_PreregisterPatientsCompanyNumber,
Mcu_PreregisterPatientsNIP,
@@ -331,16 +364,16 @@ class Preregister extends MY_Controller
VALUES(
'{$prm['xid']}',
'{$rowcor["M_CompanyNumber"]}',
'{$v['NIP']}',
'{$v['KTP']}',
'{$m_nip}',
'{$m_ktp}',
'{$patient_id}',
'{$title_id}',
'{$v['NAMA']}',
'{$m_nama}',
{$sex_id},
'{$pdob}',
'{$m_dob}',
'{$v['JOB']}',
'{$v['EMAIL']}',
'{$v['HP']}',
'{$m_email}',
'{$m_hp}',
'{$v['POSISI']}',
'{$v['DIVISI']}',
'{$v['LOKASI']}',
@@ -360,31 +393,34 @@ class Preregister extends MY_Controller
$this->sys_error("insert mcu_preregister_patients : " . $last_qry);
exit;
}
if ($rows) {
$last_id_x = $this->db->insert_id();
$preregister_patient_id = intval($last_id_x);
if ($patient_id == 0) {
$sql = "SELECT *
FROM m_patient
WHERE
M_PatientName = '{$v['NAMA']}' AND
M_PatientDOB = '{$pdob}' AND
M_PatientNIP = '{$v['NIP']}' AND
M_PatientIsActive = 'Y' LIMIT 1";
$query = $this->db->query($sql);
if (!$query) {
$last_qry = $this->db->last_query();
$this->db->trans_rollback();
if ($rows) {
$last_id_x = $this->db->insert_id();
$preregister_patient_id = intval($last_id_x);
$this->sys_error("select m_patient : " . $last_qry);
if ($patient_id == 0) {
$enc_csv = $this->ibl_encryptor;
$name_toks = $enc_csv->query_tokens($v['NAMA']);
$name_conds = [];
foreach ($name_toks as $tok) {
$tok_esc = $this->db->escape_str($tok);
$name_conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')";
}
$name_where = $name_conds ? implode(' AND ', $name_conds) : '0';
$sql = "SELECT M_PatientID FROM m_patient
WHERE ({$name_where})
AND M_PatientDOB = ?
AND M_PatientNIP = ?
AND M_PatientIsActive = 'Y' LIMIT 1";
$query = $this->db->query($sql, [$pdob, $v['NIP']]);
if (!$query) {
$this->db->trans_rollback();
$this->sys_error("select m_patient : " . $this->db->last_query());
exit;
}
$exist_r = $query->row_array();
if ($exist_r) {
$patient_id = $exist_r["M_PatientID"];
$patient_id = $exist_r["M_PatientID"];
}
//echo $sql;
}
$sql_cor = "SELECT *
@@ -426,51 +462,41 @@ class Preregister extends MY_Controller
//echo $patient_id;
if ($patient_id == 0) {
//echo 'insert new patient';
//$pdob = date('Y-m-d',strtotime($prm['Mcu_PreregisterDetailsDOB']));
$sql = "INSERT INTO m_patient (
M_PatientRegisteredByCompanyID,
M_PatientNoReg,
M_PatientName,
M_PatientM_SexID,
M_PatientM_TitleID,
M_PatientDOB,
M_PatientIdentifierValue,
M_PatientNIP,
M_PatientJob,
M_PatientPosisi,
M_PatientDivisi,
M_PatientLocation,
M_PatientDepartement,
M_PatientHP,
M_PatientEmail,
M_PatientCreatedUserID
)
VALUES(
'{$company_id}',
`fn_numbering_ibl`('PA'),
'{$v["NAMA"]}',
{$sex_id},
{$title_id},
'{$pdob}',
'{$v['KTP']}',
'{$v['NIP']}',
'{$v['JOB']}',
'{$v['POSISI']}',
'{$v['DIVISI']}',
'{$v['LOKASI']}',
'{$v['DEPARTEMENT']}',
'{$v['HP']}',
'{$v['EMAIL']}',
'{$userid}'
)";
// echo $sql;
// exit;
$query = $this->db->query($sql);
$enc_csv = $this->ibl_encryptor;
$dob_str_csv = date('d-m-Y', strtotime($pdob));
$noreg_row = $this->db->query("SELECT `fn_numbering_ibl`('PA') AS noreg")->row_array();
$ins_ptn = [
'M_PatientRegisteredByCompanyID' => $company_id,
'M_PatientNoReg' => $noreg_row['noreg'] ?? '',
'M_PatientName' => $this->_mask_name($v["NAMA"]),
'M_PatientName_enc' => $enc_csv->encrypt($v["NAMA"]),
'M_PatientName_bidx' => $enc_csv->search_bidx($v["NAMA"]),
'M_PatientM_SexID' => $sex_id,
'M_PatientM_TitleID' => $title_id,
'M_PatientDOB' => $this->_mask_dob($dob_str_csv),
'M_PatientDOB_enc' => $enc_csv->encrypt($dob_str_csv),
'M_PatientDOB_bidx' => $enc_csv->search_bidx($dob_str_csv),
'M_PatientM_IdTypeID' => $v['KTP'] ? 1 : 0,
'M_PatientIDNumber' => $v['KTP'] ? $this->_mask_id($v['KTP']) : null,
'M_PatientIDNumber_enc' => $v['KTP'] ? $enc_csv->encrypt($v['KTP']) : null,
'M_PatientNIK_bidx' => $enc_csv->search_bidx($v['KTP'] ?? ''),
'M_PatientNIP' => $v['NIP'],
'M_PatientJob' => $v['JOB'],
'M_PatientPosisi' => $v['POSISI'],
'M_PatientDivisi' => $v['DIVISI'],
'M_PatientLocation' => $v['LOKASI'],
'M_PatientDepartement' => $v['DEPARTEMENT'],
'M_PatientHP' => $this->_mask_phone($v['HP']),
'M_PatientHP_enc' => $enc_csv->encrypt($v['HP']),
'M_PatientHP_bidx' => $enc_csv->search_bidx($v['HP']),
'M_PatientEmail' => $this->_mask_email($v['EMAIL']),
'M_PatientEmail_enc' => $enc_csv->encrypt($v['EMAIL']),
'M_PatientCreatedUserID'=> $userid,
];
$query = $this->db->insert('m_patient', $ins_ptn);
if (!$query) {
$last_qry = $this->db->last_query();
$this->db->trans_rollback();
$this->sys_error("insert m_patient : " . $last_qry);
$this->sys_error("insert m_patient : " . $this->db->last_query());
exit;
}
$patient_id = $this->db->insert_id();
@@ -521,10 +547,16 @@ class Preregister extends MY_Controller
if ($prm['companyID'] != '')
$data_update_patient['M_PatientRegisteredByCompanyID'] = $prm['companyID'];
if ($v['EMAIL'] != '')
$data_update_patient['M_PatientEmail'] = $v['EMAIL'];
if ($v['HP'] != '')
$data_update_patient['M_PatientHP'] = $v['HP'];
$enc_csv = $this->ibl_encryptor;
if ($v['EMAIL'] != '') {
$data_update_patient['M_PatientEmail'] = $this->_mask_email($v['EMAIL']);
$data_update_patient['M_PatientEmail_enc'] = $enc_csv->encrypt($v['EMAIL']);
}
if ($v['HP'] != '') {
$data_update_patient['M_PatientHP'] = $this->_mask_phone($v['HP']);
$data_update_patient['M_PatientHP_enc'] = $enc_csv->encrypt($v['HP']);
$data_update_patient['M_PatientHP_bidx'] = $enc_csv->search_bidx($v['HP']);
}
if ($v['JOB'] != '')
$data_update_patient['M_PatientJob'] = $v['JOB'];
if ($v['POSISI'] != '')
@@ -543,50 +575,50 @@ class Preregister extends MY_Controller
$sql = "UPDATE mcu_preregister_patients SET Mcu_PreregisterPatientsM_PatientID = {$patient_id}
WHERE Mcu_PreregisterPatientsID = {$last_id_x}";
$query = $this->db->query($sql);
if (!$query) {
$last_qry = $this->db->last_query();
$this->db->trans_rollback();
$this->sys_error("update mcu_preregister_patients : " . $last_qry);
exit;
}
}
} else {
$preregister_patient_id = intval($exist_r[0]['Mcu_PreregisterPatientsID']);
}
// Simpan jadwal MCU per preregister patient jika parameter TANGGAL_MCU dikirim
$scheduleDate = isset($v['TANGGAL_MCU']) ? $this->normalize_schedule_date($v['TANGGAL_MCU']) : '';
if ($preregister_patient_id > 0 && $scheduleDate !== '') {
$sqlSchedule = "INSERT INTO mcu_preregister_date (
Mcu_PreregisterDateMcu_PreregisterPatientsID,
Mcu_PreregisterDateCheckinSchedule,
Mcu_PreregisterDateIsActive,
Mcu_PreregisterDateCreated,
Mcu_PreregisterDateCreatedUserID,
Mcu_PreregisterDateLastUpdated,
Mcu_PreregisterDateLastUpdatedUserID
) VALUES (
?, ?, 'Y', NOW(), ?, NOW(), ?
)
ON DUPLICATE KEY UPDATE
Mcu_PreregisterDateIsActive = 'Y',
Mcu_PreregisterDateLastUpdated = NOW(),
Mcu_PreregisterDateLastUpdatedUserID = VALUES(Mcu_PreregisterDateLastUpdatedUserID)";
$qrySchedule = $this->db->query($sqlSchedule, array(
$preregister_patient_id,
$scheduleDate,
$userid,
$userid
));
if (!$qrySchedule) {
$last_qry = $this->db->last_query();
$this->db->trans_rollback();
$this->sys_error("insert mcu_preregister_date : " . $last_qry);
exit;
}
}
}
$query = $this->db->query($sql);
if (!$query) {
$last_qry = $this->db->last_query();
$this->db->trans_rollback();
$this->sys_error("update mcu_preregister_patients : " . $last_qry);
exit;
}
}
} else {
$preregister_patient_id = intval($exist_r[0]['Mcu_PreregisterPatientsID']);
}
// Simpan jadwal MCU per preregister patient jika parameter TANGGAL_MCU dikirim
$scheduleDate = isset($v['TANGGAL_MCU']) ? $this->normalize_schedule_date($v['TANGGAL_MCU']) : '';
if ($preregister_patient_id > 0 && $scheduleDate !== '') {
$sqlSchedule = "INSERT INTO mcu_preregister_date (
Mcu_PreregisterDateMcu_PreregisterPatientsID,
Mcu_PreregisterDateCheckinSchedule,
Mcu_PreregisterDateIsActive,
Mcu_PreregisterDateCreated,
Mcu_PreregisterDateCreatedUserID,
Mcu_PreregisterDateLastUpdated,
Mcu_PreregisterDateLastUpdatedUserID
) VALUES (
?, ?, 'Y', NOW(), ?, NOW(), ?
)
ON DUPLICATE KEY UPDATE
Mcu_PreregisterDateIsActive = 'Y',
Mcu_PreregisterDateLastUpdated = NOW(),
Mcu_PreregisterDateLastUpdatedUserID = VALUES(Mcu_PreregisterDateLastUpdatedUserID)";
$qrySchedule = $this->db->query($sqlSchedule, array(
$preregister_patient_id,
$scheduleDate,
$userid,
$userid
));
if (!$qrySchedule) {
$last_qry = $this->db->last_query();
$this->db->trans_rollback();
$this->sys_error("insert mcu_preregister_date : " . $last_qry);
exit;
}
}
}
if ($this->db->trans_status() === FALSE) {
$this->db->trans_rollback();
@@ -960,32 +992,36 @@ class Preregister extends MY_Controller
}
if ($v['KTP'] != '') {
$sql = "SELECT *
FROM m_patient
$enc = $this->ibl_encryptor;
$ktp_toks = $enc->query_tokens($v['KTP']);
$ktp_conds = [];
foreach ($ktp_toks as $tok) {
$tok_esc = $this->db_onedev->escape_str($tok);
$ktp_conds[] = "JSON_CONTAINS(M_PatientNIK_bidx, '\"$tok_esc\"')";
}
$ktp_where = $ktp_conds ? implode(' AND ', $ktp_conds) : '0';
$sql = "SELECT m_patient.*, M_SexCode
FROM m_patient
JOIN m_sex ON M_PatientM_SexID = M_SexID
WHERE M_PatientM_IdTypeID = 1 AND
M_PatientIDNumber = '{$v['KTP']}' AND
M_PatientIsActive = 'Y'
WHERE M_PatientIsActive = 'Y' AND ({$ktp_where})
LIMIT 1";
$exist_r = $this->db_onedev->query($sql)->row_array();
if ($exist_r) {
$patient_id = $exist_r["M_PatientID"];
$v['NAMA'] = $exist_r["M_PatientName"];
//$pdob = date('Y-m-d',strtotime($exist_r['M_PatientDOB']));
$v['NAMA'] = $enc->decrypt($exist_r['M_PatientName_enc']) ?? $exist_r["M_PatientName"];
$title_id = $exist_r["M_PatientM_TitleID"];
$sex_id = $exist_r["M_PatientM_SexID"];
$religion_id = $exist_r["M_PatientM_ReligionID"];
$v['NIK'] = $v['NIK'] ? $v['NIK'] : $exist_r["M_PatientNIK"];
$v['EMAIL'] = $v['EMAIL'] ? $v['EMAIL'] : $exist_r["M_PatientEmail"];
$v['HP'] = $v['HP'] ? $v['HP'] : $exist_r["M_PatientHP"];
$v['NIK'] = $v['NIK'] ? $v['NIK'] : ($enc->decrypt($exist_r['M_PatientNIK_enc'] ?? '') ?? $exist_r["M_PatientNIK"]);
$v['EMAIL'] = $v['EMAIL'] ? $v['EMAIL'] : ($enc->decrypt($exist_r['M_PatientEmail_enc'] ?? '') ?? $exist_r["M_PatientEmail"]);
$v['HP'] = $v['HP'] ? $v['HP'] : ($enc->decrypt($exist_r['M_PatientHP_enc'] ?? '') ?? $exist_r["M_PatientHP"]);
$v['KEDUDUKAN'] = $v['KEDUDUKAN'] ? $v['KEDUDUKAN'] : $exist_r["M_PatientKedudukan"];
$v['JABATAN'] = $v['JABATAN'] ? $v['JABATAN'] : $exist_r["M_PatientJabatan"];
$v['JOB'] = $v['JOB'] ? $v['JOB'] : $exist_r["M_PatientJob"];
$v['LOKASI'] = $v['LOKASI'] ? addslashes($v['LOKASI']) : addslashes($exist_r["M_PatientLocation"]);
$v['JENIS_KELAMIN'] = $exist_r["M_SexCode"];
$v['KTP'] = $v['KTP'] ? $v['KTP'] : $exist_r["M_PatientIDNumber"];
$v['KTP'] = $v['KTP'] ? $v['KTP'] : ($enc->decrypt($exist_r['M_PatientIDNumber_enc'] ?? '') ?? $exist_r["M_PatientIDNumber"]);
}
//echo $sql;
}
if ($patient_id == 0) {
@@ -1005,6 +1041,11 @@ class Preregister extends MY_Controller
$sql = "SELECT * FROM m_religion WHERE M_ReligionName = 'OTHERS' AND M_ReligionIsActive = 'Y' LIMIT 1";
$religion_id = $this->db_onedev->query($sql)->row()->M_ReligionID;
}
$m_nama = $this->db_onedev->escape_str($this->_mask_name($v['NAMA']));
$m_ktp = $this->_mask_id($v['KTP']);
$m_nik = $this->_mask_id($v['NIK']);
$m_email = $this->_mask_email($v['EMAIL']);
$m_hp = $this->_mask_phone($v['HP']);
$query = " INSERT INTO mcu_preregister_patients (
Mcu_PreregisterDetailsMcuOfflinePrepareID,
Mcu_PreregisterDetailsPID,
@@ -1029,13 +1070,13 @@ class Preregister extends MY_Controller
VALUES(
'{$prm['xid']}',
'{$v['PID']}',
'{$v['KTP']}',
'{$v['NIK']}',
'{$m_ktp}',
'{$m_nik}',
'{$title_id}',
'{$v['NAMA']}',
'{$m_nama}',
'{$religion_id}',
'{$v['EMAIL']}',
'{$v['HP']}',
'{$m_email}',
'{$m_hp}',
'{$pdob}',
'{$v['KEDUDUKAN']}',
'{$v['JABATAN']}',
@@ -1060,18 +1101,22 @@ class Preregister extends MY_Controller
//print_r($row_header);
if ($patient_id == 0) {
$sql = "SELECT *
FROM m_patient
WHERE
M_PatientName = '{$v['NAMA']}' AND
M_PatientDOB = '{$pdob}' AND
M_PatientNIP = '{$v['NIK']}' AND
M_PatientIsActive = 'Y' LIMIT 1";
$enc = $this->ibl_encryptor;
$name_toks = $enc->query_tokens($v['NAMA']);
$name_conds = [];
foreach ($name_toks as $tok) {
$tok_esc = $this->db_onedev->escape_str($tok);
$name_conds[] = "JSON_CONTAINS(M_PatientName_bidx, '\"$tok_esc\"')";
}
$name_where = $name_conds ? implode(' AND ', $name_conds) : '0';
$sql = "SELECT M_PatientID FROM m_patient
WHERE ({$name_where})
AND M_PatientDOB = '{$pdob}'
AND M_PatientIsActive = 'Y' LIMIT 1";
$exist_r = $this->db_onedev->query($sql)->row_array();
if ($exist_r) {
$patient_id = $exist_r["M_PatientID"];
$patient_id = $exist_r["M_PatientID"];
}
//echo $sql;
}
//echo $patient_id;
@@ -1084,85 +1129,53 @@ class Preregister extends MY_Controller
$M_PatientM_IdTypeID = 1;
$M_PatientIDNumber = $v["KTP"];
}
$sql = "INSERT INTO m_patient (
M_PatientName,
M_PatientM_TitleID,
M_PatientM_SexID,
M_PatientM_ReligionID,
M_PatientPOB,
M_PatientDOB,
M_PatientNIK,
M_PatientM_IdTypeID,
M_PatientIDNumber,
M_PatientJabatan,
M_PatientLocation,
M_PatientKedudukan,
M_PatientJob,
M_PatientEmail,
M_PatientHP,
M_PatientUserID
)
VALUES(
'{$v["NAMA"]}',
{$title_id},
{$sex_id},
{$religion_id},
'-',
'{$pdob}',
'{$v["NIK"]}',
'{$M_PatientM_IdTypeID}',
'{$M_PatientIDNumber}',
'{$v['JABATAN']}',
'{$v['LOKASI']}',
'{$v['KEDUDUKAN']}',
'{$v['JOB']}',
'{$v['EMAIL']}',
'{$v['HP']}',
'{$userid}'
)";
//echo $sql;
$this->db_onedev->query($sql);
$data_insert_patient = array(
'M_PatientName' => $v["NAMA"],
'M_PatientM_TitleID' => $title_id,
'M_PatientM_SexID' => $sex_id,
$enc = $this->ibl_encryptor;
$dob_str = date('d-m-Y', strtotime($pdob));
$data_insert_patient = [
'M_PatientName' => $this->_mask_name($v["NAMA"]),
'M_PatientName_enc' => $enc->encrypt($v["NAMA"]),
'M_PatientName_bidx' => $enc->search_bidx($v["NAMA"]),
'M_PatientM_TitleID' => $title_id,
'M_PatientM_SexID' => $sex_id,
'M_PatientM_ReligionID' => $religion_id,
'M_PatientPOB' => '-',
'M_PatientDOB' => $pdob,
'M_PatientNIK' => $v["NIK"],
'M_PatientJabatan' => $v['JABATAN'],
'M_PatientLocation' => $v['LOKASI'],
'M_PatientKedudukan' => $v['KEDUDUKAN'],
'M_PatientJob' => $v['JOB'],
'M_PatientEmail' => $v['EMAIL'],
'M_PatientHP' => $v['HP'],
'M_PatientUserID' => $userid
);
//$this->db->insert('m_patient', $data_insert_patient);
//echo $this->db_onedev->last_query();
'M_PatientPOB' => '***',
'M_PatientPOB_enc' => $enc->encrypt('-'),
'M_PatientDOB' => $pdob,
'M_PatientDOB_enc' => $enc->encrypt($dob_str),
'M_PatientDOB_bidx' => $enc->search_bidx($dob_str),
'M_PatientNIK' => $v["NIK"],
'M_PatientNIK_bidx' => $enc->search_bidx($v["NIK"] ?? ''),
'M_PatientM_IdTypeID' => $M_PatientM_IdTypeID,
'M_PatientIDNumber' => $M_PatientIDNumber ? $this->_mask_id($M_PatientIDNumber) : null,
'M_PatientIDNumber_enc' => $M_PatientIDNumber ? $enc->encrypt($M_PatientIDNumber) : null,
'M_PatientJabatan' => $v['JABATAN'],
'M_PatientLocation' => $v['LOKASI'],
'M_PatientKedudukan' => $v['KEDUDUKAN'],
'M_PatientJob' => $v['JOB'],
'M_PatientEmail' => $this->_mask_email($v['EMAIL']),
'M_PatientEmail_enc' => $enc->encrypt($v['EMAIL']),
'M_PatientHP' => $this->_mask_phone($v['HP']),
'M_PatientHP_enc' => $enc->encrypt($v['HP']),
'M_PatientHP_bidx' => $enc->search_bidx($v['HP']),
'M_PatientUserID' => $userid,
];
$this->db_onedev->insert('m_patient', $data_insert_patient);
$patient_id = $this->db_onedev->insert_id();
//$sql = "SELECT LAST_INSERT_ID() as xid";
//$patient_id = $this->db_onedev->query($sql)->row()->xid;
//echo $patient_id ;
$sql = "INSERT INTO m_patientaddress (
M_PatientAddressM_PatientID,
M_PatientAddressDescription,
M_PatientAddressM_KelurahanID,
M_PatientAddressCreated,
M_PatientAddressUserID
)
VALUES(
{$patient_id},
'{$row_header['M_CompanyAddress']}',
'{$row_header['M_CompanyM_KelurahanID']}',
NOW(),
'{$userid}'
)";
$this->db_onedev->query($sql);
$enc = $this->ibl_encryptor;
$addr_desc = $row_header['M_CompanyAddress'];
$this->db_onedev->insert('m_patientaddress', [
'M_PatientAddressM_PatientID' => $patient_id,
'M_PatientAddressDescription' => $this->_mask_address($addr_desc),
'M_PatientAddressDescription_enc' => $enc->encrypt($addr_desc),
'M_PatientAddressM_KelurahanID' => $row_header['M_CompanyM_KelurahanID'],
'M_PatientAddressCreated' => date('Y-m-d H:i:s'),
'M_PatientAddressUserID' => $userid,
]);
//echo $sql;
//$patient_addr_id = $this->db_onedev->insert_id();
//$sql = "SELECT * FROM m_patientaddress WHERE M_PatientAddressID = {$patient_addr_id}";
@@ -1174,20 +1187,31 @@ class Preregister extends MY_Controller
} else {
//echo 'masuk';
//$pdob = date('Y-m-d',strtotime($prm['Mcu_PreregisterDetailsDOB']));
$data_update_patient = array(
'M_PatientDOB' => $pdob
);
$enc = $this->ibl_encryptor;
$dob_str2 = date('d-m-Y', strtotime($pdob));
$data_update_patient = [
'M_PatientDOB' => $pdob,
'M_PatientDOB_enc' => $enc->encrypt($dob_str2),
'M_PatientDOB_bidx'=> $enc->search_bidx($dob_str2),
];
if ($v['JENIS_KELAMIN'] == 'L')
$data_update_patient['M_PatientM_TitleID'] = 2;
else
$data_update_patient['M_PatientM_TitleID'] = 4;
if ($v['EMAIL'] != '')
$data_update_patient['M_PatientEmail'] = $v['EMAIL'];
if ($v['HP'] != '')
$data_update_patient['M_PatientHP'] = $v['HP'];
if ($v['NIK'] != '')
$data_update_patient['M_PatientNIK'] = $v['NIK'];
if ($v['EMAIL'] != '') {
$data_update_patient['M_PatientEmail'] = $this->_mask_email($v['EMAIL']);
$data_update_patient['M_PatientEmail_enc'] = $enc->encrypt($v['EMAIL']);
}
if ($v['HP'] != '') {
$data_update_patient['M_PatientHP'] = $this->_mask_phone($v['HP']);
$data_update_patient['M_PatientHP_enc'] = $enc->encrypt($v['HP']);
$data_update_patient['M_PatientHP_bidx'] = $enc->search_bidx($v['HP']);
}
if ($v['NIK'] != '') {
$data_update_patient['M_PatientNIK'] = $v['NIK'];
$data_update_patient['M_PatientNIK_bidx'] = $enc->search_bidx($v['NIK']);
}
if ($v['JABATAN'] != '')
$data_update_patient['M_PatientJabatan'] = $v['JABATAN'];
if ($v['KEDUDUKAN'] != '')
@@ -1198,8 +1222,9 @@ class Preregister extends MY_Controller
$data_update_patient['M_PatientJob'] = $v['JOB'];
if (isset($v["KTP"]) && $v["KTP"] != '') {
$data_update_patient['M_PatientM_IdTypeID'] = 1;
$data_update_patient['M_PatientIDNumber'] = $v["KTP"];
$data_update_patient['M_PatientM_IdTypeID'] = 1;
$data_update_patient['M_PatientIDNumber'] = $this->_mask_id($v["KTP"]);
$data_update_patient['M_PatientIDNumber_enc'] = $enc->encrypt($v["KTP"]);
}
$this->db_onedev->where('M_PatientID', $patient_id);
@@ -1247,56 +1272,46 @@ class Preregister extends MY_Controller
$prm = $this->sys_input;
$userid = $this->sys_user["M_UserID"];
$pdob = date('Y-m-d', strtotime($prm['M_PatientDOB']));
$query = "INSERT INTO m_patient (
M_PatientM_TitleID,
M_PatientPrefix,
M_PatientName,
M_PatientSuffix,
M_PatientDOB,
M_PatientM_SexID,
M_PatientM_ReligionID,
M_PatientEmail,
M_PatientPOB,
M_PatientHP,
M_PatientPhone,
M_PatientM_IdTypeID,
M_PatientIDNumber,
M_PatientNote,
M_PatientNIK,
M_PatientJabatan,
M_PatientKedudukan,
M_PatientPJ,
M_PatientLocation,
M_PatientJob,
M_PatientUserID
)
VALUES(
'{$prm['M_PatientM_TitleID']}',
'{$prm['M_PatientPrefix']}',
'{$prm['M_PatientName']}',
'{$prm['M_PatientSuffix']}',
'{$pdob}',
'{$prm['M_PatientM_SexID']}',
'{$prm['M_PatientM_ReligionID']}',
'{$prm['M_PatientEmail']}',
'{$prm['M_PatientPOB']}',
'{$prm['M_PatientHP']}',
'{$prm['M_PatientPhone']}',
'{$prm['M_PatientM_IdTypeID']}',
'{$prm['M_PatientIDNumber']}',
'{$prm['M_PatientNote']}',
'{$prm['M_PatientNIK']}',
'{$prm['M_PatientJabatan']}',
'{$prm['M_PatientKedudukan']}',
'{$prm['M_PatientPJ']}',
'{$prm['M_PatientLocation']}',
'{$prm['M_PatientJob']}',
$userid
)
";
//echo $query;
$rows = $this->db_onedev->query($query);
$pdob = date('Y-m-d', strtotime($prm['M_PatientDOB']));
$dob_str = date('d-m-Y', strtotime($prm['M_PatientDOB']));
$patient_name = $prm['M_PatientName'];
$enc = $this->ibl_encryptor;
$ptn = [
'M_PatientName' => $this->_mask_name($patient_name),
'M_PatientName_enc' => $enc->encrypt($patient_name),
'M_PatientName_bidx' => $enc->search_bidx($patient_name),
'M_PatientM_TitleID' => $prm['M_PatientM_TitleID'],
'M_PatientPrefix' => $prm['M_PatientPrefix'],
'M_PatientSuffix' => $prm['M_PatientSuffix'],
'M_PatientDOB' => $pdob,
'M_PatientDOB_enc' => $enc->encrypt($dob_str),
'M_PatientDOB_bidx' => $enc->search_bidx($dob_str),
'M_PatientM_SexID' => $prm['M_PatientM_SexID'],
'M_PatientM_ReligionID' => $prm['M_PatientM_ReligionID'],
'M_PatientEmail' => $this->_mask_email($prm['M_PatientEmail']),
'M_PatientEmail_enc' => $enc->encrypt($prm['M_PatientEmail']),
'M_PatientPOB' => $this->_mask_short($prm['M_PatientPOB']),
'M_PatientPOB_enc' => $enc->encrypt($prm['M_PatientPOB']),
'M_PatientHP' => $this->_mask_phone($prm['M_PatientHP']),
'M_PatientHP_enc' => $enc->encrypt($prm['M_PatientHP']),
'M_PatientHP_bidx' => $enc->search_bidx($prm['M_PatientHP']),
'M_PatientPhone' => $this->_mask_phone($prm['M_PatientPhone']),
'M_PatientPhone_enc' => $enc->encrypt($prm['M_PatientPhone']),
'M_PatientM_IdTypeID' => $prm['M_PatientM_IdTypeID'],
'M_PatientIDNumber' => $this->_mask_id($prm['M_PatientIDNumber']),
'M_PatientIDNumber_enc' => $enc->encrypt($prm['M_PatientIDNumber']),
'M_PatientNIK' => $prm['M_PatientNIK'],
'M_PatientNIK_bidx' => $enc->search_bidx($prm['M_PatientNIK'] ?? ''),
'M_PatientNote' => $prm['M_PatientNote'],
'M_PatientJabatan' => $prm['M_PatientJabatan'],
'M_PatientKedudukan' => $prm['M_PatientKedudukan'],
'M_PatientPJ' => $prm['M_PatientPJ'],
'M_PatientLocation' => $prm['M_PatientLocation'],
'M_PatientJob' => $prm['M_PatientJob'],
'M_PatientUserID' => $userid,
];
$this->db_onedev->insert('m_patient', $ptn);
$last_id = $this->db_onedev->insert_id();
$result = array(
"total" => 1,

View File

@@ -6,6 +6,7 @@ class Hasil extends MY_Controller
{
function __construct() {
parent::__construct();
$this->load->library('ibl_patient_decrypt');
}
/* WA ------ */
function update_track() {
@@ -524,14 +525,13 @@ class Hasil extends MY_Controller
}
}
function dl_report($prm, $name) {
$url = "http://localhost/birt/frameset?" ;
$qry = "";
$qry = "";
foreach($prm as $k => $v) {
if ($qry != "" ) $qry .= "&";
if ($qry != "") $qry .= "&";
$qry .= $k . "=" . urlencode($v);
}
$url .= $qry ;
$data = file_get_contents($url);
}
$relative_url = "/birt/frameset?" . $qry;
$data = $this->ibl_patient_decrypt->fetch_birt_pdf($relative_url);
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $name);
header('Content-Transfer-Encoding: binary');

View File

@@ -0,0 +1,600 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Birt_proxy — PHP proxy untuk semua BIRT report call
*
* Flow:
* 1. Terima request dari frontend (report_code + params)
* 2. Decrypt patient PII dari _enc
* 3. INSERT ke patient_print_cache
* 4. Call BIRT via file_get_contents (internal)
* 5. DELETE cache
* 6. Stream PDF ke frontend
*
* Endpoint: POST /tools/birt_proxy/stream
* Params : report_code, PT_OrderHeaderID, PUsername, (optional) PID_patient
*/
class Birt_proxy extends MY_Controller
{
public $db_onedev;
private $birt_base = 'http://localhost:8080';
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_encryptor');
}
// GET/POST /tools/birt_proxy/stream_by_code
// Gunakan ini untuk flow browser print yang butuh URL langsung,
// tapi cache PDP harus tetap dihapus segera setelah PDF di-stream.
public function stream_by_code()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = trim($prm['report_code'] ?? $prm['code_report'] ?? $prm['code'] ?? '');
$order_id = intval($prm['PT_OrderHeaderID'] ?? $prm['order_id'] ?? 0);
$payment_id = intval($prm['PPaymentID'] ?? $prm['payment_id'] ?? 0);
if (!$report_code) {
$this->sys_error('report_code wajib diisi');
return;
}
if ($order_id <= 0 && $payment_id > 0) {
$order_id = $this->_resolve_order_id_by_payment($payment_id);
}
if ($payment_id <= 0 && $order_id > 0) {
$payment_id = $this->_resolve_payment_id_by_order($order_id);
}
if ($order_id <= 0) {
$this->sys_error('order_id tidak ditemukan');
return;
}
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name);
if ($url === false) {
$this->_delete_cache($cache_id);
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$full_url = $this->_resolve_fetch_url($url);
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
]
]);
$pdf = @file_get_contents($full_url, false, $context);
$this->_delete_cache($cache_id);
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
// POST /tools/birt_proxy/stream
public function stream()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $prm['report_code'] ?? '';
$order_id = intval($prm['PT_OrderHeaderID'] ?? 0);
$payment_id = intval($prm['PPaymentID'] ?? 0);
if (!$report_code) {
$this->sys_error('report_code wajib diisi');
return;
}
if ($payment_id <= 0 && $order_id > 0) {
$payment_id = $this->_resolve_payment_id_by_order($order_id);
}
$patient_name = '';
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
} else {
$cache_id = null;
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$full_url = $this->_resolve_fetch_url($url);
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
]
]);
$pdf = @file_get_contents($full_url, false, $context);
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
// Hanya return URL (untuk iframe/window.open) — tanpa stream
// Frontend membuka URL ini secara langsung
public function get_url()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $prm['report_code'] ?? '';
$order_id = intval($prm['PT_OrderHeaderID'] ?? 0);
$patient_name = '';
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, 0, $patient_name);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$this->sys_ok(['url' => $url]);
}
// Decrypt patient PII dan simpan ke cache
private function _populate_cache($order_id)
{
// Ambil _enc columns dari m_patient via t_orderheader
$patient = $this->db_onedev->query(
"SELECT M_PatientID,
M_PatientName_enc, M_PatientDOB_enc, M_PatientHP_enc,
M_PatientEmail_enc, M_PatientDOB
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ? LIMIT 1",
[$order_id]
)->row_array();
if (!$patient) return null;
$addr = $this->db_onedev->query(
"SELECT M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ?
AND M_PatientAddressIsActive = 'Y'
AND M_PatientAddressNote = 'Utama'
LIMIT 1",
[$patient['M_PatientID']]
)->row_array();
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($patient['M_PatientName_enc'] ?? '') ?? '';
$dob = $enc->decrypt($patient['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($patient['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($patient['M_PatientHP_enc'] ?? '') ?? '';
$email= $enc->decrypt($patient['M_PatientEmail_enc']?? '') ?? '';
$address = $enc->decrypt($addr['M_PatientAddressDescription_enc'] ?? '') ?? '';
// Hapus cache lama untuk order ini + cleanup expired
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_order_id = ? OR ppc_created < NOW() - INTERVAL 5 MINUTE",
[$order_id]
);
// Insert cache baru
$this->db_onedev->query(
"INSERT INTO patient_print_cache
(ppc_order_id, ppc_patient_id, ppc_name, ppc_dob, ppc_hp, ppc_email, ppc_address, ppc_created)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())",
[$order_id, $patient['M_PatientID'], $name, $dob, $hp, $email, $address]
);
return $this->db_onedev->insert_id();
}
private function _delete_cache($cache_id)
{
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_created < NOW() - INTERVAL 5 MINUTE"
);
}
private function _resolve_fetch_url($url)
{
$url = trim((string) $url);
if ($url === '') {
return '';
}
if (preg_match('#^https?://#i', $url)) {
return $url;
}
if (strpos($url, '/birt/') === 0) {
return $this->birt_base . $url;
}
if (strpos($url, '/one-api-lab/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . $url;
}
if (strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . '/one-api-lab' . $url;
}
return $this->birt_base . $url;
}
private function _resolve_order_id_by_payment($payment_id)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentT_OrderHeaderID
FROM f_payment
WHERE F_PaymentID = ?
LIMIT 1",
[$payment_id]
)->row_array();
return intval($row['F_PaymentT_OrderHeaderID'] ?? 0);
}
private function _resolve_payment_id_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentID
FROM f_payment
WHERE F_PaymentT_OrderHeaderID = ?
ORDER BY F_PaymentID DESC
LIMIT 1",
[$order_id]
)->row_array();
return intval($row['F_PaymentID'] ?? 0);
}
private function _resolve_patient_name_by_cache($cache_id)
{
if (!$cache_id) {
return '';
}
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_id = ?
LIMIT 1",
[$cache_id]
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function _resolve_patient_name_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_order_id = ?
ORDER BY ppc_id DESC
LIMIT 1",
[$order_id]
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function _resolve_patient_name_from_enc_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT M_PatientName_enc
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ?
LIMIT 1",
[$order_id]
)->row_array();
return trim($this->ibl_encryptor->decrypt($row['M_PatientName_enc'] ?? '') ?? '');
}
private function _resolve_report_username()
{
if (!empty($this->sys_user['M_StaffName'])) {
return trim($this->sys_user['M_StaffName']);
}
if (!empty($this->sys_user['M_UserUsername'])) {
return trim($this->sys_user['M_UserUsername']);
}
if (!empty($this->sys_user['userName'])) {
return trim($this->sys_user['userName']);
}
return 'ADMIN';
}
private function _build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name)
{
$row = $this->db_onedev->query(
"SELECT Print_TransactionUrl
FROM print_transaction
WHERE Print_TransactionCode = ?
LIMIT 1",
[$report_code]
)->row_array();
if (!$row) {
return false;
}
$url_template = $this->_apply_report_template_hotfix($report_code, $row['Print_TransactionUrl']);
$username = $this->_resolve_report_username();
$tm = round(microtime(true) * 1000);
$resolved_payment_id = $payment_id > 0 ? $payment_id : $this->_resolve_payment_id_by_order($order_id);
$is_internal_app_url = $this->_is_internal_app_url($url_template);
$replacements = [
'PUsername' => $this->_format_report_string_param($username, $is_internal_app_url),
'PT_OrderHeaderID' => $order_id,
'PPaymentID' => $resolved_payment_id,
'PAn' => $this->_format_report_string_param($patient_name, $is_internal_app_url),
'TS' => $tm,
];
$url = $url_template;
foreach ($replacements as $placeholder => $value) {
if ($value === null) {
$value = '';
}
$url = str_replace($placeholder, $value, $url);
}
return $url;
}
private function _apply_report_template_hotfix($report_code, $url_template)
{
$print_report_hotfix = [
'LAB-RESULT-P-01' => [
'from' => 'rpt_test.rptdesign',
'to' => 'rpt_test_bkp020626.rptdesign',
],
'MIKROO-RESULT-P-01' => [
'from' => 'rpt_test.rptdesign',
'to' => 'rpt_test_bkp020626.rptdesign',
],
];
if (!isset($print_report_hotfix[$report_code])) {
return $url_template;
}
$hotfix = $print_report_hotfix[$report_code];
$resolved_url = str_replace($hotfix['from'], $hotfix['to'], $url_template);
if (strpos($resolved_url, 'username=') === false) {
$resolved_url .= (strpos($resolved_url, '?') === false ? '?' : '&') . 'username=PUsername';
}
return $resolved_url;
}
// GET /tools/birt_proxy/header_json?PID=<order_id>
// Hanya bisa diakses dari localhost (127.0.0.1) — dipanggil oleh BIRT scripted dataset
// Return JSON semua kolom sp_rpt_hasil_header, PII sudah di-decrypt
public function header_json()
{
$order_id = intval($this->input->get('PID') ?? 0);
if ($order_id <= 0) {
echo json_encode(['error' => 'PID required']);
exit;
}
$row = $this->db_onedev->query("
SELECT
DATE_FORMAT(T_OrderHeaderDate, '%d-%m-%Y') AS T_OrderHeaderDate,
T_OrderHeaderLabNumber,
M_TitleName,
M_PatientName,
M_PatientName_enc,
m_sexname AS Gender,
M_PatientNoReg,
M_PatientDOB,
M_PatientDOB_enc,
T_OrderHeaderM_PatientAge,
M_CompanyName AS CorporateName,
M_PatientHp,
M_PatientHP_enc,
M_PatientEmail,
M_PatientEmail_enc,
'' AS M_PatientAddressCity,
'' AS M_PatientAddressState,
M_CompanyName AS CorporateAddress,
M_CompanyEmail AS CorporateEmail,
M_CompanyPhone AS CorporatePhone,
M_CompanyAddressCity AS CorporateAddressCity,
'' AS CorporateAddressState,
TRIM(CONCAT(IFNULL(pj.M_DoctorPrefix,''),' ',IFNULL(pj.M_DoctorPrefix2,''),' ',IFNULL(pj.M_DoctorName,''),' ',IFNULL(pj.M_DoctorSufix,''),' ',IFNULL(pj.M_DoctorSufix2,''))) AS M_DoctorName,
TRIM(CONCAT(IFNULL(pjj.M_DoctorPrefix,''),' ',IFNULL(pjj.M_DoctorPrefix2,''),' ',IFNULL(pjj.M_DoctorName,''),' ',IFNULL(pjj.M_DoctorSufix,''),' ',IFNULL(pjj.M_DoctorSufix2,''))) AS M_DoctorName2,
M_PatientID,
M_PatientNIP, M_PatientJob, M_PatientPosisi, M_PatientDivisi, M_PatientLocation,
CONCAT(IFNULL(M_PatientDepartement,''),' - ',IFNULL(M_PatientNIP,'')) AS M_PatientDepartement
FROM t_orderheader
LEFT JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID AND M_PatientIsActive = 'Y'
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID AND M_TitleIsActive = 'Y'
JOIN m_company ON T_OrderHeaderM_CompanyID = M_CompanyID AND M_CompanyIsActive = 'Y'
LEFT JOIN m_doctor pjj ON T_OrderHeaderPj2M_DoctorID = pjj.M_DoctorID AND pjj.M_DoctorIsActive = 'Y'
LEFT JOIN m_doctor pj ON T_OrderHeaderPjM_DoctorID = pj.M_DoctorID AND pj.M_DoctorIsActive = 'Y'
WHERE T_OrderHeaderID = ? AND T_OrderHeaderIsActive = 'Y'
", [$order_id])->row_array();
if (!$row) {
echo json_encode(['error' => 'order not found']);
exit;
}
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($row['M_PatientName_enc'] ?? '') ?: ($row['M_PatientName'] ?? '');
$dob = $enc->decrypt($row['M_PatientDOB_enc'] ?? '') ?: date('d-m-Y', strtotime($row['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($row['M_PatientHP_enc'] ?? '') ?: ($row['M_PatientHp'] ?? '');
$email= $enc->decrypt($row['M_PatientEmail_enc']?? '') ?: ($row['M_PatientEmail'] ?? '');
$addr_row = $this->db_onedev->query("
SELECT CONCAT(
IFNULL(M_PatientAddressDescription,''),' ',
IFNULL((SELECT regional_nm FROM regional WHERE regional_cd = NULLIF(TRIM(M_PatientAddressRegionalCd),'') LIMIT 1),'')
) AS addr,
M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ? AND M_PatientAddressIsActive = 'Y'
ORDER BY M_PatientAddressID LIMIT 1
", [$row['M_PatientID']])->row_array();
$address = '';
if ($addr_row) {
$address = $enc->decrypt($addr_row['M_PatientAddressDescription_enc'] ?? '') ?: trim($addr_row['addr'] ?? '');
}
$umur = $dob . ' / ' . ($row['T_OrderHeaderM_PatientAge'] ?? '');
$this->_populate_cache($order_id);
$data = [
'T_OrderHeaderDate' => $row['T_OrderHeaderDate'] ?? '',
'T_OrderHeaderLabNumber' => $row['T_OrderHeaderLabNumber'] ?? '',
'M_PatientName' => trim(($row['M_TitleName'] ?? '') . '. ' . $name),
'Gender' => $row['Gender'] ?? '',
'M_PatientNoReg' => $row['M_PatientNoReg'] ?? '',
'M_PatientDOB' => $dob,
'T_OrderHeaderM_PatientAge' => $row['T_OrderHeaderM_PatientAge'] ?? '',
'CorporateName' => $row['CorporateName'] ?? '',
'M_PatientAddress' => $address,
'M_PatientHp' => $hp,
'M_PatientEmail' => $email,
'M_PatientAddressCity' => '',
'M_PatientAddressState' => '',
'CorporateAddress' => $row['CorporateAddress'] ?? '',
'CorporateEmail' => $row['CorporateEmail'] ?? '',
'CorporatePhone' => $row['CorporatePhone'] ?? '',
'CorporateAddressCity' => $row['CorporateAddressCity'] ?? '',
'CorporateAddressState' => '',
'M_DoctorName' => $row['M_DoctorName'] ?? '',
'M_DoctorName2' => $row['M_DoctorName2'] ?? '',
'Umur' => $umur,
'M_PatientNIP' => $row['M_PatientNIP'] ?? '',
'M_PatientJob' => $row['M_PatientJob'] ?? '',
'M_PatientPosisi' => $row['M_PatientPosisi'] ?? '',
'M_PatientDivisi' => $row['M_PatientDivisi'] ?? '',
'M_PatientLocation' => $row['M_PatientLocation'] ?? '',
'M_PatientDepartement' => $row['M_PatientDepartement'] ?? '',
];
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
private function _is_internal_app_url($url)
{
$url = (string) $url;
return (
strpos($url, '/one-api-lab/') === 0 ||
strpos($url, '/tools/') === 0 ||
strpos($url, '/index.php/') === 0
);
}
private function _format_report_string_param($value, $is_internal_app_url = false)
{
$value = (string) $value;
if ($is_internal_app_url) {
return rawurlencode($value);
}
return rawurlencode("'" . $value . "'");
}
}

View File

@@ -0,0 +1,598 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Birt_proxy — PHP proxy untuk semua BIRT report call
*
* Flow:
* 1. Terima request dari frontend (report_code + params)
* 2. Decrypt patient PII dari _enc
* 3. INSERT ke patient_print_cache
* 4. Call BIRT via file_get_contents (internal)
* 5. DELETE cache
* 6. Stream PDF ke frontend
*
* Endpoint: POST /tools/birt_proxy/stream
* Params : report_code, PT_OrderHeaderID, PUsername, (optional) PID_patient
*/
class Birt_proxy extends MY_Controller
{
public $db_onedev;
private $birt_base = 'http://localhost:8080';
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_encryptor');
}
// GET/POST /tools/birt_proxy/stream_by_code
// Gunakan ini untuk flow browser print yang butuh URL langsung,
// tapi cache PDP harus tetap dihapus segera setelah PDF di-stream.
public function stream_by_code()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = trim($prm['report_code'] ?? $prm['code_report'] ?? $prm['code'] ?? '');
$order_id = intval($prm['PT_OrderHeaderID'] ?? $prm['order_id'] ?? 0);
$payment_id = intval($prm['PPaymentID'] ?? $prm['payment_id'] ?? 0);
if (!$report_code) {
$this->sys_error('report_code wajib diisi');
return;
}
if ($order_id <= 0 && $payment_id > 0) {
$order_id = $this->_resolve_order_id_by_payment($payment_id);
}
if ($payment_id <= 0 && $order_id > 0) {
$payment_id = $this->_resolve_payment_id_by_order($order_id);
}
if ($order_id <= 0) {
$this->sys_error('order_id tidak ditemukan');
return;
}
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name);
if ($url === false) {
$this->_delete_cache($cache_id);
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$full_url = $this->_resolve_fetch_url($url);
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
]
]);
$pdf = @file_get_contents($full_url, false, $context);
$this->_delete_cache($cache_id);
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
// POST /tools/birt_proxy/stream
public function stream()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $prm['report_code'] ?? '';
$order_id = intval($prm['PT_OrderHeaderID'] ?? 0);
$payment_id = intval($prm['PPaymentID'] ?? 0);
if (!$report_code) {
$this->sys_error('report_code wajib diisi');
return;
}
if ($payment_id <= 0 && $order_id > 0) {
$payment_id = $this->_resolve_payment_id_by_order($order_id);
}
$patient_name = '';
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
} else {
$cache_id = null;
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$full_url = $this->_resolve_fetch_url($url);
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
]
]);
$pdf = @file_get_contents($full_url, false, $context);
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
// Hanya return URL (untuk iframe/window.open) — tanpa stream
// Frontend membuka URL ini secara langsung
public function get_url()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $prm['report_code'] ?? '';
$order_id = intval($prm['PT_OrderHeaderID'] ?? 0);
$patient_name = '';
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, 0, $patient_name);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$this->sys_ok(['url' => $url]);
}
// Decrypt patient PII dan simpan ke cache
private function _populate_cache($order_id)
{
// Ambil _enc columns dari m_patient via t_orderheader
$patient = $this->db_onedev->query(
"SELECT M_PatientID,
M_PatientName_enc, M_PatientDOB_enc, M_PatientHP_enc,
M_PatientEmail_enc, M_PatientDOB
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ? LIMIT 1",
[$order_id]
)->row_array();
if (!$patient) return null;
$addr = $this->db_onedev->query(
"SELECT M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ?
AND M_PatientAddressIsActive = 'Y'
AND M_PatientAddressNote = 'Utama'
LIMIT 1",
[$patient['M_PatientID']]
)->row_array();
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($patient['M_PatientName_enc'] ?? '') ?? '';
$dob = $enc->decrypt($patient['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($patient['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($patient['M_PatientHP_enc'] ?? '') ?? '';
$email= $enc->decrypt($patient['M_PatientEmail_enc']?? '') ?? '';
$address = $enc->decrypt($addr['M_PatientAddressDescription_enc'] ?? '') ?? '';
// Hapus cache lama untuk order ini + cleanup expired
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_order_id = ? OR ppc_created < NOW() - INTERVAL 5 MINUTE",
[$order_id]
);
// Insert cache baru
$this->db_onedev->query(
"INSERT INTO patient_print_cache
(ppc_order_id, ppc_patient_id, ppc_name, ppc_dob, ppc_hp, ppc_email, ppc_address, ppc_created)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())",
[$order_id, $patient['M_PatientID'], $name, $dob, $hp, $email, $address]
);
return $this->db_onedev->insert_id();
}
private function _delete_cache($cache_id)
{
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_created < NOW() - INTERVAL 5 MINUTE"
);
}
private function _resolve_fetch_url($url)
{
$url = trim((string) $url);
if ($url === '') {
return '';
}
if (preg_match('#^https?://#i', $url)) {
return $url;
}
if (strpos($url, '/birt/') === 0) {
return $this->birt_base . $url;
}
if (strpos($url, '/one-api-lab/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . $url;
}
if (strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . '/one-api-lab' . $url;
}
return $this->birt_base . $url;
}
private function _resolve_order_id_by_payment($payment_id)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentT_OrderHeaderID
FROM f_payment
WHERE F_PaymentID = ?
LIMIT 1",
[$payment_id]
)->row_array();
return intval($row['F_PaymentT_OrderHeaderID'] ?? 0);
}
private function _resolve_payment_id_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentID
FROM f_payment
WHERE F_PaymentT_OrderHeaderID = ?
ORDER BY F_PaymentID DESC
LIMIT 1",
[$order_id]
)->row_array();
return intval($row['F_PaymentID'] ?? 0);
}
private function _resolve_patient_name_by_cache($cache_id)
{
if (!$cache_id) {
return '';
}
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_id = ?
LIMIT 1",
[$cache_id]
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function _resolve_patient_name_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_order_id = ?
ORDER BY ppc_id DESC
LIMIT 1",
[$order_id]
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function _resolve_patient_name_from_enc_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT M_PatientName_enc
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ?
LIMIT 1",
[$order_id]
)->row_array();
return trim($this->ibl_encryptor->decrypt($row['M_PatientName_enc'] ?? '') ?? '');
}
private function _resolve_report_username()
{
if (!empty($this->sys_user['M_StaffName'])) {
return trim($this->sys_user['M_StaffName']);
}
if (!empty($this->sys_user['M_UserUsername'])) {
return trim($this->sys_user['M_UserUsername']);
}
if (!empty($this->sys_user['userName'])) {
return trim($this->sys_user['userName']);
}
return 'ADMIN';
}
private function _build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name)
{
$row = $this->db_onedev->query(
"SELECT Print_TransactionUrl
FROM print_transaction
WHERE Print_TransactionCode = ?
LIMIT 1",
[$report_code]
)->row_array();
if (!$row) {
return false;
}
$url_template = $this->_apply_report_template_hotfix($report_code, $row['Print_TransactionUrl']);
$username = $this->_resolve_report_username();
$tm = round(microtime(true) * 1000);
$resolved_payment_id = $payment_id > 0 ? $payment_id : $this->_resolve_payment_id_by_order($order_id);
$is_internal_app_url = $this->_is_internal_app_url($url_template);
$replacements = [
'PUsername' => $this->_format_report_string_param($username, $is_internal_app_url),
'PT_OrderHeaderID' => $order_id,
'PPaymentID' => $resolved_payment_id,
'PAn' => $this->_format_report_string_param($patient_name, $is_internal_app_url),
'TS' => $tm,
];
$url = $url_template;
foreach ($replacements as $placeholder => $value) {
if ($value === null) {
$value = '';
}
$url = str_replace($placeholder, $value, $url);
}
return $url;
}
private function _apply_report_template_hotfix($report_code, $url_template)
{
$print_report_hotfix = [
'LAB-RESULT-P-01' => [
'from' => 'rpt_test.rptdesign',
'to' => 'rpt_test_bkp020626.rptdesign',
],
'MIKROO-RESULT-P-01' => [
'from' => 'rpt_test.rptdesign',
'to' => 'rpt_test_bkp020626.rptdesign',
],
];
if (!isset($print_report_hotfix[$report_code])) {
return $url_template;
}
$hotfix = $print_report_hotfix[$report_code];
$resolved_url = str_replace($hotfix['from'], $hotfix['to'], $url_template);
if (strpos($resolved_url, 'username=') === false) {
$resolved_url .= (strpos($resolved_url, '?') === false ? '?' : '&') . 'username=PUsername';
}
return $resolved_url;
}
// GET /tools/birt_proxy/header_json?PID=<order_id>
// Hanya bisa diakses dari localhost (127.0.0.1) — dipanggil oleh BIRT scripted dataset
// Return JSON semua kolom sp_rpt_hasil_header, PII sudah di-decrypt
public function header_json()
{
$order_id = intval($this->input->get('PID') ?? 0);
if ($order_id <= 0) {
echo json_encode(['error' => 'PID required']);
exit;
}
$row = $this->db_onedev->query("
SELECT
DATE_FORMAT(T_OrderHeaderDate, '%d-%m-%Y') AS T_OrderHeaderDate,
T_OrderHeaderLabNumber,
M_TitleName,
M_PatientName,
M_PatientName_enc,
m_sexname AS Gender,
M_PatientNoReg,
M_PatientDOB,
M_PatientDOB_enc,
T_OrderHeaderM_PatientAge,
M_CompanyName AS CorporateName,
M_PatientHp,
M_PatientHP_enc,
M_PatientEmail,
M_PatientEmail_enc,
'' AS M_PatientAddressCity,
'' AS M_PatientAddressState,
M_CompanyName AS CorporateAddress,
M_CompanyEmail AS CorporateEmail,
M_CompanyPhone AS CorporatePhone,
M_CompanyAddressCity AS CorporateAddressCity,
'' AS CorporateAddressState,
TRIM(CONCAT(IFNULL(pj.M_DoctorPrefix,''),' ',IFNULL(pj.M_DoctorPrefix2,''),' ',IFNULL(pj.M_DoctorName,''),' ',IFNULL(pj.M_DoctorSufix,''),' ',IFNULL(pj.M_DoctorSufix2,''))) AS M_DoctorName,
TRIM(CONCAT(IFNULL(pjj.M_DoctorPrefix,''),' ',IFNULL(pjj.M_DoctorPrefix2,''),' ',IFNULL(pjj.M_DoctorName,''),' ',IFNULL(pjj.M_DoctorSufix,''),' ',IFNULL(pjj.M_DoctorSufix2,''))) AS M_DoctorName2,
M_PatientID,
M_PatientNIP, M_PatientJob, M_PatientPosisi, M_PatientDivisi, M_PatientLocation,
CONCAT(IFNULL(M_PatientDepartement,''),' - ',IFNULL(M_PatientNIP,'')) AS M_PatientDepartement
FROM t_orderheader
LEFT JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID AND M_PatientIsActive = 'Y'
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID AND M_TitleIsActive = 'Y'
JOIN m_company ON T_OrderHeaderM_CompanyID = M_CompanyID AND M_CompanyIsActive = 'Y'
LEFT JOIN m_doctor pjj ON T_OrderHeaderPj2M_DoctorID = pjj.M_DoctorID AND pjj.M_DoctorIsActive = 'Y'
LEFT JOIN m_doctor pj ON T_OrderHeaderPjM_DoctorID = pj.M_DoctorID AND pj.M_DoctorIsActive = 'Y'
WHERE T_OrderHeaderID = ? AND T_OrderHeaderIsActive = 'Y'
", [$order_id])->row_array();
if (!$row) {
echo json_encode(['error' => 'order not found']);
exit;
}
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($row['M_PatientName_enc'] ?? '') ?: ($row['M_PatientName'] ?? '');
$dob = $enc->decrypt($row['M_PatientDOB_enc'] ?? '') ?: date('d-m-Y', strtotime($row['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($row['M_PatientHP_enc'] ?? '') ?: ($row['M_PatientHp'] ?? '');
$email= $enc->decrypt($row['M_PatientEmail_enc']?? '') ?: ($row['M_PatientEmail'] ?? '');
$addr_row = $this->db_onedev->query("
SELECT CONCAT(
IFNULL(M_PatientAddressDescription,''),' ',
IFNULL((SELECT regional_nm FROM regional WHERE regional_cd = NULLIF(TRIM(M_PatientAddressRegionalCd),'') LIMIT 1),'')
) AS addr,
M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ? AND M_PatientAddressIsActive = 'Y'
ORDER BY M_PatientAddressID LIMIT 1
", [$row['M_PatientID']])->row_array();
$address = '';
if ($addr_row) {
$address = $enc->decrypt($addr_row['M_PatientAddressDescription_enc'] ?? '') ?: trim($addr_row['addr'] ?? '');
}
$umur = $dob . ' / ' . ($row['T_OrderHeaderM_PatientAge'] ?? '');
$data = [
'T_OrderHeaderDate' => $row['T_OrderHeaderDate'] ?? '',
'T_OrderHeaderLabNumber' => $row['T_OrderHeaderLabNumber'] ?? '',
'M_PatientName' => trim(($row['M_TitleName'] ?? '') . '. ' . $name),
'Gender' => $row['Gender'] ?? '',
'M_PatientNoReg' => $row['M_PatientNoReg'] ?? '',
'M_PatientDOB' => $dob,
'T_OrderHeaderM_PatientAge' => $row['T_OrderHeaderM_PatientAge'] ?? '',
'CorporateName' => $row['CorporateName'] ?? '',
'M_PatientAddress' => $address,
'M_PatientHp' => $hp,
'M_PatientEmail' => $email,
'M_PatientAddressCity' => '',
'M_PatientAddressState' => '',
'CorporateAddress' => $row['CorporateAddress'] ?? '',
'CorporateEmail' => $row['CorporateEmail'] ?? '',
'CorporatePhone' => $row['CorporatePhone'] ?? '',
'CorporateAddressCity' => $row['CorporateAddressCity'] ?? '',
'CorporateAddressState' => '',
'M_DoctorName' => $row['M_DoctorName'] ?? '',
'M_DoctorName2' => $row['M_DoctorName2'] ?? '',
'Umur' => $umur,
'M_PatientNIP' => $row['M_PatientNIP'] ?? '',
'M_PatientJob' => $row['M_PatientJob'] ?? '',
'M_PatientPosisi' => $row['M_PatientPosisi'] ?? '',
'M_PatientDivisi' => $row['M_PatientDivisi'] ?? '',
'M_PatientLocation' => $row['M_PatientLocation'] ?? '',
'M_PatientDepartement' => $row['M_PatientDepartement'] ?? '',
];
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
private function _is_internal_app_url($url)
{
$url = (string) $url;
return (
strpos($url, '/one-api-lab/') === 0 ||
strpos($url, '/tools/') === 0 ||
strpos($url, '/index.php/') === 0
);
}
private function _format_report_string_param($value, $is_internal_app_url = false)
{
$value = (string) $value;
if ($is_internal_app_url) {
return rawurlencode($value);
}
return rawurlencode("'" . $value . "'");
}
}

View File

@@ -0,0 +1,598 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Birt_proxy — PHP proxy untuk semua BIRT report call
*
* Flow:
* 1. Terima request dari frontend (report_code + params)
* 2. Decrypt patient PII dari _enc
* 3. INSERT ke patient_print_cache
* 4. Call BIRT via file_get_contents (internal)
* 5. DELETE cache
* 6. Stream PDF ke frontend
*
* Endpoint: POST /tools/birt_proxy/stream
* Params : report_code, PT_OrderHeaderID, PUsername, (optional) PID_patient
*/
class Birt_proxy extends MY_Controller
{
public $db_onedev;
private $birt_base = 'http://localhost:8080';
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_encryptor');
}
// GET/POST /tools/birt_proxy/stream_by_code
// Gunakan ini untuk flow browser print yang butuh URL langsung,
// tapi cache PDP harus tetap dihapus segera setelah PDF di-stream.
public function stream_by_code()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = trim($prm['report_code'] ?? $prm['code_report'] ?? $prm['code'] ?? '');
$order_id = intval($prm['PT_OrderHeaderID'] ?? $prm['order_id'] ?? 0);
$payment_id = intval($prm['PPaymentID'] ?? $prm['payment_id'] ?? 0);
if (!$report_code) {
$this->sys_error('report_code wajib diisi');
return;
}
if ($order_id <= 0 && $payment_id > 0) {
$order_id = $this->_resolve_order_id_by_payment($payment_id);
}
if ($payment_id <= 0 && $order_id > 0) {
$payment_id = $this->_resolve_payment_id_by_order($order_id);
}
if ($order_id <= 0) {
$this->sys_error('order_id tidak ditemukan');
return;
}
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name);
if ($url === false) {
$this->_delete_cache($cache_id);
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$full_url = $this->_resolve_fetch_url($url);
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
]
]);
$pdf = @file_get_contents($full_url, false, $context);
$this->_delete_cache($cache_id);
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
// POST /tools/birt_proxy/stream
public function stream()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $prm['report_code'] ?? '';
$order_id = intval($prm['PT_OrderHeaderID'] ?? 0);
$payment_id = intval($prm['PPaymentID'] ?? 0);
if (!$report_code) {
$this->sys_error('report_code wajib diisi');
return;
}
if ($payment_id <= 0 && $order_id > 0) {
$payment_id = $this->_resolve_payment_id_by_order($order_id);
}
$patient_name = '';
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
} else {
$cache_id = null;
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$full_url = $this->_resolve_fetch_url($url);
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
]
]);
$pdf = @file_get_contents($full_url, false, $context);
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
// Hanya return URL (untuk iframe/window.open) — tanpa stream
// Frontend membuka URL ini secara langsung
public function get_url()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $prm['report_code'] ?? '';
$order_id = intval($prm['PT_OrderHeaderID'] ?? 0);
$patient_name = '';
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
$patient_name = $this->_resolve_patient_name_by_cache($cache_id);
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_by_order($order_id);
}
if ($patient_name === '') {
$patient_name = $this->_resolve_patient_name_from_enc_by_order($order_id);
}
}
$url = $this->_build_birt_url_by_code($report_code, $order_id, 0, $patient_name);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$this->sys_ok(['url' => $url]);
}
// Decrypt patient PII dan simpan ke cache
private function _populate_cache($order_id)
{
// Ambil _enc columns dari m_patient via t_orderheader
$patient = $this->db_onedev->query(
"SELECT M_PatientID,
M_PatientName_enc, M_PatientDOB_enc, M_PatientHP_enc,
M_PatientEmail_enc, M_PatientDOB
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ? LIMIT 1",
[$order_id]
)->row_array();
if (!$patient) return null;
$addr = $this->db_onedev->query(
"SELECT M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ?
AND M_PatientAddressIsActive = 'Y'
AND M_PatientAddressNote = 'Utama'
LIMIT 1",
[$patient['M_PatientID']]
)->row_array();
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($patient['M_PatientName_enc'] ?? '') ?? '';
$dob = $enc->decrypt($patient['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($patient['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($patient['M_PatientHP_enc'] ?? '') ?? '';
$email= $enc->decrypt($patient['M_PatientEmail_enc']?? '') ?? '';
$address = $enc->decrypt($addr['M_PatientAddressDescription_enc'] ?? '') ?? '';
// Hapus cache lama untuk order ini + cleanup expired
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_order_id = ? OR ppc_created < NOW() - INTERVAL 5 MINUTE",
[$order_id]
);
// Insert cache baru
$this->db_onedev->query(
"INSERT INTO patient_print_cache
(ppc_order_id, ppc_patient_id, ppc_name, ppc_dob, ppc_hp, ppc_email, ppc_address, ppc_created)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())",
[$order_id, $patient['M_PatientID'], $name, $dob, $hp, $email, $address]
);
return $this->db_onedev->insert_id();
}
private function _delete_cache($cache_id)
{
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_created < NOW() - INTERVAL 5 MINUTE"
);
}
private function _resolve_fetch_url($url)
{
$url = trim((string) $url);
if ($url === '') {
return '';
}
if (preg_match('#^https?://#i', $url)) {
return $url;
}
if (strpos($url, '/birt/') === 0) {
return $this->birt_base . $url;
}
if (strpos($url, '/one-api-lab/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . $url;
}
if (strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . '/one-api-lab' . $url;
}
return $this->birt_base . $url;
}
private function _resolve_order_id_by_payment($payment_id)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentT_OrderHeaderID
FROM f_payment
WHERE F_PaymentID = ?
LIMIT 1",
[$payment_id]
)->row_array();
return intval($row['F_PaymentT_OrderHeaderID'] ?? 0);
}
private function _resolve_payment_id_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT F_PaymentID
FROM f_payment
WHERE F_PaymentT_OrderHeaderID = ?
ORDER BY F_PaymentID DESC
LIMIT 1",
[$order_id]
)->row_array();
return intval($row['F_PaymentID'] ?? 0);
}
private function _resolve_patient_name_by_cache($cache_id)
{
if (!$cache_id) {
return '';
}
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_id = ?
LIMIT 1",
[$cache_id]
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function _resolve_patient_name_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_order_id = ?
ORDER BY ppc_id DESC
LIMIT 1",
[$order_id]
)->row_array();
return trim($row['ppc_name'] ?? '');
}
private function _resolve_patient_name_from_enc_by_order($order_id)
{
$row = $this->db_onedev->query(
"SELECT M_PatientName_enc
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ?
LIMIT 1",
[$order_id]
)->row_array();
return trim($this->ibl_encryptor->decrypt($row['M_PatientName_enc'] ?? '') ?? '');
}
private function _resolve_report_username()
{
if (!empty($this->sys_user['M_StaffName'])) {
return trim($this->sys_user['M_StaffName']);
}
if (!empty($this->sys_user['M_UserUsername'])) {
return trim($this->sys_user['M_UserUsername']);
}
if (!empty($this->sys_user['userName'])) {
return trim($this->sys_user['userName']);
}
return 'ADMIN';
}
private function _build_birt_url_by_code($report_code, $order_id, $payment_id, $patient_name)
{
$row = $this->db_onedev->query(
"SELECT Print_TransactionUrl
FROM print_transaction
WHERE Print_TransactionCode = ?
LIMIT 1",
[$report_code]
)->row_array();
if (!$row) {
return false;
}
$url_template = $this->_apply_report_template_hotfix($report_code, $row['Print_TransactionUrl']);
$username = $this->_resolve_report_username();
$tm = round(microtime(true) * 1000);
$resolved_payment_id = $payment_id > 0 ? $payment_id : $this->_resolve_payment_id_by_order($order_id);
$is_internal_app_url = $this->_is_internal_app_url($url_template);
$replacements = [
'PUsername' => $this->_format_report_string_param($username, $is_internal_app_url),
'PT_OrderHeaderID' => $order_id,
'PPaymentID' => $resolved_payment_id,
'PAn' => $this->_format_report_string_param($patient_name, $is_internal_app_url),
'TS' => $tm,
];
$url = $url_template;
foreach ($replacements as $placeholder => $value) {
if ($value === null) {
$value = '';
}
$url = str_replace($placeholder, $value, $url);
}
return $url;
}
private function _apply_report_template_hotfix($report_code, $url_template)
{
$print_report_hotfix = [
'LAB-RESULT-P-01' => [
'from' => 'rpt_test.rptdesign',
'to' => 'rpt_test_bkp020626.rptdesign',
],
'MIKROO-RESULT-P-01' => [
'from' => 'rpt_test.rptdesign',
'to' => 'rpt_test_bkp020626.rptdesign',
],
];
if (!isset($print_report_hotfix[$report_code])) {
return $url_template;
}
$hotfix = $print_report_hotfix[$report_code];
$resolved_url = str_replace($hotfix['from'], $hotfix['to'], $url_template);
if (strpos($resolved_url, 'username=') === false) {
$resolved_url .= (strpos($resolved_url, '?') === false ? '?' : '&') . 'username=PUsername';
}
return $resolved_url;
}
// GET /tools/birt_proxy/header_json?PID=<order_id>
// Hanya bisa diakses dari localhost (127.0.0.1) — dipanggil oleh BIRT scripted dataset
// Return JSON semua kolom sp_rpt_hasil_header, PII sudah di-decrypt
public function header_json()
{
$order_id = intval($this->input->get('PID') ?? 0);
if ($order_id <= 0) {
echo json_encode(['error' => 'PID required']);
exit;
}
$row = $this->db_onedev->query("
SELECT
DATE_FORMAT(T_OrderHeaderDate, '%d-%m-%Y') AS T_OrderHeaderDate,
T_OrderHeaderLabNumber,
M_TitleName,
M_PatientName,
M_PatientName_enc,
m_sexname AS Gender,
M_PatientNoReg,
M_PatientDOB,
M_PatientDOB_enc,
T_OrderHeaderM_PatientAge,
M_CompanyName AS CorporateName,
M_PatientHp,
M_PatientHP_enc,
M_PatientEmail,
M_PatientEmail_enc,
'' AS M_PatientAddressCity,
'' AS M_PatientAddressState,
M_CompanyName AS CorporateAddress,
M_CompanyEmail AS CorporateEmail,
M_CompanyPhone AS CorporatePhone,
M_CompanyAddressCity AS CorporateAddressCity,
'' AS CorporateAddressState,
TRIM(CONCAT(IFNULL(pj.M_DoctorPrefix,''),' ',IFNULL(pj.M_DoctorPrefix2,''),' ',IFNULL(pj.M_DoctorName,''),' ',IFNULL(pj.M_DoctorSufix,''),' ',IFNULL(pj.M_DoctorSufix2,''))) AS M_DoctorName,
TRIM(CONCAT(IFNULL(pjj.M_DoctorPrefix,''),' ',IFNULL(pjj.M_DoctorPrefix2,''),' ',IFNULL(pjj.M_DoctorName,''),' ',IFNULL(pjj.M_DoctorSufix,''),' ',IFNULL(pjj.M_DoctorSufix2,''))) AS M_DoctorName2,
M_PatientID,
M_PatientNIP, M_PatientJob, M_PatientPosisi, M_PatientDivisi, M_PatientLocation,
CONCAT(IFNULL(M_PatientDepartement,''),' - ',IFNULL(M_PatientNIP,'')) AS M_PatientDepartement
FROM t_orderheader
LEFT JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID AND M_PatientIsActive = 'Y'
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID AND M_TitleIsActive = 'Y'
JOIN m_company ON T_OrderHeaderM_CompanyID = M_CompanyID AND M_CompanyIsActive = 'Y'
LEFT JOIN m_doctor pjj ON T_OrderHeaderPj2M_DoctorID = pjj.M_DoctorID AND pjj.M_DoctorIsActive = 'Y'
LEFT JOIN m_doctor pj ON T_OrderHeaderPjM_DoctorID = pj.M_DoctorID AND pj.M_DoctorIsActive = 'Y'
WHERE T_OrderHeaderID = ? AND T_OrderHeaderIsActive = 'Y'
", [$order_id])->row_array();
if (!$row) {
echo json_encode(['error' => 'order not found']);
exit;
}
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($row['M_PatientName_enc'] ?? '') ?: ($row['M_PatientName'] ?? '');
$dob = $enc->decrypt($row['M_PatientDOB_enc'] ?? '') ?: date('d-m-Y', strtotime($row['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($row['M_PatientHP_enc'] ?? '') ?: ($row['M_PatientHp'] ?? '');
$email= $enc->decrypt($row['M_PatientEmail_enc']?? '') ?: ($row['M_PatientEmail'] ?? '');
$addr_row = $this->db_onedev->query("
SELECT CONCAT(
IFNULL(M_PatientAddressDescription,''),' ',
IFNULL((SELECT regional_nm FROM regional WHERE regional_cd = NULLIF(TRIM(M_PatientAddressRegionalCd),'') LIMIT 1),'')
) AS addr,
M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ? AND M_PatientAddressIsActive = 'Y'
ORDER BY M_PatientAddressID LIMIT 1
", [$row['M_PatientID']])->row_array();
$address = '';
if ($addr_row) {
$address = $enc->decrypt($addr_row['M_PatientAddressDescription_enc'] ?? '') ?: trim($addr_row['addr'] ?? '');
}
$umur = $dob . ' / ' . ($row['T_OrderHeaderM_PatientAge'] ?? '');
$data = [
'T_OrderHeaderDate' => $row['T_OrderHeaderDate'] ?? '',
'T_OrderHeaderLabNumber' => $row['T_OrderHeaderLabNumber'] ?? '',
'M_PatientName' => trim(($row['M_TitleName'] ?? '') . '. ' . $name),
'Gender' => $row['Gender'] ?? '',
'M_PatientNoReg' => $row['M_PatientNoReg'] ?? '',
'M_PatientDOB' => $dob,
'T_OrderHeaderM_PatientAge' => $row['T_OrderHeaderM_PatientAge'] ?? '',
'CorporateName' => $row['CorporateName'] ?? '',
'M_PatientAddress' => $address,
'M_PatientHp' => $hp,
'M_PatientEmail' => $email,
'M_PatientAddressCity' => '',
'M_PatientAddressState' => '',
'CorporateAddress' => $row['CorporateAddress'] ?? '',
'CorporateEmail' => $row['CorporateEmail'] ?? '',
'CorporatePhone' => $row['CorporatePhone'] ?? '',
'CorporateAddressCity' => $row['CorporateAddressCity'] ?? '',
'CorporateAddressState' => '',
'M_DoctorName' => $row['M_DoctorName'] ?? '',
'M_DoctorName2' => $row['M_DoctorName2'] ?? '',
'Umur' => $umur,
'M_PatientNIP' => $row['M_PatientNIP'] ?? '',
'M_PatientJob' => $row['M_PatientJob'] ?? '',
'M_PatientPosisi' => $row['M_PatientPosisi'] ?? '',
'M_PatientDivisi' => $row['M_PatientDivisi'] ?? '',
'M_PatientLocation' => $row['M_PatientLocation'] ?? '',
'M_PatientDepartement' => $row['M_PatientDepartement'] ?? '',
];
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
private function _is_internal_app_url($url)
{
$url = (string) $url;
return (
strpos($url, '/one-api-lab/') === 0 ||
strpos($url, '/tools/') === 0 ||
strpos($url, '/index.php/') === 0
);
}
private function _format_report_string_param($value, $is_internal_app_url = false)
{
$value = (string) $value;
if ($is_internal_app_url) {
return rawurlencode($value);
}
return rawurlencode("'" . $value . "'");
}
}

View File

@@ -0,0 +1,450 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* BIRT proxy untuk report nonlab.
*
* Endpoint:
* - GET/POST /tools/birt_proxy_nonlab/stream_by_code
* - POST /tools/birt_proxy_nonlab/stream
* - GET/POST /tools/birt_proxy_nonlab/get_url
* - GET /tools/birt_proxy_nonlab/header_json
*
* Params:
* - report_code, code_report, atau code. Default: RONTGEN-RESULT-P-01
* - PID, PSo_ResultEntryID, So_ResultEntryID, result_entry_id, atau PT_OrderHeaderID
*/
class Birt_proxy_nonlab extends MY_Controller
{
public $db_onedev;
private $birt_base = 'http://localhost:8080';
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_encryptor');
}
public function stream_by_code()
{
$this->_stream_by_code();
}
public function stream()
{
$this->_stream_by_code();
}
public function get_url()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $this->_resolve_report_code($prm);
$pid = $this->_resolve_pid($prm);
if ($pid <= 0) {
$this->sys_error('PID wajib diisi');
return;
}
$order_id = $this->_resolve_order_id_by_result_entry($pid);
if ($order_id > 0) {
$this->_populate_cache($order_id);
}
$url = $this->_build_birt_url_by_code($report_code, $pid);
if ($url === false) {
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$this->sys_ok(['url' => $url]);
}
public function header_json()
{
$pid = intval($this->input->get('PID') ?? $this->input->get('So_ResultEntryID') ?? 0);
if ($pid <= 0) {
echo json_encode(['error' => 'PID required']);
exit;
}
$row = $this->db_onedev->query("
SELECT
T_OrderHeaderID,
DATE_FORMAT(T_OrderHeaderDate, '%d-%m-%Y') AS T_OrderHeaderDate,
T_OrderHeaderLabNumber,
M_TitleName,
M_PatientName,
M_PatientName_enc,
m_sexname AS Gender,
M_PatientNoReg,
M_PatientDOB,
M_PatientDOB_enc,
T_OrderHeaderM_PatientAge,
M_CompanyName AS CorporateName,
M_PatientHp,
M_PatientHP_enc,
M_PatientEmail,
M_PatientEmail_enc,
M_CompanyAddress AS CorporateAddress,
M_CompanyEmail AS CorporateEmail,
M_CompanyPhone AS CorporatePhone,
M_CompanyAddressCity AS CorporateAddressCity,
TRIM(CONCAT(IFNULL(pj.M_DoctorPrefix, ''), ' ', IFNULL(pj.M_DoctorPrefix2, ''), ' ', IFNULL(pj.M_DoctorName, ''), ' ', IFNULL(pj.M_DoctorSufix, ''), ' ', IFNULL(pj.M_DoctorSufix2, ''))) AS M_DoctorName,
TRIM(CONCAT(IFNULL(pjj.M_DoctorPrefix, ''), ' ', IFNULL(pjj.M_DoctorPrefix2, ''), ' ', IFNULL(pjj.M_DoctorName, ''), ' ', IFNULL(pjj.M_DoctorSufix, ''), ' ', IFNULL(pjj.M_DoctorSufix2, ''))) AS M_DoctorName2,
M_PatientID,
M_PatientNIP,
M_PatientJob,
M_PatientPosisi,
M_PatientDivisi,
M_PatientLocation,
CONCAT(IFNULL(M_PatientDepartement, ''), ' - ', IFNULL(M_PatientNIP, '')) AS M_PatientDepartement
FROM t_orderheader
JOIN so_resultentry ON So_ResultEntryT_OrderHeaderID = T_OrderHeaderID
AND So_ResultEntryIsActive = 'Y'
LEFT JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
AND M_PatientIsActive = 'Y'
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
AND M_TitleIsActive = 'Y'
LEFT JOIN m_company ON T_OrderHeaderM_CompanyID = M_CompanyID
AND M_CompanyIsActive = 'Y'
LEFT JOIN m_sex ON M_PatientM_SexID = M_SexID
LEFT JOIN m_doctor pjj ON T_OrderHeaderPj2M_DoctorID = pjj.M_DoctorID
AND pjj.M_DoctorIsActive = 'Y'
LEFT JOIN m_doctor pj ON T_OrderHeaderPjM_DoctorID = pj.M_DoctorID
AND pj.M_DoctorIsActive = 'Y'
WHERE So_ResultEntryID = ?
AND T_OrderHeaderIsActive = 'Y'
GROUP BY T_OrderHeaderID
LIMIT 1
", [$pid])->row_array();
if (!$row) {
echo json_encode(['error' => 'result entry not found']);
exit;
}
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($row['M_PatientName_enc'] ?? '') ?: ($row['M_PatientName'] ?? '');
$dob = $enc->decrypt($row['M_PatientDOB_enc'] ?? '') ?: date('d-m-Y', strtotime($row['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($row['M_PatientHP_enc'] ?? '') ?: ($row['M_PatientHp'] ?? '');
$email = $enc->decrypt($row['M_PatientEmail_enc'] ?? '') ?: ($row['M_PatientEmail'] ?? '');
$addr_row = $this->db_onedev->query("
SELECT
CONCAT(
IFNULL(M_PatientAddressDescription, ''), ' ',
IFNULL(M_DistrictName, ''), ' ',
IFNULL(M_CityName, '')
) AS addr,
M_PatientAddressDescription_enc
FROM m_patientaddress AS p
LEFT JOIN (
SELECT regional_cd, regional_nm AS M_KelurahanName, pro_cd, kab_cd, kec_cd
FROM regional
) reg_kel ON NULLIF(TRIM(p.M_PatientAddressRegionalCd), '') = reg_kel.regional_cd
LEFT JOIN (
SELECT regional_cd, regional_nm AS M_DistrictName
FROM regional
) reg_kec ON CONCAT(reg_kel.pro_cd, reg_kel.kab_cd, reg_kel.kec_cd, '000') = reg_kec.regional_cd
LEFT JOIN (
SELECT regional_cd, regional_nm AS M_CityName
FROM regional
) reg_kab ON CONCAT(reg_kel.pro_cd, reg_kel.kab_cd, '000000') = reg_kab.regional_cd
WHERE M_PatientAddressM_PatientID = ?
AND M_PatientAddressIsActive = 'Y'
ORDER BY (M_PatientAddressNote = 'Utama') DESC, M_PatientAddressID
LIMIT 1
", [$row['M_PatientID']])->row_array();
$address = '';
if ($addr_row) {
$address = $enc->decrypt($addr_row['M_PatientAddressDescription_enc'] ?? '') ?: trim($addr_row['addr'] ?? '');
}
if (!empty($row['T_OrderHeaderID'])) {
$this->_populate_cache((int) $row['T_OrderHeaderID']);
}
$data = [
'T_OrderHeaderID' => (int) ($row['T_OrderHeaderID'] ?? 0),
'T_OrderHeaderDate' => $row['T_OrderHeaderDate'] ?? '',
'T_OrderHeaderLabNumber' => $row['T_OrderHeaderLabNumber'] ?? '',
'M_PatientName' => trim(($row['M_TitleName'] ?? '') . '. ' . $name),
'Gender' => $row['Gender'] ?? '',
'M_PatientNoReg' => $row['M_PatientNoReg'] ?? '',
'M_PatientDOB' => $dob,
'T_OrderHeaderM_PatientAge' => $row['T_OrderHeaderM_PatientAge'] ?? '',
'CorporateName' => $row['CorporateName'] ?? '',
'M_PatientAddress' => $address,
'M_PatientHp' => $hp,
'M_PatientEmail' => $email,
'M_PatientAddressCity' => '',
'M_PatientAddressState' => $address,
'CorporateAddress' => $row['CorporateAddress'] ?? '',
'CorporateEmail' => $row['CorporateEmail'] ?? '',
'CorporatePhone' => $row['CorporatePhone'] ?? '',
'CorporateAddressCity' => $row['CorporateAddressCity'] ?? '',
'CorporateAddressState' => '',
'M_DoctorName' => $row['M_DoctorName'] ?? '',
'M_DoctorName2' => $row['M_DoctorName2'] ?? '',
'Umur' => $dob . ' / ' . ($row['T_OrderHeaderM_PatientAge'] ?? ''),
'M_PatientNIP' => $row['M_PatientNIP'] ?? '',
'M_PatientJob' => $row['M_PatientJob'] ?? '',
'M_PatientPosisi' => $row['M_PatientPosisi'] ?? '',
'M_PatientDivisi' => $row['M_PatientDivisi'] ?? '',
'M_PatientLocation' => $row['M_PatientLocation'] ?? '',
'M_PatientDepartement' => $row['M_PatientDepartement'] ?? '',
];
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
private function _stream_by_code()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
return;
}
$prm = $this->sys_input;
$report_code = $this->_resolve_report_code($prm);
$pid = $this->_resolve_pid($prm);
if ($pid <= 0) {
$this->sys_error('PID wajib diisi');
return;
}
$cache_id = null;
$order_id = $this->_resolve_order_id_by_result_entry($pid);
if ($order_id > 0) {
$cache_id = $this->_populate_cache($order_id);
}
$url = $this->_build_birt_url_by_code($report_code, $pid);
if ($url === false) {
$this->_delete_cache($cache_id);
$this->sys_error("Report code tidak ditemukan: {$report_code}");
return;
}
$context = stream_context_create([
'http' => [
'timeout' => 120,
'method' => 'GET',
],
]);
$pdf = @file_get_contents($this->_resolve_fetch_url($url), false, $context);
$this->_delete_cache($cache_id);
if ($pdf === false) {
$this->sys_error('Gagal generate report dari BIRT server');
return;
}
$filename = $report_code . '_' . $pid . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
private function _resolve_report_code($prm)
{
$report_code = trim($prm['report_code'] ?? $prm['code_report'] ?? $prm['code'] ?? '');
return $report_code !== '' ? $report_code : 'RONTGEN-RESULT-P-01';
}
private function _resolve_pid($prm)
{
return intval(
$prm['PID'] ??
$prm['PSo_ResultEntryID'] ??
$prm['So_ResultEntryID'] ??
$prm['result_entry_id'] ??
$prm['PT_OrderHeaderID'] ??
0
);
}
private function _build_birt_url_by_code($report_code, $pid)
{
$row = $this->db_onedev->query(
"SELECT Print_TransactionUrl
FROM print_transaction
WHERE Print_TransactionCode = ?
LIMIT 1",
[$report_code]
)->row_array();
if (!$row) {
return false;
}
$username = $this->_resolve_report_username();
$tm = round(microtime(true) * 1000);
$url = $row['Print_TransactionUrl'];
$replacements = [
'PUsername' => $this->_format_report_string_param($username),
'PT_OrderHeaderID' => $pid,
'PSo_ResultEntryID' => $pid,
'TS' => $tm,
];
foreach ($replacements as $placeholder => $value) {
$url = str_replace($placeholder, $value, $url);
}
$url = preg_replace('/([?&]PID=)PID([&#]|$)/', '${1}' . $pid . '$2', $url);
return $url;
}
private function _resolve_fetch_url($url)
{
$url = trim((string) $url);
if ($url === '') {
return '';
}
if (preg_match('#^https?://#i', $url)) {
return $url;
}
if (strpos($url, '/birt/') === 0) {
return $this->birt_base . $url;
}
if (strpos($url, '/one-api-lab/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . $url;
}
if (strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0) {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $scheme . '://' . $host . '/one-api-lab' . $url;
}
return $this->birt_base . $url;
}
private function _resolve_order_id_by_result_entry($result_entry_id)
{
$row = $this->db_onedev->query(
"SELECT So_ResultEntryT_OrderHeaderID
FROM so_resultentry
WHERE So_ResultEntryID = ?
AND So_ResultEntryIsActive = 'Y'
LIMIT 1",
[$result_entry_id]
)->row_array();
return intval($row['So_ResultEntryT_OrderHeaderID'] ?? 0);
}
private function _populate_cache($order_id)
{
$patient = $this->db_onedev->query(
"SELECT M_PatientID,
M_PatientName_enc, M_PatientDOB_enc, M_PatientHP_enc,
M_PatientEmail_enc, M_PatientDOB
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ?
AND T_OrderHeaderIsActive = 'Y'
LIMIT 1",
[$order_id]
)->row_array();
if (!$patient) {
return null;
}
$addr = $this->db_onedev->query(
"SELECT M_PatientAddressDescription_enc
FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ?
AND M_PatientAddressIsActive = 'Y'
ORDER BY (M_PatientAddressNote = 'Utama') DESC, M_PatientAddressID
LIMIT 1",
[$patient['M_PatientID']]
)->row_array();
$enc = $this->ibl_encryptor;
$name = $enc->decrypt($patient['M_PatientName_enc'] ?? '') ?? '';
$dob = $enc->decrypt($patient['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($patient['M_PatientDOB'] ?? 'now'));
$hp = $enc->decrypt($patient['M_PatientHP_enc'] ?? '') ?? '';
$email = $enc->decrypt($patient['M_PatientEmail_enc'] ?? '') ?? '';
$address = $enc->decrypt($addr['M_PatientAddressDescription_enc'] ?? '') ?? '';
$this->db_onedev->query(
"DELETE FROM patient_print_cache
WHERE ppc_order_id = ?
OR ppc_created < NOW() - INTERVAL 5 MINUTE",
[$order_id]
);
$this->db_onedev->query(
"INSERT INTO patient_print_cache
(ppc_order_id, ppc_patient_id, ppc_name, ppc_dob, ppc_hp, ppc_email, ppc_address, ppc_created)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())",
[$order_id, $patient['M_PatientID'], $name, $dob, $hp, $email, $address]
);
return $this->db_onedev->insert_id();
}
private function _delete_cache($cache_id)
{
if ($cache_id) {
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_id = ?",
[$cache_id]
);
}
$this->db_onedev->query(
"DELETE FROM patient_print_cache WHERE ppc_created < NOW() - INTERVAL 5 MINUTE"
);
}
private function _resolve_report_username()
{
if (!empty($this->sys_user['M_StaffName'])) {
return trim($this->sys_user['M_StaffName']);
}
if (!empty($this->sys_user['M_UserUsername'])) {
return trim($this->sys_user['M_UserUsername']);
}
if (!empty($this->sys_user['userName'])) {
return trim($this->sys_user['userName']);
}
return 'ADMIN';
}
private function _format_report_string_param($value)
{
return rawurlencode("'" . (string) $value . "'");
}
}

View File

@@ -16,6 +16,15 @@ Content-Type: application/json
"T_OrderHeaderLabNumber": "{{lab_number}}"
}
### Merge langsung dari qr_printout (T_OrderHeaderID)
POST http://10.9.20.31/one-api-lab/tools/ibl_merge_report_admin/merge_from_qr
Content-Type: application/json
{
"token": "{{token}}",
"T_OrderHeaderID": {{order_header_id}}
}
### Admin preview via backend tools
POST http://10.9.20.31/one-api-lab/tools/ibl_merge_report_admin/preview
Content-Type: application/json

View File

@@ -47,6 +47,37 @@ class Ibl_merge_report_admin extends MY_Controller
exit;
}
public function merge_from_qr()
{
if (!$this->isLogin) {
$this->sys_error('Invalid Token');
exit;
}
$auth = $this->ibl_merge_report_gateway->is_admin_group_allowed($this->sys_user['M_UserID']);
if ($auth['status'] !== 'OK') {
$this->sys_error($auth['code'] . ' - ' . $auth['message']);
exit;
}
$orderHeaderId = isset($this->sys_input['T_OrderHeaderID']) ? (int) $this->sys_input['T_OrderHeaderID'] : 0;
if ($orderHeaderId <= 0) {
$this->sys_error('T_ORDERHEADERID_REQUIRED - T_OrderHeaderID wajib diisi.');
exit;
}
$result = $this->ibl_merge_report_gateway->stream_from_qr_printout($orderHeaderId);
if ($result['status'] !== 'OK') {
$this->sys_error($result['code'] . ' - ' . $result['message']);
exit;
}
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $result['data']['payload']['name'] . '"');
echo $result['data']['body'];
exit;
}
public function preview()
{
if (!$this->isLogin) {

View File

@@ -10,6 +10,7 @@ class Inform_consent extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_patient_decrypt');
}
public function index()
@@ -62,22 +63,19 @@ class Inform_consent extends MY_Controller
private function get_patient_data($orderHeaderId)
{
$cacheId = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$sql = "SELECT
h.T_OrderHeaderID,
h.T_OrderHeaderDate,
DATE_FORMAT(h.T_OrderHeaderDate, '%d-%m-%Y') as tanggal,
DATE_FORMAT(h.T_OrderHeaderDate, '%H:%i') as jam,
p.M_PatientID,
p.M_PatientNoReg,
CONCAT(
IF(TRIM(IFNULL(t.M_TitleName,''))='', '', CONCAT(TRIM(IFNULL(t.M_TitleName,'')), '. ')),
TRIM(IFNULL(p.M_PatientPrefix,'')), ' ', TRIM(IFNULL(p.M_PatientName,'')), ' ', TRIM(IFNULL(p.M_PatientSuffix,''))
) AS patient_name,
p.M_PatientNoReg, p.M_PatientPrefix, p.M_PatientSuffix,
t.M_TitleName,
s.M_SexCode,
IFNULL(p.M_PatientPOB,'') as pob,
DATE_FORMAT(p.M_PatientDOB, '%d-%m-%Y') as dob,
IFNULL(pa.M_PatientAddressDescription,'') as alamat,
IFNULL(p.M_PatientHP,'') as phone,
p.M_PatientName_enc, p.M_PatientDOB_enc, p.M_PatientPOB_enc,
p.M_PatientDOB, p.M_PatientHP_enc,
pa.M_PatientAddressDescription_enc,
IFNULL(c.M_CompanyName,'') as company_name,
(
SELECT ps.Patient_SignatureUrl
@@ -98,10 +96,39 @@ class Inform_consent extends MY_Controller
WHERE h.T_OrderHeaderID = ?
LIMIT 1";
$qry = $this->db_onedev->query($sql, [$orderHeaderId]);
if (!$qry) {
return false;
}
return $qry->row_array();
if (!$qry) return false;
$row = $qry->row_array();
if (!$row) return false;
$row = $this->ibl_patient_decrypt->decrypt_row($row);
$title = trim($row['M_TitleName'] ?? '');
$cacheRow = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_order_id = ?
ORDER BY ppc_id DESC
LIMIT 1",
[$orderHeaderId]
)->row_array();
$printName = trim($cacheRow['ppc_name'] ?? '');
$namePart = trim($printName !== '' ? $printName : ($row['M_PatientName'] ?? ''));
$row['patient_name'] = trim(
($title ? $title . ' ' : '') .
trim($row['M_PatientPrefix'] ?? '') . ' ' .
$namePart . ' ' .
trim($row['M_PatientSuffix'] ?? '')
);
$row['patient_sign_name'] = trim(
trim($row['M_PatientPrefix'] ?? '') . ' ' .
$namePart . ' ' .
trim($row['M_PatientSuffix'] ?? '')
);
$row['dob'] = $row['M_PatientDOB'];
$row['pob'] = $row['M_PatientPOB'] ?? '';
$row['phone'] = $row['M_PatientHP'] ?? '';
$row['alamat'] = $row['M_PatientAddressDescription'] ?? '';
$this->ibl_patient_decrypt->delete_cache($cacheId);
return $row;
}
private function get_consent_content()
@@ -243,7 +270,7 @@ class Inform_consent extends MY_Controller
if ($signaturePath !== null) {
$pdf->Image($signaturePath, $rightX - 6, $signBottom - 23, 42, 12);
}
$this->draw_full_name_block($pdf, $rightX, $signBottom - 9, $rightW, trim(preg_replace('/\s+/', ' ', $patient['patient_name'])));
$this->draw_full_name_block($pdf, $rightX, $signBottom - 9, $rightW, trim(preg_replace('/\s+/', ' ', $patient['patient_sign_name'])));
$boxEndY = min($this->pageContentBottom, $signBottom + 2);
$pdf->Rect(10, $boxStartY, 190, max(10, $boxEndY - $boxStartY));

View File

@@ -8,6 +8,7 @@ class Inform_consent_cpmi extends MY_Controller
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_patient_decrypt');
}
public function index()
@@ -59,12 +60,17 @@ class Inform_consent_cpmi extends MY_Controller
private function get_patient_data($orderHeaderId)
{
$cacheId = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$sql = "SELECT
h.T_OrderHeaderID,
DATE_FORMAT(h.T_OrderHeaderDate, '%d-%m-%Y') as tanggal,
IFNULL(h.T_OrderHeaderM_PatientAge, '') as age,
p.M_PatientID,
CONCAT(TRIM(IFNULL(t.M_TitleName,'')), ' ', TRIM(IFNULL(p.M_PatientPrefix,'')), ' ', TRIM(IFNULL(p.M_PatientName,'')), ' ', TRIM(IFNULL(p.M_PatientSuffix,''))) AS patient_name,
IFNULL(t.M_TitleName, '') as M_TitleName,
IFNULL(p.M_PatientPrefix, '') as M_PatientPrefix,
IFNULL(p.M_PatientName, '') as M_PatientName,
IFNULL(p.M_PatientSuffix, '') as M_PatientSuffix,
IFNULL(s.M_SexCode, '') as sex_code,
DATE_FORMAT(p.M_PatientDOB, '%d-%m-%Y') as dob,
IFNULL(pa.M_PatientAddressDescription,'') as alamat,
@@ -86,7 +92,40 @@ class Inform_consent_cpmi extends MY_Controller
WHERE h.T_OrderHeaderID = ?
LIMIT 1";
$qry = $this->db_onedev->query($sql, [$orderHeaderId]);
return $qry ? $qry->row_array() : null;
if (!$qry) {
return null;
}
$row = $qry->row_array();
if (!$row) {
return null;
}
$cacheRow = $this->db_onedev->query(
"SELECT ppc_name
FROM patient_print_cache
WHERE ppc_order_id = ?
ORDER BY ppc_id DESC
LIMIT 1",
[$orderHeaderId]
)->row_array();
$printName = trim($cacheRow['ppc_name'] ?? '');
$namePart = trim($printName !== '' ? $printName : ($row['M_PatientName'] ?? ''));
$title = trim((string)($row['M_TitleName'] ?? ''));
$row['patient_name'] = trim(
($title ? $title . ' ' : '') .
trim((string)($row['M_PatientPrefix'] ?? '')) . ' ' .
$namePart . ' ' .
trim((string)($row['M_PatientSuffix'] ?? ''))
);
$row['patient_sign_name'] = trim(
trim((string)($row['M_PatientPrefix'] ?? '')) . ' ' .
$namePart . ' ' .
trim((string)($row['M_PatientSuffix'] ?? ''))
);
$this->ibl_patient_decrypt->delete_cache($cacheId);
return $row;
}
private function get_consent_content()
@@ -178,7 +217,7 @@ class Inform_consent_cpmi extends MY_Controller
}
$pdf->SetFont('Arial', 'U', 8.5);
$pdf->SetXY(149, $y);
$pdf->Cell(54, 5, $this->safe_text(trim(preg_replace('/\s+/', ' ', $patient['patient_name']))), 0, 1, 'C');
$pdf->Cell(54, 5, $this->safe_text(trim(preg_replace('/\s+/', ' ', $patient['patient_sign_name']))), 0, 1, 'C');
$y += 8;
$pdf->Line(14, $y, 196, $y);

View File

@@ -9,6 +9,7 @@ class Kartu_kontrol extends MY_Controller
{
parent::__construct();
$this->db_lab = $this->load->database('onedev', true);
$this->load->library('ibl_patient_decrypt');
}
public function index()
@@ -53,10 +54,15 @@ class Kartu_kontrol extends MY_Controller
private function get_data($pid)
{
// Populate decrypt cache sebelum call SP
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($pid);
$qry = $this->db_lab->query("CALL sp_rpt_fo_001(?, 'fpdf')", [$pid]);
if (!$qry) {
return [];
}
// Hapus cache setelah SP selesai
$this->ibl_patient_decrypt->delete_cache($cache_id);
if (!$qry) return [];
return $qry->result_array();
}

View File

@@ -79,6 +79,7 @@ class Medical_checkup_report extends MY_Controller
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('Generateqrreport');
$this->load->library('ibl_patient_decrypt');
}
public function index()
@@ -221,18 +222,10 @@ class Medical_checkup_report extends MY_Controller
IFNULL(NULLIF(TRIM(IFNULL(p.M_PatientDivisi, '')), ''), '-')
) AS departemen_npk,
IFNULL(s.M_SexName, '-') AS jenis_kelamin,
CONCAT(
IF(TRIM(IFNULL(t.M_TitleName, '')) = '', '', CONCAT(TRIM(t.M_TitleName), '. ')),
TRIM(IFNULL(p.M_PatientPrefix, '')), ' ',
TRIM(IFNULL(p.M_PatientName, '')), ' ',
TRIM(IFNULL(p.M_PatientSuffix, ''))
) AS nama_pasien,
CONCAT(
IFNULL(DATE_FORMAT(p.M_PatientDOB, '%d-%m-%Y'), '-'),
' / ',
IFNULL(NULLIF(TRIM(IFNULL(h.T_OrderHeaderM_PatientAge, '')), ''), '-')
) AS tgl_lahir_usia,
IFNULL(pa.M_PatientAddressDescription, '-') AS alamat_pasien
p.M_PatientPrefix, p.M_PatientSuffix, t.M_TitleName,
p.M_PatientName_enc, p.M_PatientDOB_enc,
p.M_PatientDOB, h.T_OrderHeaderM_PatientAge,
pa.M_PatientAddressDescription_enc
FROM t_orderheader h
JOIN m_patient p ON p.M_PatientID = h.T_OrderHeaderM_PatientID
LEFT JOIN m_title t ON t.M_TitleID = p.M_PatientM_TitleID
@@ -244,10 +237,22 @@ class Medical_checkup_report extends MY_Controller
WHERE h.T_OrderHeaderID = ?
LIMIT 1";
$qry = $this->db_onedev->query($sql, [$orderHeaderId]);
if (!$qry) {
return false;
}
return $qry->row_array();
if (!$qry) return false;
$row = $qry->row_array();
if (!$row) return false;
$row = $this->ibl_patient_decrypt->decrypt_row($row);
$title = trim($row['M_TitleName'] ?? '');
$row['nama_pasien'] = trim(
($title ? $title . '. ' : '') .
trim($row['M_PatientPrefix'] ?? '') . ' ' .
trim($row['M_PatientName'] ?? '') . ' ' .
trim($row['M_PatientSuffix'] ?? '')
);
$row['tgl_lahir_usia'] = trim($row['M_PatientDOB'] ?? '-') . ' / ' .
trim($row['T_OrderHeaderM_PatientAge'] ?? '-');
$row['alamat_pasien'] = $row['M_PatientAddressDescription'] ?? '-';
return $row;
}
private function get_result_rows($orderHeaderId)

View File

@@ -12,6 +12,25 @@ class Merge_report extends MY_Controller {
$this->preview($id);
}
public function preview_qr($id = null) {
if ($id === null || !is_numeric($id)) {
header('Content-Type: application/json');
echo json_encode(['status' => 'ERR', 'message' => 'T_ORDER_HEADER_ID_INVALID - ID tidak valid.']);
return;
}
$result = $this->ibl_merge_report_gateway->stream_from_qr_printout((int) $id);
if ($result['status'] !== 'OK') {
header('Content-Type: application/json');
echo json_encode(['status' => 'ERR', 'message' => $result['code'] . ' - ' . $result['message']]);
return;
}
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $result['data']['payload']['name'] . '"');
echo $result['data']['body'];
}
public function preview($id = null) {
if ($id === null || !is_numeric($id)) {
header('Content-Type: application/json');

View File

@@ -0,0 +1,430 @@
<?php
class OutgoingRef_v3 extends MY_Controller
{
function __construct()
{
parent::__construct();
$this->db = $this->load->database("onedev", true);
}
function get_nonlab($type, $xid)
{
//mikro
if ($type == "mikro") {
$sql = "select * from other_mikro where Other_MikroID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array();
if ($qry) {
$result["type"] = "mikro";
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
$sql = "select * from other_mikrodetails where Other_MikroDetailsOther_MikroID=? and
Other_MikroDetailsIsActive = 'Y' ";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"] = $qry->result_array();
}
}
return $result;
}
//cytologi
if ($type == "cytologi") {
$sql = "select * from other_cytologi where Other_CytologiID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array();
if ($qry) {
$result["type"] = "cytologi";
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
$sql = "select * from other_cytologidetails where Other_CytologiDetailsOther_CytologiID=? and
Other_CytologiDetailsIsActive = 'Y' ";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"] = $qry->result_array();
}
}
return $result;
}
//fna
if ($type == "fna") {
$sql = "select * from other_fna where Other_FnaID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array();
if ($qry) {
$result["type"] = "fna";
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
$sql = "select * from other_fnadetails where Other_FnaDetailsOther_FnaID=? and
Other_FnaDetailsIsActive = 'Y' ";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"] = $qry->result_array();
}
}
return $result;
}
//papsmear
//
if ($type == "papsmear") {
$sql = "select * from other_papsmear where Other_PapSmearID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array();
if ($qry) {
$result["type"] = "papsmear";
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
$result["detail"] = array();
//bahan
$sql = "select * from other_papsmearbahan where Other_PapSmearBahanOther_PapSmearID = ?
and Other_PapSmearBahanIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["bahan"] = $qry->result_array();
}
//category
$sql = "select * from other_papsmearcategory
where Other_PapsmearCategoryOther_PapSmearID = ?
and Other_PapsmearCategoryIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["category"] = $qry->result_array();
}
// check
$sql = "select * from other_papsmearcheck
where Other_PapSmearCheckOther_PapSmearID= ?
and Other_PapSmearCheckIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["check"] = $qry->result_array();
}
// details
$sql = "select * from other_papsmeardetails
where Other_PapSmearDetailsOther_PapSmearID= ?
and Other_PapSmearDetailsIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["details"] = $qry->result_array();
}
// maturasi
$sql = "select * from other_papsmearmaturasi
where Other_PapSmearMaturasiOther_PapSmearID = ?
and Other_PapSmearMaturasiIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["maturasi"] = $qry->result_array();
}
}
return $result;
}
//lcprep
//
if ($type == "lcprep") {
$sql = "select * from other_lcprep where Other_LcprepID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array();
if ($qry) {
$result["type"] = "lcprep";
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
$result["detail"] = array();
//adekuasi
$sql = "select * from other_lcprepadekuasi where Other_LcprepAdekuasiOther_LcprepID = ?
and Other_LcprepAdekuasiIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["adekuasi"] = $qry->result_array();
}
//interpretasi
$sql = "select * from other_lcprepinterpretasi
where Other_PapsmearInterpretasiOther_LcprepID = ?
and Other_PapsmearInterpretasiIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["interpretasi"] = $qry->result_array();
}
// details
$sql = "select * from other_lcprepdetails
where Other_LcprepDetailsOther_LcprepID= ?
and Other_LcprepDetailsIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["details"] = $qry->result_array();
}
// kategoriumum
$sql = "select * from other_lcprepkategoriumum
where Other_LcprepKategoriUmumOther_LcprepID = ?
and Other_LcprepKategoriUmumIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["kategoriumum"] = $qry->result_array();
}
}
return $result;
}
return array();
}
function process_nonlab($incomingRefDetailID)
{
//mikro
$sql = "select ifnull(Other_MikroID,0) mikroID
from incoming_ref_detail
join t_orderdetail on T_OrderDetailT_OrderHeaderID = incomingRefDetailNewT_OrderHeaderID
and T_OrderDetailT_TestID = incomingRefDetailT_TestID and T_OrderDetailIsActive = 'Y'
left join other_mikro on T_OrderDetailID = Other_MikroT_OrderDetailID and
Other_MikroIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if (count($rows) > 0) {
$mikroID = $rows[0]["mikroID"];
$nl = "";
if ($mikroID > 0) {
$nl = $this->get_nonlab("mikro", $mikroID);
return $nl;
}
}
//papsmear
$sql = "select ifnull(Other_PapSmearID,0) papsmearID
from incoming_ref_detail
join t_orderdetail on T_OrderDetailT_OrderHeaderID = incomingRefDetailNewT_OrderHeaderID
and T_OrderDetailT_TestID = incomingRefDetailT_TestID and T_OrderDetailIsActive = 'Y'
left join other_papsmear on T_OrderDetailID = Other_PapSmearT_OrderDetailID and
Other_PapSmearIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if (count($rows) > 0) {
$papsmearID = $rows[0]["papsmearID"];
$nl = "";
if ($papsmear > 0) {
$nl = $this->get_nonlab("papsmear", $papsmearID);
return $nl;
}
}
//lcprep
$sql = "select ifnull(Other_LcprepID,0) lcprepID
from incoming_ref_detail
join t_orderdetail on T_OrderDetailT_OrderHeaderID = incomingRefDetailNewT_OrderHeaderID
and T_OrderDetailT_TestID = incomingRefDetailT_TestID and T_OrderDetailIsActive = 'Y'
left join other_lcprep on T_OrderDetailID = Other_LcprepT_OrderDetailID and
Other_LcprepIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if (count($rows) > 0) {
$lcprepID = $rows[0]["lcprepID"];
$nl = "";
if ($lcprepID > 0) {
$nl = $this->get_nonlab("lcprep", $lcprepID);
return $nl;
}
}
//fna
$sql = "select ifnull(Other_FnaID,0) fnaID
from incoming_ref_detail
join t_orderdetail on T_OrderDetailT_OrderHeaderID = incomingRefDetailNewT_OrderHeaderID
and T_OrderDetailT_TestID = incomingRefDetailT_TestID and T_OrderDetailIsActive = 'Y'
left join other_fna on T_OrderDetailID = Other_FnaT_OrderDetailID and
Other_FnaIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if (count($rows) > 0) {
$fnaID = $rows[0]["fnaID"];
$nl = "";
if ($fnaID > 0) {
$nl = $this->get_nonlab("fna", $fnaID);
return $nl;
}
}
//
//cytology
$sql = "select ifnull(Other_CytologiID,0) cytologiID
from incoming_ref_detail
join t_orderdetail on T_OrderDetailT_OrderHeaderID = incomingRefDetailNewT_OrderHeaderID
and T_OrderDetailT_TestID = incomingRefDetailT_TestID and T_OrderDetailIsActive = 'Y'
left join other_cytologi on T_OrderDetailID = Other_CytologiT_OrderDetailID and
Other_CytologiIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if (count($rows) > 0) {
$cytologiID = $rows[0]["cytologiID"];
$nl = "";
if ($cytologiID > 0) {
$nl = $this->get_nonlab("cytologi", $cytologiID);
return $nl;
}
}
}
function process()
{
$sql = "select tx_branch_status.*, M_BranchName
from tx_branch_status
join m_branch on TxBranchStatusM_BranchID = M_BranchID
where TxBranchStatusIsSent = 'N' and TxBranchStatusRetry < 5
limit 0,20";
$qry = $this->db->query($sql);
$sql_update = "update tx_branch_status set TxBranchStatusIsSent=?,
TxBranchStatusRetry = TxBranchStatusRetry + 1
where TxBranchStatusID = ?";
if ($qry) {
$rows = $qry->result_array();
foreach ($rows as $r) {
$param = $r["TxBranchStatusJson"];
$stage = $r["TxBranchStatusStage"];
$ipAddress = $r["TxBranchStatusM_BranchIP"];
$branchName = $r["M_BranchName"];
$txID = $r["TxBranchStatusID"];
if ($stage == "VALIDATION") {
//cek nonlab
$j_param = json_decode($param, true);
foreach ($j_param as $idx => $j) {
if (isset($j["incomingRefDetailID"])) {
$nonlab_result = $this->process_nonlab($j["incomingRefDetailID"]);
if ($nonlab_result != array()) {
$j_param[$idx]["nonlab_result"] = $nonlab_result;
$param = json_encode($j_param);
}
}
}
}
$url = "http://$ipAddress/one-api-lab/tools/xstatusbranch_v3/update";
$rst = $this->post($url, $param);
if ($rst["status"] == "OK") {
$this->xlog("Update status $stage to $branchName @ $ipAddress [OK]");
$this->db->query($sql_update, array('Y', $txID));
} else {
$err_msg = print_r($rst, true);
$this->xlog("Update status $stage to $branchName @ $ipAddress [ERR] : $err_msg");
$this->db->query($sql_update, array('N', $txID));
}
}
} else {
$this->xlog("Err: " . print_r($this->db->error(), true));
}
}
function xlog($message)
{
$dt = date("Y-m-d H:i:s");
echo "$dt $message\n";
}
function status($incomingRefID)
{
$sql = "select * from incoming_ref where incomingRefID = ?";
$qry = $this->db->query($sql, array($incomingRefID));
$rows = array();
$branchID = 0;
if ($qry) {
$rows = $qry->result_array();
if (count($rows) > 0) $branchID = $rows[0]["incomingRefM_BranchID"];
}
$ip_address = "";
if ($branchID > 0) {
$sql = "select *
from m_branch where M_BranchID = ?";
$qry = $this->db->query($sql, array($branchID));
if ($qry) {
$rows = $qry->result_array();
if (count($rows) > 0) $ip_address = $rows[0]["M_BranchIPAddress"];
}
}
if ($ip_address == "") {
echo "No IP Address from $branchID ";
exit;
}
$sql = "select
incomingRefT_RefDeliveryOrderID,
incomingRefDetailT_OrderDetailID,
incomingRefDetailStatus,
T_OrderDetailResult,
T_OrderDetailNat_NormalValueID,
T_OrderDetailVerification,
T_OrderDetailValidation
from incoming_ref_detail
join incoming_ref on incomingRefID = incomingRefDetailIncomingRefID
and incomingRefID = ?
left join t_orderdetail on incomingRefDetailNewT_OrderHeaderID = T_OrderDetailT_OrderHeaderID
and T_OrderDetailIsActive = 'Y' and incomingRefDetailT_TestID = T_OrderDetailT_TestID";
$qry = $this->db->query($sql, array($incomingRefID));
if ($qry) {
$rows = $qry->result_array();
$param = json_encode($rows);
$url = "http://$ip_address/one-api/tools/xstatusbranch_v2/update";
$result = $this->post($url, $param);
}
}
function post($url, $data)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data)
)
);
$result = curl_exec($ch);
curl_close($ch);
if ($result === false) {
return array(
"status" => "ERR",
"message" => curl_error($ch)
);
}
$rst = json_decode($result, true);
return $rst;
}
}

View File

@@ -1,21 +1,23 @@
<?php
class OutgoingRef_v4 extends MY_Controller
{
function __construct() {
function __construct()
{
parent::__construct();
$this->db = $this->load->database("onedev", true);
}
function get_nonlab($type,$xid) {
function get_nonlab($type, $xid)
{
//mikro
if ($type == "mikro") {
$sql = "select * from other_mikro where Other_MikroID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array() ;
if ($qry ) {
$result = array();
if ($qry) {
$result["type"] = "mikro";
$rows = $qry->result_array();
if (count($rows) > 0 ) {
$result["header"]= $rows[0];
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
@@ -23,8 +25,8 @@ class OutgoingRef_v4 extends MY_Controller
Other_MikroDetailsIsActive = 'Y' ";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"] = $qry->result_array();
}
$result["detail"] = $qry->result_array();
}
}
return $result;
}
@@ -33,12 +35,12 @@ class OutgoingRef_v4 extends MY_Controller
if ($type == "cytologi") {
$sql = "select * from other_cytologi where Other_CytologiID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array() ;
if ($qry ) {
$result = array();
if ($qry) {
$result["type"] = "cytologi";
$rows = $qry->result_array();
if (count($rows) > 0 ) {
$result["header"]= $rows[0];
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
@@ -46,8 +48,8 @@ class OutgoingRef_v4 extends MY_Controller
Other_CytologiDetailsIsActive = 'Y' ";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"] = $qry->result_array();
}
$result["detail"] = $qry->result_array();
}
}
return $result;
}
@@ -56,12 +58,12 @@ class OutgoingRef_v4 extends MY_Controller
if ($type == "fna") {
$sql = "select * from other_fna where Other_FnaID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array() ;
if ($qry ) {
$result = array();
if ($qry) {
$result["type"] = "fna";
$rows = $qry->result_array();
if (count($rows) > 0 ) {
$result["header"]= $rows[0];
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
@@ -69,8 +71,8 @@ class OutgoingRef_v4 extends MY_Controller
Other_FnaDetailsIsActive = 'Y' ";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"] = $qry->result_array();
}
$result["detail"] = $qry->result_array();
}
}
return $result;
}
@@ -80,12 +82,12 @@ class OutgoingRef_v4 extends MY_Controller
if ($type == "papsmear") {
$sql = "select * from other_papsmear where Other_PapSmearID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array() ;
if ($qry ) {
$result = array();
if ($qry) {
$result["type"] = "papsmear";
$rows = $qry->result_array();
if (count($rows) > 0 ) {
$result["header"]= $rows[0];
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
@@ -95,40 +97,40 @@ class OutgoingRef_v4 extends MY_Controller
and Other_PapSmearBahanIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["bahan"] = $qry->result_array();
}
$result["detail"]["bahan"] = $qry->result_array();
}
//category
$sql = "select * from other_papsmearcategory
where Other_PapsmearCategoryOther_PapSmearID = ?
and Other_PapsmearCategoryIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["category"] = $qry->result_array();
$result["detail"]["category"] = $qry->result_array();
}
// check
// check
$sql = "select * from other_papsmearcheck
where Other_PapSmearCheckOther_PapSmearID= ?
and Other_PapSmearCheckIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["check"] = $qry->result_array();
}
$result["detail"]["check"] = $qry->result_array();
}
// details
$sql = "select * from other_papsmeardetails
where Other_PapSmearDetailsOther_PapSmearID= ?
and Other_PapSmearDetailsIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["details"] = $qry->result_array();
}
$result["detail"]["details"] = $qry->result_array();
}
// maturasi
$sql = "select * from other_papsmearmaturasi
where Other_PapSmearMaturasiOther_PapSmearID = ?
and Other_PapSmearMaturasiIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["maturasi"] = $qry->result_array();
}
$result["detail"]["maturasi"] = $qry->result_array();
}
}
return $result;
}
@@ -138,12 +140,12 @@ class OutgoingRef_v4 extends MY_Controller
if ($type == "lcprep") {
$sql = "select * from other_lcprep where Other_LcprepID=? ";
$qry = $this->db->query($sql, array($xid));
$result = array() ;
if ($qry ) {
$result = array();
if ($qry) {
$result["type"] = "lcprep";
$rows = $qry->result_array();
if (count($rows) > 0 ) {
$result["header"]= $rows[0];
$rows = $qry->result_array();
if (count($rows) > 0) {
$result["header"] = $rows[0];
} else {
return array();
}
@@ -153,15 +155,15 @@ class OutgoingRef_v4 extends MY_Controller
and Other_LcprepAdekuasiIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["adekuasi"] = $qry->result_array();
}
$result["detail"]["adekuasi"] = $qry->result_array();
}
//interpretasi
$sql = "select * from other_lcprepinterpretasi
where Other_PapsmearInterpretasiOther_LcprepID = ?
and Other_PapsmearInterpretasiIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["interpretasi"] = $qry->result_array();
$result["detail"]["interpretasi"] = $qry->result_array();
}
// details
$sql = "select * from other_lcprepdetails
@@ -169,23 +171,24 @@ class OutgoingRef_v4 extends MY_Controller
and Other_LcprepDetailsIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["details"] = $qry->result_array();
}
$result["detail"]["details"] = $qry->result_array();
}
// kategoriumum
$sql = "select * from other_lcprepkategoriumum
where Other_LcprepKategoriUmumOther_LcprepID = ?
and Other_LcprepKategoriUmumIsActive='Y'";
$qry = $this->db->query($sql, array($xid));
if ($qry) {
$result["detail"]["kategoriumum"] = $qry->result_array();
}
$result["detail"]["kategoriumum"] = $qry->result_array();
}
}
return $result;
}
return array();
}
function process_nonlab($incomingRefDetailID) {
function process_nonlab($incomingRefDetailID)
{
//mikro
$sql = "select ifnull(Other_MikroID,0) mikroID
from incoming_ref_detail
@@ -194,19 +197,19 @@ class OutgoingRef_v4 extends MY_Controller
left join other_mikro on T_OrderDetailID = Other_MikroT_OrderDetailID and
Other_MikroIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql,array($incomingRefDetailID));
if ( ! $qry ) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(),true) . "\n";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if ( count($rows) > 0 ) {
if (count($rows) > 0) {
$mikroID = $rows[0]["mikroID"];
$nl = "";
if ($mikroID > 0 ) {
if ($mikroID > 0) {
$nl = $this->get_nonlab("mikro", $mikroID);
return $nl;
}
}
}
//papsmear
@@ -217,19 +220,19 @@ class OutgoingRef_v4 extends MY_Controller
left join other_papsmear on T_OrderDetailID = Other_PapSmearT_OrderDetailID and
Other_PapSmearIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql,array($incomingRefDetailID));
if ( ! $qry ) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(),true) . "\n";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if ( count($rows) > 0 ) {
if (count($rows) > 0) {
$papsmearID = $rows[0]["papsmearID"];
$nl = "";
if ($papsmear> 0 ) {
if ($papsmear > 0) {
$nl = $this->get_nonlab("papsmear", $papsmearID);
return $nl;
}
}
}
//lcprep
@@ -240,19 +243,19 @@ class OutgoingRef_v4 extends MY_Controller
left join other_lcprep on T_OrderDetailID = Other_LcprepT_OrderDetailID and
Other_LcprepIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql,array($incomingRefDetailID));
if ( ! $qry ) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(),true) . "\n";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if ( count($rows) > 0 ) {
if (count($rows) > 0) {
$lcprepID = $rows[0]["lcprepID"];
$nl = "";
if ($lcprepID > 0 ) {
if ($lcprepID > 0) {
$nl = $this->get_nonlab("lcprep", $lcprepID);
return $nl;
}
}
}
//fna
@@ -263,19 +266,19 @@ class OutgoingRef_v4 extends MY_Controller
left join other_fna on T_OrderDetailID = Other_FnaT_OrderDetailID and
Other_FnaIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql,array($incomingRefDetailID));
if ( ! $qry ) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(),true) . "\n";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if ( count($rows) > 0 ) {
if (count($rows) > 0) {
$fnaID = $rows[0]["fnaID"];
$nl = "";
if ($fnaID > 0 ) {
if ($fnaID > 0) {
$nl = $this->get_nonlab("fna", $fnaID);
return $nl;
}
}
}
//
//cytology
@@ -286,22 +289,23 @@ class OutgoingRef_v4 extends MY_Controller
left join other_cytologi on T_OrderDetailID = Other_CytologiT_OrderDetailID and
Other_CytologiIsActive = 'Y'
where incomingRefDetailID = ? ";
$qry = $this->db->query($sql,array($incomingRefDetailID));
if ( ! $qry ) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(),true) . "\n";
$qry = $this->db->query($sql, array($incomingRefDetailID));
if (! $qry) {
echo "ERR : process_nonlab : $incomingRefDetailID : " . print_r($this->db->error(), true) . "\n";
return "";
}
$rows = $qry->result_array();
if ( count($rows) > 0 ) {
if (count($rows) > 0) {
$cytologiID = $rows[0]["cytologiID"];
$nl = "";
if ($cytologiID > 0 ) {
if ($cytologiID > 0) {
$nl = $this->get_nonlab("cytologi", $cytologiID);
return $nl;
}
}
}
}
function cek() {
function cek()
{
$sql = "select tx_branch_status.*, M_BranchName
from tx_branch_status
join m_branch on TxBranchStatusM_BranchID = M_BranchID
@@ -318,7 +322,8 @@ class OutgoingRef_v4 extends MY_Controller
print_r($rows);
}
}
function process() {
function process()
{
$sql = "select tx_branch_status.*, M_BranchName
from tx_branch_status
join m_branch on TxBranchStatusM_BranchID = M_BranchID
@@ -338,7 +343,7 @@ class OutgoingRef_v4 extends MY_Controller
where TxBranchStatusID = ?";
if ($qry) {
$rows = $qry->result_array();
foreach($rows as $r) {
foreach ($rows as $r) {
$param = $r["TxBranchStatusJson"];
$stage = $r["TxBranchStatusStage"];
$ipAddress = $r["TxBranchStatusM_BranchIP"];
@@ -346,57 +351,69 @@ class OutgoingRef_v4 extends MY_Controller
$txID = $r["TxBranchStatusID"];
if ($stage == "VALIDATION") {
//cek nonlab
$j_param = json_decode($param,true);
foreach($j_param as $idx => $j ) {
$j_param = json_decode($param, true);
foreach ($j_param as $idx => $j) {
if (isset($j["incomingRefDetailID"])) {
$nonlab_result = $this->process_nonlab( $j["incomingRefDetailID"] );
if ($nonlab_result != array() ) {
$nonlab_result = $this->process_nonlab($j["incomingRefDetailID"]);
if ($nonlab_result != array()) {
$j_param[$idx]["nonlab_result"] = $nonlab_result;
$param = json_encode($j_param);
}
}
}
}
$url = "http://$ipAddress/one-api/tools/xstatusbranch_v4/update";
$rst = $this->post($url,$param);
if ($rst["status"] == "OK" ) {
$url = "http://$ipAddress/one-api-lab/tools/xstatusbranch_v4/update";
$rst = $this->post($url, $param);
if ($rst["status"] == "OK") {
$this->xlog("Update status $stage to $branchName @ $ipAddress [OK]");
$this->db->query($sql_update, array('Y',$txID));
$this->db->query($sql_update, array('Y', $txID));
} else {
$err_msg = print_r($rst,true);
$err_msg = print_r($rst, true);
$this->xlog("Update status $stage to $branchName @ $ipAddress [ERR] : $err_msg");
$this->db->query($sql_update, array('N',$txID));
}
$this->db->query($sql_update, array('N', $txID));
}
}
} else {
$this->xlog("Err: " . print_r($this->db->error(),true));
$this->xlog("Err: " . print_r($this->db->error(), true));
}
}
function xlog($message) {
function xlog($message)
{
$dt = date("Y-m-d H:i:s");
echo "$dt $message\n";
}
function post($url,$data) {
$zdata = gzdeflate($data,9);
$ch = curl_init($url);
$this->xlog("Post to : " . $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $zdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($zdata))
);
function post($url, $data)
{
$zdata = gzdeflate($data, 9);
$ch = curl_init($url);
$this->xlog("Post to : " . $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $zdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json',
'Content-Length: ' . strlen($zdata)
)
);
$result = curl_exec($ch);
$curl_error = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($result === false ) {
return array("status" => "ERR" ,
"message" => curl_error($ch)
if ($result === false) {
return array(
"status" => "ERR",
"message" => $curl_error,
"http_code" => $http_code
);
}
$rst = json_decode($result,true);
return $rst;
}
}
$rst = json_decode($result, true);
return $rst;
}
}

View File

@@ -8,6 +8,7 @@ class Qr_report_uploader extends MY_Controller
public function __construct()
{
parent::__construct();
$this->load->library('ibl_patient_decrypt');
$this->db_onedev = $this->load->database('onedev', true);
$this->db_log = $this->load->database('one_lab_log', true);
}
@@ -120,7 +121,11 @@ class Qr_report_uploader extends MY_Controller
$orderHeaderID = (int)$row['QR_PrintOutT_OrderHeaderID'];
$reportUrl = $row['QR_PrintOutReportURL'];
// Populate cache sebelum fetch dari BIRT
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderID);
$pdfContent = $this->download_file($reportUrl);
$this->ibl_patient_decrypt->delete_cache($cache_id);
if ($pdfContent === false) {
return $this->mark_failed($row, $orderHeaderID, '', 'DOWNLOAD_FAILED');
}

View File

@@ -0,0 +1,542 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Rpt_lab_result — FPDF-based lab result report
* Endpoint: GET /tools/rpt_lab_result/pdf?token=...&PT_OrderHeaderID=...
*
* Menggunakan sp_rpt_hasil_header untuk data pasien,
* query langsung untuk detail hasil pemeriksaan.
* Cache PDP di-populate sebelum call SP lalu dihapus setelah selesai.
*/
class Rpt_lab_result extends MY_Controller
{
public $db_onedev;
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database('onedev', true);
$this->load->library('ibl_patient_decrypt');
}
public function index()
{
$this->sys_ok(['message' => 'Use /tools/rpt_lab_result/pdf?PT_OrderHeaderID=<id>']);
}
public function pdf()
{
try {
$order_id = $this->_get_order_id();
if ($order_id <= 0) {
$this->sys_error('PT_OrderHeaderID wajib diisi');
return;
}
$username = trim($this->input->get('username', true) ?? $this->input->post('username', true) ?? '');
if ($username === '') {
$username = $this->sys_user['M_StaffName'] ?? $this->sys_user['M_UserUsername'] ?? 'ADMIN';
}
// Populate cache PDP
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($order_id);
// Header data via SP
$header = $this->_fetch_header($order_id, $username);
if (!$header) {
$this->ibl_patient_decrypt->delete_cache($cache_id);
$this->sys_error('Data order tidak ditemukan');
return;
}
// Detail hasil pemeriksaan
$details = $this->_fetch_details($order_id, $username);
// Sampling data
$sampling = $this->_fetch_sampling($order_id);
// Company info (address, phone IBL)
$company_info = $this->_fetch_company_info();
// Delete cache setelah data diambil
$this->ibl_patient_decrypt->delete_cache($cache_id);
// Generate PDF
$pdf_bytes = $this->_build_pdf($header, $details, $sampling, $company_info, $username);
$filename = 'lab_result_' . $order_id . '_' . date('Ymd') . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf_bytes));
echo $pdf_bytes;
exit;
} catch (Exception $e) {
$this->sys_error($e->getMessage());
}
}
// -------------------------------------------------------------------------
// Data fetching
// -------------------------------------------------------------------------
public function data()
{
$order_id = $this->_get_order_id();
if ($order_id <= 0) { $this->sys_error('PT_OrderHeaderID wajib'); return; }
$username = $this->input->get('username', true) ?: 'ADMIN';
$details = $this->_fetch_details($order_id, $username);
$this->sys_ok(['count' => count($details), 'rows' => array_slice($details, 0, 5), 'last_query' => $this->db_onedev->last_query()]);
}
private function _fetch_header($order_id, $username)
{
$qry = $this->db_onedev->query('CALL sp_rpt_hasil_header(?, ?)', [$order_id, $username]);
if (!$qry) return null;
$row = $qry->row_array();
if (method_exists($this, 'clean_mysqli_connection')) {
$this->clean_mysqli_connection($this->db_onedev->conn_id);
} else {
$this->_clean_multi_result();
}
return $row ?: null;
}
private function _fetch_sampling($order_id)
{
$qry = $this->db_onedev->query('CALL sp_rpt_hasil_lab_sampling(?, ?)', [$order_id, '']);
if (!$qry) return [];
$rows = $qry->result_array();
if (method_exists($this, 'clean_mysqli_connection')) {
$this->clean_mysqli_connection($this->db_onedev->conn_id);
} else {
$this->_clean_multi_result();
}
return $rows;
}
private function _fetch_details($order_id, $username = '')
{
$qry = $this->db_onedev->query('CALL sp_rpt_hasil_lab(?, ?)', [$order_id, $username]);
if (!$qry) return [];
$rows = $qry->result_array();
if (method_exists($this, 'clean_mysqli_connection')) {
$this->clean_mysqli_connection($this->db_onedev->conn_id);
} else {
$this->_clean_multi_result();
}
return $rows;
}
private function _fetch_company_info()
{
$row = $this->db_onedev->query(
"SELECT S_SystemsCompanyAddress, S_SystemsCompanyCity, S_SystemsCompanyPhone, S_SystemsCompanyName FROM conf_systems LIMIT 1"
)->row_array();
return $row ?: [];
}
// -------------------------------------------------------------------------
// PDF builder
// -------------------------------------------------------------------------
private function _build_pdf(array $h, array $details, array $sampling, array $co, $username)
{
require_once APPPATH . 'third_party/fpdf/fpdf.php';
// Footer data
$validator_name = '';
foreach ($details as $d) {
if (!empty($d['M_StaffName'])) { $validator_name = $d['M_StaffName']; }
}
$lab_number = $this->_s($h['T_OrderHeaderLabNumber'] ?? '');
$print_username = $this->_s($username);
$print_datetime = date('d-m-Y H:i:s');
$pv = $validator_name;
$pu = $print_username;
$pd = $print_datetime;
$pln = $lab_number;
$pdf = new class($pu, $pd, $pln, $pv) extends FPDF {
private $pu, $pd, $pln, $pv;
public function __construct($pu, $pd, $pln, $pv) {
parent::__construct('P', 'mm', 'A4');
$this->pu = $pu; $this->pd = $pd; $this->pln = $pln; $this->pv = $pv;
}
public function Footer() {
$ml = 10; $cw = $this->GetPageWidth() - 20;
$this->SetFont('Helvetica', '', 10);
// Validasi Oleh (kanan)
$this->SetY(-38);
$this->SetX($ml);
$this->Cell($cw, 5, 'Validasi Oleh', 0, 1, 'R');
$this->Ln(8);
// Garis tanda tangan (kanan 40%)
$this->SetX($ml + $cw * 0.62);
$this->Cell($cw * 0.38, 0.3, '', 'T', 1);
// Nama validator (kanan)
$this->SetX($ml);
$this->Cell($cw, 5, iconv('UTF-8', 'windows-1252//TRANSLIT', (string) $this->pv), 0, 1, 'R');
$this->Ln(2);
// Printed by (kiri)
$printed = 'Printed by : ' . $this->pu . ' / ' . $this->pd . ' / ' . $this->pln;
$this->SetX($ml);
$this->Cell($cw, 5, $printed, 0, 0, 'L');
$this->Ln();
// Page number (tengah)
$this->SetX($ml);
$this->Cell($cw, 5, $this->PageNo() . ' / {nb}', 0, 0, 'C');
}
};
$pdf->AliasNbPages('{nb}');
$pdf->SetMargins(9, 8, 9);
$pdf->SetAutoPageBreak(true, 42);
$pdf->AddPage();
$pw = $pdf->GetPageWidth(); // 210
$ml = 9;
$mr = 9;
$cw = $pw - $ml - $mr; // 190
// Form revision number
$form_row = $this->db_onedev->query(
"SELECT IFNULL(M_No_FormRev,'') AS M_No_FormRev FROM m_no_form WHERE M_No_FormName='LAB' AND M_No_FormIsActive='Y' LIMIT 1"
);
$form_rev = $form_row ? $this->_s($form_row->row_array()['M_No_FormRev'] ?? '') : '';
// ── Pojok kanan atas: lab number + form rev ───────────────────────────
$pdf->SetFont('Arial', '', 7);
$pdf->SetXY($ml, 4);
$top_right = trim($lab_number . ($form_rev ? ' ' . $form_rev : ''));
$pdf->Cell($cw, 5, $top_right, 0, 1, 'R');
// ── Kop atas ──────────────────────────────────────────────────────────
$company_addr = $this->_s($co['S_SystemsCompanyAddress'] ?? 'Jl. LL. RE. Martadinata No. 135');
$company_city = $this->_s($co['S_SystemsCompanyCity'] ?? 'Bandung');
$company_phone = $this->_s($co['S_SystemsCompanyPhone'] ?? '(022)7271946');
$addr_line = $company_addr . ' ' . $company_city . ' Telp. ' . $company_phone;
$pj_name = $this->_s($h['M_DoctorName'] ?? '');
$pdf->SetFont('Arial', '', 9);
$pdf->SetXY($ml, 8);
$pdf->Cell($cw / 2, 5, $addr_line, 0, 0, 'L');
$pdf->SetX($ml + $cw / 2);
$pdf->Cell($cw / 2, 5, 'Penanggung Jawab : ' . $pj_name, 0, 1, 'R');
$pdf->SetLineWidth(0.8);
$pdf->Line($ml, 13.5, $ml + $cw, 13.5);
$pdf->SetLineWidth(0.2);
$pdf->SetY(15);
// ── Patient info (2 columns) ──────────────────────────────────────────
$lw = 94;
$rw = $cw - $lw;
$lbl_w = 30;
$col_w = 4;
$val_w = $lw - $lbl_w - $col_w;
$lbl_rw = 30;
$val_rw = $rw - $lbl_rw - $col_w;
$pid = $this->_s($h['T_OrderHeaderLabNumber'] ?? '-');
$left_rows = [
['NO. REG', $this->_s($h['M_PatientNoReg'] ?? '-')],
['NAMA', $this->_s($h['M_PatientName'] ?? '-')],
['PENGIRIM', $this->_s($h['M_DoctorName2'] ?? '-')],
['KEL. PELANGGAN*', $this->_s($h['CorporateName'] ?? '-')],
['ALAMAT', $this->_s($h['M_PatientAddress'] ?? '-')],
];
$right_rows = [
['TANGGAL REG', $this->_s($h['T_OrderHeaderDate'] ?? '-')],
['PID', $pid],
['JENIS KELAMIN', $this->_s($h['Gender'] ?? '-')],
['TGL. LAHIR / USIA', $this->_s($h['Umur'] ?? '-')],
['NO. TLP. / HP', $this->_s($h['M_PatientHp'] ?? '-')],
];
$fnt = 'Arial';
$fs = 8.5;
$y_info_start = $pdf->GetY();
$barcode_x = $ml + $lw + 4;
$barcode_y = $y_info_start;
$barcode_w = 58;
$barcode_h = 8;
$this->_draw_fake_barcode($pdf, $barcode_x, $barcode_y, $barcode_w, $barcode_h, $pid);
$y_left_cur = $y_info_start + 1;
$y_right_cur = $y_info_start + 10;
foreach ($left_rows as $r) {
$y_left_cur = $this->_draw_header_row($pdf, $ml, $y_left_cur, $lbl_w, $col_w, $val_w, $r[0], $r[1], $fnt, $fs);
}
foreach ($right_rows as $r) {
$y_right_cur = $this->_draw_header_row($pdf, $ml + $lw + 4, $y_right_cur, $lbl_rw, $col_w, $val_rw - 4, $r[0], $r[1], $fnt, $fs);
}
$pdf->SetY(max($y_left_cur, $y_right_cur) + 10);
// ── Table header ─────────────────────────────────────────────────────
$col = ['JENIS PEMERIKSAAN' => 64, 'HASIL' => 24, 'NILAI RUJUKAN' => 43, 'SATUAN' => 24, 'METODE' => 37];
$col_w_arr = array_values($col);
$row_h = 5.2;
$y_table_start = $pdf->GetY();
$pdf->SetFont($fnt, 'B', 8.8);
$pdf->SetX($ml);
foreach ($col as $label => $w) {
$pdf->Cell($w, $row_h + 1.8, $label, '1', 0, 'C');
}
$pdf->Ln();
// ── Table rows ────────────────────────────────────────────────────────
// Strategy:
// - Group/SubSubGroup rows : full-width text only, tanpa border bawah
// - Test rows : text per kolom + separator full-width manual
// - Outer L/R/T/B : drawn via Rect() after all rows
$prev_subgroup = null;
$prev_subsubgroup = null;
$qr_url = '';
foreach ($details as $d) {
$subgroup = strtoupper(trim($d['Nat_SubGroupName'] ?? ''));
$subsubgroup = trim($d['Nat_SubSubGroupName'] ?? '');
$sascode_len = strlen(trim($d['T_TestSasCode'] ?? ''));
$test_name = $this->_s(trim($d['T_TestName'] ?? ''));
$result = $this->_s(trim(strip_tags($d['T_OrderDetailResult'] ?? '')));
$normal = $this->_s(trim($d['T_OrderDetailNormalValueNote'] ?? $d['T_OrderDetailNormalValueDescription'] ?? ''));
$unit = $this->_s(trim($d['T_OrderDetailNat_UnitName'] ?? ''));
$method = $this->_s(trim($d['T_OrderdetailNat_MethodeName'] ?? $d['methodeName'] ?? ''));
$flag = trim($d['T_OrderDetailResultFlag'] ?? '');
$is_abnormal = ($flag !== '' && $flag !== 'N');
if (empty($qr_url) && !empty($d['qrreport'])) {
$qr_url = $d['qrreport'];
}
// Level 1: SubGroup — bold, full-width, tanpa separator
if ($subgroup !== '' && $subgroup !== $prev_subgroup) {
$pdf->SetX($ml);
$pdf->SetFont($fnt, 'B', 8.8);
$pdf->Cell($cw, $row_h, $subgroup, 0, 1, 'L');
$prev_subgroup = $subgroup; $prev_subsubgroup = null;
}
// Level 2: SubSubGroup — italic, full-width, tanpa separator
if ($subsubgroup !== '' && $subsubgroup !== $prev_subsubgroup) {
$pdf->SetX($ml);
$pdf->SetFont($fnt, 'I', 8.6);
$pdf->Cell($cw, $row_h, ' ' . $this->_s($subsubgroup), 0, 1, 'L');
$prev_subsubgroup = $subsubgroup;
}
// Level 3: Test row — indent via spaces, separator full-width
$sp = 0; $prefix = '';
if ($sascode_len >= 10 && $sascode_len < 12) { $sp = 2; }
elseif ($sascode_len >= 12 && $sascode_len < 14) { $sp = 4; $prefix = chr(183) . ' '; }
elseif ($sascode_len >= 14) { $sp = 6; $prefix = chr(183) . ' '; }
$display_name = str_repeat(' ', $sp) . $prefix . $test_name;
$pdf->SetFont($fnt, $is_abnormal ? 'B' : '', 8.2);
$y_start = $pdf->GetY();
// Hitung row height
$row_height = $row_h;
$texts = [$display_name, $result, $normal, $unit, $method];
foreach ($texts as $i => $txt) {
$lines = $this->_estimate_text_lines($pdf, $col_w_arr[$i] - 2, $txt);
$row_height = max($row_height, $lines * $row_h);
}
// Render cells tanpa border, lalu gambar separator full-width manual
$x_cur = $ml;
foreach ($texts as $i => $txt) {
$w = $col_w_arr[$i];
$align = ($i === 1) ? 'C' : 'L';
$pdf->SetXY($x_cur, $y_start);
$pdf->Cell($w, $row_height, $txt, 0, 0, $align);
$x_cur += $w;
}
$pdf->Line($ml, $y_start + $row_height, $ml + $cw, $y_start + $row_height);
$pdf->SetXY($ml, $y_start + $row_height);
}
// Outer rectangle — clean single-weight border for entire table
$y_table_end = $pdf->GetY();
$pdf->Rect($ml, $y_table_start, $cw, $y_table_end - $y_table_start);
// ── Catatan ───────────────────────────────────────────────────────────
$pdf->Ln(3);
$pdf->SetFont($fnt, '', 8.2);
$pdf->SetX($ml);
$pdf->Cell($cw, $row_h, 'Catatan :', 0, 1, 'L');
$pdf->Ln(2);
// ── Waktu Pengambilan Spesimen ────────────────────────────────────────
if (!empty($sampling)) {
$pdf->SetFont($fnt, '', 8.2);
$pdf->SetX($ml);
$pdf->Cell($cw, $row_h, 'Waktu Pengambilan Spesimen', 0, 1, 'L');
foreach ($sampling as $s) {
$bahan = $this->_s($s['T_BahanName'] ?? '');
$tgl = $this->_s($s['sampling_date'] ?? '');
$jam = $this->_s($s['sample_time'] ?? '');
$pdf->SetX($ml);
$pdf->Cell(35, $row_h, $bahan, 0, 0, 'L');
$pdf->Cell(4, $row_h, ':', 0, 0, 'C');
$pdf->Cell(35, $row_h, $tgl, 0, 0, 'L');
$pdf->Cell(30, $row_h, $jam, 0, 1, 'L');
}
}
// ── QR code (absolute, 20mm di atas footer = -42-3 = -45 dari bawah) ──
if (!empty($qr_url)) {
$tmp = $this->_fetch_image_to_temp($qr_url);
if ($tmp) {
$page_h = $pdf->GetPageHeight();
$qr_y = $page_h - 42 - 3 - 20; // footer_margin=42, gap=3, qr_h=20
$pdf->Image($tmp, $ml, $qr_y, 20, 20);
@unlink($tmp);
}
}
return $pdf->Output('S');
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
private function _get_order_id()
{
foreach (['PT_OrderHeaderID', 'PID', 'order_id'] as $k) {
$v = $this->input->get($k, true) ?? $this->input->post($k, true) ?? ($this->sys_input[$k] ?? null);
if ($v !== null && $v !== '') return intval($v);
}
return 0;
}
private function _draw_header_row($pdf, $x, $y, $label_w, $colon_w, $value_w, $label, $value, $font, $font_size)
{
$label = $this->_s($label);
$value = $this->_s($value);
$line_h = 5.2;
$pdf->SetFont($font, 'B', $font_size);
$pdf->SetXY($x, $y);
$pdf->Cell($label_w, $line_h, $label, 0, 0, 'L');
$pdf->SetFont($font, '', $font_size);
$pdf->Cell($colon_w, $line_h, ':', 0, 0, 'C');
$value_lines = $this->_estimate_text_lines($pdf, $value_w, $value);
$row_h = max(1, $value_lines) * $line_h;
$pdf->SetXY($x + $label_w + $colon_w, $y);
$pdf->MultiCell($value_w, $line_h, $value, 0, 'L');
return $y + $row_h;
}
private function _estimate_text_lines($pdf, $width, $text)
{
$text = str_replace("\r", '', (string) $text);
if ($text === '') {
return 1;
}
$lines = explode("\n", $text);
$count = 0;
foreach ($lines as $line) {
$line = trim($line);
if ($line === '') {
$count++;
continue;
}
$words = preg_split('/\s+/', $line);
$current = '';
foreach ($words as $word) {
$candidate = $current === '' ? $word : $current . ' ' . $word;
if ($pdf->GetStringWidth($candidate) <= $width) {
$current = $candidate;
continue;
}
if ($current !== '') {
$count++;
$current = $word;
} else {
$count += max(1, (int) ceil($pdf->GetStringWidth($word) / max($width, 1)));
$current = '';
}
}
if ($current !== '') {
$count++;
}
}
return max(1, $count);
}
private function _draw_fake_barcode($pdf, $x, $y, $w, $h, $text)
{
$text = preg_replace('/[^A-Za-z0-9]/', '', (string) $text);
if ($text === '') {
return;
}
$sequence = '1010';
$chars = str_split(strtoupper($text));
foreach ($chars as $char) {
$bits = str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
$sequence .= $bits . '0';
}
$sequence .= '10101';
$module_w = $w / strlen($sequence);
$pdf->SetFillColor(0, 0, 0);
for ($i = 0, $len = strlen($sequence); $i < $len; $i++) {
if ($sequence[$i] !== '1') {
continue;
}
$bar_x = $x + ($i * $module_w);
$bar_h = ($i % 7 === 0) ? $h : ($h - 1.2);
$pdf->Rect($bar_x, $y, max(0.35, $module_w * 0.92), $bar_h, 'F');
}
}
private function _s($text)
{
return iconv('UTF-8', 'windows-1252//TRANSLIT', (string) $text);
}
private function _fetch_image_to_temp($url)
{
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_FOLLOWLOCATION => true]);
$data = curl_exec($ch);
curl_close($ch);
if (!$data) return null;
$tmp = tempnam(sys_get_temp_dir(), 'qr_') . '.png';
file_put_contents($tmp, $data);
return $tmp;
}
private function _clean_multi_result()
{
if (isset($this->db_onedev->conn_id) && $this->db_onedev->conn_id instanceof mysqli) {
while ($this->db_onedev->conn_id->more_results()) {
$this->db_onedev->conn_id->next_result();
}
}
}
}

View File

@@ -4,6 +4,7 @@ class Rpt_t_002 extends MY_Controller
public function __construct()
{
parent::__construct();
$this->load->library('ibl_patient_decrypt');
}
public function index()
@@ -34,8 +35,10 @@ class Rpt_t_002 extends MY_Controller
if ($an === null) {
$an = '';
}
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$sql = 'CALL sp_rpt_t_002(?, ?, ?)';
$qry = $this->db->query($sql, [$orderHeaderId, $an, $username]);
$this->ibl_patient_decrypt->delete_cache($cache_id);
$lastQry = $this->db->last_query();
if (!$qry) {
@@ -75,8 +78,10 @@ class Rpt_t_002 extends MY_Controller
if ($an === null) {
$an = '';
}
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$sql = 'CALL sp_rpt_t_002(?, ?, ?)';
$qry = $this->db->query($sql, [$orderHeaderId, $an, $username]);
$this->ibl_patient_decrypt->delete_cache($cache_id);
$lastQry = $this->db->last_query();
if (!$qry) {
$this->sys_error_db([

View File

@@ -4,6 +4,7 @@ class Rpt_t_002_eng extends MY_Controller
public function __construct()
{
parent::__construct();
$this->load->library('ibl_patient_decrypt');
}
public function index()
@@ -35,8 +36,10 @@ class Rpt_t_002_eng extends MY_Controller
$an = '';
}
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$sql = 'CALL sp_rpt_t_002_eng(?, ?, ?)';
$qry = $this->db->query($sql, [$orderHeaderId, $an, $username]);
$this->ibl_patient_decrypt->delete_cache($cache_id);
$lastQry = $this->db->last_query();
if (!$qry) {
@@ -77,6 +80,7 @@ class Rpt_t_002_eng extends MY_Controller
$an = '';
}
$cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$sql = 'CALL sp_rpt_t_002_eng(?, ?, ?)';
$qry = $this->db->query($sql, [$orderHeaderId, $an, $username]);
$lastQry = $this->db->last_query();

View File

@@ -1,15 +1,18 @@
<?php
class Xferbranch_v4 extends CI_Controller
{
function __construct() {
function __construct()
{
parent::__construct();
$this->db = $this->load->database("onedev", true);
}
function index() {
function index()
{
echo "Xfer Branch API";
}
function batch() {
function batch()
{
$this->fix();
$sql = "select T_RefDeliveryOrderID, T_RefDeliveryOrderNumber,
@@ -25,13 +28,13 @@ class Xferbranch_v4 extends CI_Controller
$tot_upload = 0;
if ($qry) {
$rows = $qry->result_array();
foreach($rows as $r) {
foreach ($rows as $r) {
$date = date("Y-m-d H:i:s ");
$branch = $r["M_BranchName"];
$ip_address = $r["M_BranchIPAddress"];
$number = $r["T_RefDeliveryOrderNumber"];
if ( $this->do($r["T_RefDeliveryOrderID"])) {
if ($this->do($r["T_RefDeliveryOrderID"])) {
echo "$date [OK] uploaded {$number} to {$branch} @ $ip_address\n";
} else {
echo "$date [ERR] uploaded {$number} to {$branch} @ $ip_address\n";
@@ -39,13 +42,14 @@ class Xferbranch_v4 extends CI_Controller
$tot_upload++;
}
}
if ($tot_upload == 0 ) {
if ($tot_upload == 0) {
$date = date("Y-m-d H:i:s ");
echo "$date [OK] No upload data\n";
}
}
function do($deliveryID) {
function do($deliveryID)
{
$sql = "select distinct
T_RefDeliveryOrderID,
T_RefDeliveryOrderDate,
@@ -81,20 +85,20 @@ class Xferbranch_v4 extends CI_Controller
where T_RefDeliveryOrderID = ?
and T_RefDeliveryOrderDetailIsConfirm = 'Y'
";
$qry = $this->db->query($sql, array($deliveryID) );
$qry = $this->db->query($sql, array($deliveryID));
$rows = array();
$branch_ip_address = "";
if($qry) {
if ($qry) {
$rows = $qry->result_array();
if (count($rows) == 0 ) {
echo "No Detail Records. => ";
$this->db->query("update t_ref_deliveryorder set T_RefDeliveryOrderIsConfirm = 'Z' where T_RefDeliveryOrderID = ?", array($deliveryID));
if (count($rows) == 0) {
echo "No Detail Records. => ";
$this->db->query("update t_ref_deliveryorder set T_RefDeliveryOrderIsConfirm = 'Z' where T_RefDeliveryOrderID = ?", array($deliveryID));
return false;
}
$sqlp = "select * from m_patient where M_PatientID = ?";
$sqlpa = "select * from m_patientaddress where M_PatientAddressM_PatientID = ?";
$sqlpt = "select * from m_title where M_TitleID = ?";
$sqlp = "select * from m_patient where M_PatientID = ?";
$sqlpa = "select * from m_patientaddress where M_PatientAddressM_PatientID = ?";
$sqlpt = "select * from m_title where M_TitleID = ?";
$sqlt = "select
T_OrderDetailT_TestID T_TestID,
@@ -124,88 +128,88 @@ class Xferbranch_v4 extends CI_Controller
left join t_orderpromise on T_OrderDetailT_OrderPromiseID = T_OrderPromiseID";
$arr_price_test = array();
foreach($rows as $idx => $r) {
foreach ($rows as $idx => $r) {
//patient & address
$branch_ip_address = $r["DestinationIPAddress"];
$patientID = $r["T_OrderHeaderM_PatientID"];
$headerID = $r["T_OrderHeaderID"];
$detailID= $r["T_OrderDetailID"];
$detailID = $r["T_OrderDetailID"];
$deliveryDetailID = $r["T_RefDeliveryOrderDetailID"];
$qryp = $this->db->query($sqlp, array($patientID));
$patient = array();
if ($qryp) {
$rowsp = $qryp->result_array();
if( count($rowsp) > 0 ) {
if (count($rowsp) > 0) {
$patient = $rowsp[0];
$titleID = $patient["M_PatientM_TitleID"];
$qrypa = $this->db->query($sqlpa, array($patientID));
if ($qrypa) {
$patient["address"] = $qrypa->result_array();
$patient["address"] = $qrypa->result_array();
}
$qrypt = $this->db->query($sqlpt, array($titleID));
if ($qrypt) {
$rowspt = $qrypt->result_array();
if (count($rowspt) > 0 ) $patient["title"] = $rowspt[0];
$rowspt = $qrypt->result_array();
if (count($rowspt) > 0) $patient["title"] = $rowspt[0];
}
}
}
}
$rows[$idx]["patient"] = $patient;
// test
$qryt = $this->db->query($sqlt,array($detailID));
$qryt = $this->db->query($sqlt, array($detailID));
$arr_test = array();
if ($qryt) {
$rowst = $qryt->result_array();
if (! isset($arr_price_test[$headerID])) {
$arr_price_test[$headerID] = array();
}
foreach($rowst as $r ) {
$sasCode = substr($r["T_TestSasCode"],0,8);
foreach ($rowst as $r) {
$sasCode = substr($r["T_TestSasCode"], 0, 8);
$curSasCode = $r["T_TestSasCode"];
if ( ! isset($arr_price_test[$headerID][$sasCode]) ) {
if(strlen($curSasCode) == 8) $arr_price_test[$headerID][$sasCode] = true;
if (! isset($arr_price_test[$headerID][$sasCode])) {
if (strlen($curSasCode) == 8) $arr_price_test[$headerID][$sasCode] = true;
$rows[$idx]["test"][] = $r;
$arr_test[]=$r["T_TestID"];
$arr_test[] = $r["T_TestID"];
}
}
}
// $child_test
$qryct = $this->db->query($sqlct,array($deliveryDetailID));
$qryct = $this->db->query($sqlct, array($deliveryDetailID));
if ($qryct) {
$rowsct = $qryct->result_array();
$rows[$idx]["child_test"] = $rowsct;
}
$reqs = array();
if (count($arr_test) > 0 ) {
if (count($arr_test) > 0) {
$sqlr = "select * from t_orderreq where T_OrderReqT_OrderHeaderID = ?";
$qryr = $this->db->query($sqlr, array($headerID));
if ($qryr) {
$rowsr = $qryr->result_array();
foreach($rowsr as $r) {
foreach ($rowsr as $r) {
$a_test_r = json_decode($r["T_OrderReqT_TestID"]);
$a_x = array_intersect($arr_test,$a_test_r);
if( count($a_x) > 0 ) $reqs[] = $r;
$a_x = array_intersect($arr_test, $a_test_r);
if (count($a_x) > 0) $reqs[] = $r;
}
}
}
$rows[$idx]["requirements"] = $reqs;
}
$o_rows=$rows;
$o_rows = $rows;
$rows = array();
foreach($o_rows as $r) {
if (isset($r["test"]) ) {
foreach ($o_rows as $r) {
if (isset($r["test"])) {
$rows[] = $r;
}
}
$param = json_encode($rows);
$url = "http://$branch_ip_address/one-api/tools/incomingref_v4/receive";
$result = $this->post($url,$param);
echo "to $url \nresponse : $result\n";
$result = json_decode($result,true);
if ($result["status"] == "OK" ) {
$url = "http://$branch_ip_address/one-api-lab/tools/incomingref_v4/receive";
$result = $this->post($url, $param);
echo "to $url \nresponse : $result\n";
$result = json_decode($result, true);
if ($result["status"] == "OK") {
$sqlu = "update t_ref_deliveryorder set T_RefDeliveryOrderIsConfirm = 'S'
where T_RefDeliveryOrderID = ?";
$this->db->query($sqlu, array($deliveryID));
@@ -214,13 +218,13 @@ class Xferbranch_v4 extends CI_Controller
print_r($result);
}
} else {
print_r($this->db->error());
print_r($this->db->error());
}
return false;
}
public function fix()
{
$sql = "SELECT
{
$sql = "SELECT
T_RefDeliveryOrderID,
T_RefDeliveryOrderDetailT_WorklistRefConfirmDetailID,
T_RefDeliveryOrderDate,
@@ -242,34 +246,39 @@ WHERE T_RefDeliveryOrderDetailIsActive = 'Y' AND
T_RefDeliveryOrderDetailT_BarcodeLabBarcode <> T_BarcodeLabBarcode AND
T_RefDeliveryOrderDate >= '2024-08-01'";
$qry = $this->db->query($sql);
$rows = $qry->result_array();
if (count($rows) > 0) {
foreach ($rows as $k => $v) {
$sql = "UPDATE t_ref_deliveryorder_detail
$qry = $this->db->query($sql);
$rows = $qry->result_array();
if (count($rows) > 0) {
foreach ($rows as $k => $v) {
$sql = "UPDATE t_ref_deliveryorder_detail
SET T_RefDeliveryOrderDetailT_BarcodeLabBarcode = '{$v['T_BarcodeLabBarcode']}'
WHERE T_RefDeliveryOrderDetailID = {$v['T_RefDeliveryOrderDetailID']}";
$qry = $this->db->query($sql);
$sql = "UPDATE t_worklist_ref_confirmdetail
$qry = $this->db->query($sql);
$sql = "UPDATE t_worklist_ref_confirmdetail
SET T_WorklistRefConfirmDetailT_BarcodeLabBarcode = '{$v['T_BarcodeLabBarcode']}'
WHERE T_WorklistRefConfirmDetailID = {$v['T_RefDeliveryOrderDetailT_WorklistRefConfirmDetailID']}";
$qry = $this->db->query($sql);
}
}
}
function post($url,$data) {
$qry = $this->db->query($sql);
}
}
}
function post($url, $data)
{
$zdata = gzdeflate($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $zdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $zdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($zdata))
);
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json',
'Content-Length: ' . strlen($zdata)
)
);
$result = curl_exec($ch);
//echo "RST : $result ";
return $result;

View File

@@ -0,0 +1,639 @@
<?php
class Xstatusbranch_v3 extends MY_Controller
{
function __construct() {
parent::__construct();
$this->db = $this->load->database("onedev", true);
}
function index() {
echo "API";
}
//mikro
function update_mikro($detailID, $header, $detail ) {
$sql = "select * from t_orderdetail where T_OrderDetailID = ? ";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro cek Validation " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
if (count($rows) == 0 ) return;
if ($rows[0]["T_OrderDetailValidation"] == "Y" ) return;
$sql = "select Other_MikroID from other_mikro where Other_MikroT_OrderDetailID = ? and Other_MikroIsActive = 'Y'";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro cek Existence " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
$mikroID = 0;
if (count($rows) > 0 ) $mikroID = $rows[0]["Other_MikroID"];
if ( $mikroID > 0 ) {
$sql = "update other_mikro set Other_MikroIsActive = 'N' where Other_MikroID = ? ";
$qry = $this->db->query($sql, array($mikroID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_mikrodetails set Other_MikroDetailsIsActive = 'N' where Other_MikroDetailsOther_MikroID= ? ";
$qry = $this->db->query($sql, array($mikroID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
unset($header["Other_MikroID"]);
$header["Other_MikroCreated"] = date("Y-m-d h:i:s");
$header["Other_MikroLastUpdated"] =date("Y-m-d h:i:s");
$header["Other_MikroUserID"] = 3;
$header["Other_MikroIsActive"] = "Y";
$header["Other_MikroT_OrderDetailID"] = $detailID;
$qry = $this->db->insert("other_mikro",$header);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro Add Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$mikroID = $this->db->insert_id();
if ($mikroID == 0 ) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro ID Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
foreach($detail as $idx => $d ) {
$detail[$idx]["Other_MikroDetailsOther_MikroID"] = $mikroID ;
$detail[$idx]["Other_MikroDetailsUserID"] = 3;
$detail[$idx]["Other_MikroDetailsIsActive"] = "Y";
unset($detail[$idx]["Other_MikroDetailsID"]);
unset($detail[$idx]["Other_MikroDetailsCreated"]);
unset($detail[$idx]["Other_MikroDetailsLastUpdated"]);
}
$qry = $this->db->insert_batch("other_mikrodetails",$detail);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Mikro Add Detail" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
//cytologi
function update_cytologi($detailID, $header, $detail ) {
$sql = "select * from t_orderdetail where T_OrderDetailID = ? ";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi cek Validation " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
if (count($rows) == 0 ) return;
if ($rows[0]["T_OrderDetailValidation"] == "Y" ) return;
$sql = "select Other_CytologiID from other_cytologi where Other_CytologiT_OrderDetailID = ? and Other_CytologiIsActive = 'Y'";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi cek Existence " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
$cytologiID = 0;
if (count($rows) > 0 ) $cytologiID = $rows[0]["Other_CytologiID"];
if ( $cytologiID > 0 ) {
$sql = "update other_cytologi set Other_CytologiIsActive = 'N' where Other_CytologiID = ? ";
$qry = $this->db->query($sql, array($cytologiID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_cytologidetails set Other_CytologiDetailsIsActive = 'N' where Other_CytologiDetailsOther_CytologiID= ? ";
$qry = $this->db->query($sql, array($cytologiID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
unset($header["Other_CytologiID"]);
$header["Other_CytologiCreated"] = date("Y-m-d h:i:s");
$header["Other_CytologiLastUpdated"] =date("Y-m-d h:i:s");
$header["Other_CytologiUserID"] = 3;
$header["Other_CytologiIsActive"] = "Y";
$header["Other_CytologiT_OrderDetailID"] = $detailID;
$qry = $this->db->insert("other_cytologi",$header);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi Add Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$cytologiID = $this->db->insert_id();
if ($cytologiID == 0 ) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi ID Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
foreach($detail as $idx => $d ) {
$detail[$idx]["Other_CytologiDetailsOther_CytologiID"] = $cytologiID ;
$detail[$idx]["Other_CytologiDetailsUserID"] = 3;
$detail[$idx]["Other_CytologiDetailsIsActive"] = "Y";
unset($detail[$idx]["Other_CytologiDetailsID"]);
unset($detail[$idx]["Other_CytologiDetailsCreated"]);
unset($detail[$idx]["Other_CytologiDetailsLastUpdated"]);
}
$qry = $this->db->insert_batch("other_cytologidetails",$detail);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Cytologi Add Detail" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
//fna
function update_fna($detailID, $header, $detail ) {
$sql = "select * from t_orderdetail where T_OrderDetailID = ? ";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA cek Validation " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
if (count($rows) == 0 ) return;
if ($rows[0]["T_OrderDetailValidation"] == "Y" ) return;
$sql = "select Other_FNAID from other_fna where Other_FNAT_OrderDetailID = ? and Other_FNAIsActive = 'Y'";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA cek Existence " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
$fnaID = 0;
if (count($rows) > 0 ) $fnaID = $rows[0]["Other_FNAID"];
if ( $fnaID > 0 ) {
$sql = "update other_fna set Other_FNAIsActive = 'N' where Other_FNAID = ? ";
$qry = $this->db->query($sql, array($fnaID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_fnadetails set Other_FNADetailsIsActive = 'N' where Other_FNADetailsOther_FNAID= ? ";
$qry = $this->db->query($sql, array($fnaID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
unset($header["Other_FNAID"]);
$header["Other_FNACreated"] = date("Y-m-d h:i:s");
$header["Other_FNALastUpdated"] =date("Y-m-d h:i:s");
$header["Other_FNAUserID"] = 3;
$header["Other_FNAIsActive"] = "Y";
$header["Other_FNAT_OrderDetailID"] = $detailID;
$qry = $this->db->insert("other_fna",$header);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA Add Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$fnaID = $this->db->insert_id();
if ($fnaID == 0 ) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA ID Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
foreach($detail as $idx => $d ) {
$detail[$idx]["Other_FNADetailsOther_FNAID"] = $fnaID ;
$detail[$idx]["Other_FNADetailsUserID"] = 3;
$detail[$idx]["Other_FNADetailsIsActive"] = "Y";
unset($detail[$idx]["Other_FNADetailsID"]);
unset($detail[$idx]["Other_FNADetailsCreated"]);
unset($detail[$idx]["Other_FNADetailsLastUpdated"]);
}
$qry = $this->db->insert_batch("other_fnadetails",$detail);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError FNA Add Detail" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
// update papsmear
function update_papsmear ($detailID, $header, $detail ) {
$sql = "select * from t_orderdetail where T_OrderDetailID = ? ";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear cek Validation " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
if (count($rows) == 0 ) return;
if ($rows[0]["T_OrderDetailValidation"] == "Y" ) return;
$sql = "select Other_PapSmearID from other_papsmear where Other_PapSmearT_OrderDetailID = ? and Other_PapSmearIsActive = 'Y'";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear cek Existence " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
$papsmearID = 0;
if (count($rows) > 0 ) $papsmearID = $rows[0]["Other_PapSmearID"];
if ( $papsmearID > 0 ) {
$sql = "update other_papsmear set Other_PapSmearIsActive = 'N' where Other_PapSmearID = ? ";
$qry = $this->db->query($sql, array($papsmearID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_papsmearbahan set Other_PapSmearBahanIsActive = 'N' where Other_PapSmearBahanOther_PapSmearID= ? ";
$qry = $this->db->query($sql, array($papsmearID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_papsmearcategory set Other_PapSmearCategoryIsActive = 'N' where Other_PapSmearCategoryOther_PapSmearID= ? ";
$qry = $this->db->query($sql, array($papsmearID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Category set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_papsmearcheck set Other_PapSmearCheckIsActive = 'N' where Other_PapSmearCheckOther_PapSmearID= ? ";
$qry = $this->db->query($sql, array($papsmearID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Check set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_papsmeardetails set Other_PapSmearDetailsIsActive = 'N' where Other_PapSmearDetailsOther_PapSmearID= ? ";
$qry = $this->db->query($sql, array($papsmearID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_papsmearmaturasi set Other_PapSmearMaturasiIsActive = 'N' where Other_PapSmearMaturasiOther_PapSmearID= ? ";
$qry = $this->db->query($sql, array($papsmearID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
unset($header["Other_PapSmearID"]);
$header["Other_PapSmearCreated"] = date("Y-m-d h:i:s");
$header["Other_PapSmearLastUpdated"] =date("Y-m-d h:i:s");
$header["Other_PapSmearUserID"] = 3;
$header["Other_PapSmearIsActive"] = "Y";
$header["Other_PapSmearT_OrderDetailID"] = $detailID;
$qry = $this->db->insert("other_papsmear",$header);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Add Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$papsmearID= $this->db->insert_id();
if ($papsmearID == 0 ) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear ID Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
foreach($detail as $key => $d ) {
if ($key == "bahan") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_PapSmearBahanOther_PapSmearID"] = $papsmearID ;
$detail[$key][$idx]["Other_PapSmearBahanUserID"] = 3;
$detail[$key][$idx]["Other_PapSmearBahanIsActive"] = "Y";
unset($detail[$key][$idx]["Other_PapSmearBahanID"]);
unset($detail[$key][$idx]["Other_PapSmearBahanCreated"]);
unset($detail[$key][$idx]["Other_PapSmearBahanLastUpdated"]);
}
$qry = $this->db->insert_batch("other_papsmearbahan",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Add Bahan " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "category") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_PapSmearCategoryOther_PapSmearID"] = $papsmearID ;
$detail[$key][$idx]["Other_PapSmearCategoryUserID"] = 3;
$detail[$key][$idx]["Other_PapSmearCategoryIsActive"] = "Y";
unset($detail[$key][$idx]["Other_PapSmearCategoryID"]);
unset($detail[$key][$idx]["Other_PapSmearCategoryCreated"]);
unset($detail[$key][$idx]["Other_PapSmearCategoryLastUpdated"]);
}
$qry = $this->db->insert_batch("other_papsmearcategory",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Add Category " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "check") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_PapSmearCheckOther_PapSmearID"] = $papsmearID ;
$detail[$key][$idx]["Other_PapSmearCheckUserID"] = 3;
$detail[$key][$idx]["Other_PapSmearCheckIsActive"] = "Y";
unset($detail[$key][$idx]["Other_PapSmearCheckID"]);
unset($detail[$key][$idx]["Other_PapSmearCheckCreated"]);
unset($detail[$key][$idx]["Other_PapSmearCheckLastUpdated"]);
}
$qry = $this->db->insert_batch("other_papsmearcheck",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Add Check " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "details") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_PapSmearDetailsOther_PapSmearID"] = $papsmearID ;
$detail[$key][$idx]["Other_PapSmearDetailsUserID"] = 3;
$detail[$key][$idx]["Other_PapSmearDetailsIsActive"] = "Y";
unset($detail[$key][$idx]["Other_PapSmearDetailsID"]);
unset($detail[$key][$idx]["Other_PapSmearDetailsCreated"]);
unset($detail[$key][$idx]["Other_PapSmearDetailsLastUpdated"]);
}
$qry = $this->db->insert_batch("other_papsmeardetails",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Add Details " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "maturasi") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_PapSmearMaturasiOther_PapSmearID"] = $papsmearID ;
$detail[$key][$idx]["Other_PapSmearMaturasiUserID"] = 3;
$detail[$key][$idx]["Other_PapSmearMaturasiIsActive"] = "Y";
unset($detail[$key][$idx]["Other_PapSmearMaturasiID"]);
unset($detail[$key][$idx]["Other_PapSmearMaturasiCreated"]);
unset($detail[$key][$idx]["Other_PapSmearMaturasiLastUpdated"]);
}
$qry = $this->db->insert_batch("other_papsmearmaturasi",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError PapSmear Add Maturasi " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
}
}
function update_lcprep($detailID, $header, $detail ) {
$sql = "select * from t_orderdetail where T_OrderDetailID = ? ";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep cek Validation " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
if (count($rows) == 0 ) return;
if ($rows[0]["T_OrderDetailValidation"] == "Y" ) return;
$sql = "select Other_LcprepID from other_lcprep where Other_LcprepT_orderDetailID = ? and Other_LcprepIsActive = 'Y'";
$qry = $this->db->query($sql, array($detailID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep cek Existence " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$rows = $qry->result_array();
$lcprepID = 0;
if (count($rows) > 0 ) $lcprepID = $rows[0]["Other_LcprepID"];
if ( $lcprepID > 0 ) {
$sql = "update other_lcprep set Other_LcprepIsActive = 'N' where Other_LcprepID = ? ";
$qry = $this->db->query($sql, array($lcprepID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_lcprepadekuasi set Other_LcprepAdekuasiIsActive = 'N' where Other_LcprepAdekuasiOther_LcprepID= ? ";
$qry = $this->db->query($sql, array($lcprepID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Adekuasi set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_lcprepinterpretasi set Other_LcprepInterpretasiIsActive = 'N' where Other_LcprepInterpretasiOther_LcprepID= ? ";
$qry = $this->db->query($sql, array($lcprepID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Interpretasi set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_lcprepdetails set Other_LcprepDetailsIsActive = 'N' where Other_LcprepDetailsOther_LcprepID= ? ";
$qry = $this->db->query($sql, array($lcprepID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Details set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$sql = "update other_lcprepkategoriumum set Other_LcprepKategoriUmumIsActive = 'N' where Other_LcprepKategoriUmumOther_LcprepID= ? ";
$qry = $this->db->query($sql, array($lcprepID));
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep KategoriUmum set Inactive" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
unset($header["Other_LcprepID"]);
$header["Other_LcprepCreated"] = date("Y-m-d h:i:s");
$header["Other_LcprepLastUpdated"] =date("Y-m-d h:i:s");
$header["Other_LcprepUserID"] = 3;
$header["Other_LcprepIsActive"] = "Y";
$header["Other_LcprepT_orderDetailID"] = $detailID;
$qry = $this->db->insert("other_lcprep",$header);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Add Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
$lcprepID= $this->db->insert_id();
if ($lcprepID == 0 ) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep ID Header" . print_r($this->db->error(),true),FILE_APPEND);
return;
}
foreach($detail as $key => $d ) {
if ($key == "adekuasi") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_LcprepAdekuasiOther_LcprepID"] = $lcprepID ;
$detail[$key][$idx]["Other_LcprepAdekuasiUserID"] = 3;
$detail[$key][$idx]["Other_LcprepAdekuasiIsActive"] = "Y";
unset($detail[$key][$idx]["Other_LcprepAdekuasiID"]);
unset($detail[$key][$idx]["Other_LcprepAdekuasiCreated"]);
unset($detail[$key][$idx]["Other_LcprepAdekuasiLastUpdated"]);
}
$qry = $this->db->insert_batch("other_lcprepadekuasi",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Add Adekuasi " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "interpretasi") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_LcprepInterpretasiOther_LcprepID"] = $lcprepID ;
$detail[$key][$idx]["Other_LcprepInterpretasiUserID"] = 3;
$detail[$key][$idx]["Other_LcprepInterpretasiIsActive"] = "Y";
unset($detail[$key][$idx]["Other_LcprepInterpretasiID"]);
unset($detail[$key][$idx]["Other_LcprepInterpretasiCreated"]);
unset($detail[$key][$idx]["Other_LcprepInterpretasiLastUpdated"]);
}
$qry = $this->db->insert_batch("other_lcprepinterpretasi",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Add Interpretasi " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "details") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_LcprepDetailsOther_LcprepID"] = $lcprepID ;
$detail[$key][$idx]["Other_LcprepDetailsUserID"] = 3;
$detail[$key][$idx]["Other_LcprepDetailsIsActive"] = "Y";
unset($detail[$key][$idx]["Other_LcprepDetailsID"]);
unset($detail[$key][$idx]["Other_LcprepDetailsCreated"]);
unset($detail[$key][$idx]["Other_LcprepDetailsLastUpdated"]);
}
$qry = $this->db->insert_batch("other_lcprepdetails",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Add Details " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
if ($key == "kategoriumum") {
foreach($d as $idx => $xd) {
$detail[$key][$idx]["Other_LcprepKategoriUmumOther_LcprepID"] = $lcprepID ;
$detail[$key][$idx]["Other_LcprepKategoriUmumUserID"] = 3;
$detail[$key][$idx]["Other_LcprepKategoriUmumIsActive"] = "Y";
unset($detail[$key][$idx]["Other_LcprepKategoriUmumID"]);
unset($detail[$key][$idx]["Other_LcprepKategoriUmumCreated"]);
unset($detail[$key][$idx]["Other_LcprepKategoriUmumLastUpdated"]);
}
$qry = $this->db->insert_batch("other_lcprepkategoriumum",$detail[$key]);
if(!$qry) {
file_put_contents("/xtmp/dbg-xstatus_branch-v3.log", "\nError Lcprep Add KategoriUmum " . print_r($this->db->error(),true),FILE_APPEND);
return;
}
}
}
}
function update() {
$prm = $this->sys_input;
foreach($prm as $p) {
$deliveryOrderID = $p["incomingRefT_RefDeliveryOrderID"];
$detailID = $p["incomingRefDetailT_OrderDetailID"];
$normalValueID = $p["T_OrderDetailNat_NormalValueID"];
$result = $p["T_OrderDetailResult"];
$resultnote = $p["T_OrderDetailNote"];
$validation = $p["T_OrderDetailValidation"];
$verification = $p["T_OrderDetailVerification"];
$status = $p["incomingRefDetailStatus"];
$note = '';
if (isset($p["note"])) $note = $p["note"];
if ($validation == "Y") {
$ret = $this->update_delivery($deliveryOrderID,$detailID,"$result","VALIDATION",$note);
if ($ret) $ret = $this->update_order($detailID,$result,$resultnote,$normalValueID);
if (isset($p["nonlab_result"])) {
$type = $p["nonlab_result"]["type"];
$header = $p["nonlab_result"]["header"];
$detail = $p["nonlab_result"]["detail"];
if ($type == "mikro" ) {
$this->update_mikro($detailID,$header,$detail);
}
if ($type == "papsmear" ) {
$this->update_papsmear($detailID,$header,$detail);
}
if ($type == "lcprep" ) {
$this->update_lcprep($detailID,$header,$detail);
}
if ($type == "fna" ) {
$this->update_fna($detailID,$header,$detail);
}
if ($type == "cytologi" ) {
$this->update_cytologi($detailID,$header,$detail);
}
}
} elseif($verification == "Y") {
$ret = $this->update_delivery($deliveryOrderID,$detailID,"$result","VERIFICATION",$note);
} elseif($status != "N") {
$ret = $this->update_delivery($deliveryOrderID,$detailID,"Diterima");
} else {
$ret = $this->update_delivery($deliveryOrderID,$detailID,"Ditolak");
}
if (! $ret ) {
echo json_encode( array("status"=>"ERR", "message"=> print_r($this->db->error(),true) ));
exit;
}
}
echo json_encode( array("status"=>"OK", "message"=>""));
}
function update_delivery($deliveryOrderID,$detailID,$value,$stage = "", $note = "" ) {
$sql = "select T_RefDeliveryOrderDetailT_OrderDetailID ,
T_RefDeliveryOrderDetailID,
T_RefDeliveryOrderChildID
from t_ref_deliveryorder_child
join t_ref_deliveryorder_detail on
T_RefDeliveryOrderChildT_RefDeliveryOrderDetailID = T_RefDeliveryOrderDetailID
where T_RefDeliveryOrderDetailT_RefDeliveryOrderID = ?
and T_RefDeliveryOrderChildT_OrderDetailID = ?";
$qry = $this->db->query($sql, array($deliveryOrderID, $detailID));
$flag_child = false;
$child_id = 0;
$refDeliveryOrderDetailID = 0;
if ($qry) {
$rows = $qry->result_array();
if (count($rows) > 0 ) {
$flag_child = true;
$child_id = $rows[0]["T_RefDeliveryOrderChildID"];
$parentDetailID = $rows[0]["T_RefDeliveryOrderDetailID"];
}
}
if ($flag_child) {
$sql = "update t_ref_deliveryorder_child
set T_RefDeliveryOrderChildResult = ?,
T_RefDeliveryOrderChildStage= ? ,
T_RefDeliveryOrderChildNote = ?
where T_RefDeliveryOrderChildID=?";
$qry = $this->db->query($sql, array($value, $state, $note, $child_id));
$value = "...";
$detailID = $parentDetailID;
}
if ($flag_child) {
$sql = "update t_ref_deliveryorder_detail
set T_RefDeliveryOrderDetailResult = ?,
T_RefDeliveryOrderDetailStage = ?,
T_RefDeliveryOrderDetailNote = ?
where T_RefDeliveryOrderDetailT_RefDeliveryOrderID = ?
and T_RefDeliveryOrderDetailID = ?";
} else {
$sql = "update t_ref_deliveryorder_detail
set T_RefDeliveryOrderDetailResult = ?,
T_RefDeliveryOrderDetailStage = ?,
T_RefDeliveryOrderDetailNote = ?
where T_RefDeliveryOrderDetailT_RefDeliveryOrderID = ?
and T_RefDeliveryOrderDetailT_OrderDetailID = ?";
}
$qry = $this->db->query($sql, array($value, $stage, $note, $deliveryOrderID, $detailID));
return $qry;
}
function update_order($detailID,$value,$resultnote,$normalValueID) {
$verUserID = 3;
$sql = "update t_orderdetail
set T_OrderDetailResult = ?,
T_OrderDetailNote = ?,
T_OrderDetailNat_NormalValueID = ?,
T_OrderDetailVerification = 'Y',
T_OrderDetailVerDate = now(),
T_OrderDetailVerUserID = $verUserID
where T_OrderDetailID = ?";
$qry = $this->db->query($sql, array($value,$resultnote, $normalValueID, $detailID));
if ( $qry) {
$sql = "update t_orderdetail, nat_normalvalue, nat_methode
set T_OrderDetailNat_MethodeID = Nat_NormalValueNat_MethodeID ,
T_OrderDetailNat_MethodeName = Nat_MethodeName,
T_OrderDetailNormalValueNote = Nat_NormalValueNote,
T_OrderDetailNormalValueDescription = Nat_NormalValueDescription,
T_OrderDetailMinValue = Nat_NormalValueMinValue,
T_OrderDetailMaxValue = Nat_NormalValueMaxValue,
T_OrderDetailMinValueInclusive = Nat_NormalValueMinValueInclusive,
T_OrderDetailMaxValueInclusive = Nat_NormalValueMaxValueInclusive
where T_OrderDetailID = ?
and T_OrderDetailNat_NormalValueID = Nat_NormalValueID
and Nat_NormalValueNat_MethodeID = Nat_MethodeID";
$qryn = $this->db->query($sql, array($detailID));
//if (! $qryn ) print_r($this->db->error());
if (! $qryn ) return false;
$sql = "call sp_set_normal_value_flag(?)";
$qryu = $this->db->query($sql, array($detailID));
//if (! $qryu ) print_r($this->db->error());
if (! $qryu ) return false;
$this->clean_mysqli_connection($this->db->conn_id);
}
return $qry;
}
}

View File

@@ -0,0 +1,87 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Ibl_encryptor
{
private $key;
private $search_key;
private $cipher = 'aes-256-gcm';
private $tag_length = 16;
public function __construct()
{
$passphrase = $_ENV['IBL_ENCRYPT_KEY'] ?? '';
$passphrase_s = $_ENV['IBL_ENCRYPT_SEARCH_KEY'] ?? '';
if ($passphrase === '' || $passphrase_s === '') {
log_message('error', 'Ibl_encryptor: IBL_ENCRYPT_KEY atau IBL_ENCRYPT_SEARCH_KEY kosong di .env');
}
// Derive 32-byte key dari passphrase via SHA-256
$this->key = hash('sha256', $passphrase, true);
$this->search_key = hash('sha256', $passphrase_s, true);
}
// Enkripsi plaintext → base64(iv[12] + tag[16] + ciphertext)
public function encrypt($plaintext)
{
if ($plaintext === null || $plaintext === '') return null;
$iv = random_bytes(12);
$tag = '';
$ct = openssl_encrypt((string)$plaintext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag, '', $this->tag_length);
if ($ct === false) return null;
return base64_encode($iv . $tag . $ct);
}
// Dekripsi base64(iv + tag + ciphertext) → plaintext
public function decrypt($ciphertext)
{
if ($ciphertext === null || $ciphertext === '') return null;
$raw = base64_decode($ciphertext);
if (strlen($raw) < 12 + $this->tag_length) return null;
$iv = substr($raw, 0, 12);
$tag = substr($raw, 12, $this->tag_length);
$ct = substr($raw, 12 + $this->tag_length);
$pt = openssl_decrypt($ct, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag);
return $pt === false ? null : $pt;
}
// Hasilkan JSON array trigram token untuk kolom _bidx (partial search)
public function search_bidx($value)
{
if ($value === null || $value === '') return null;
$norm = mb_strtolower(trim((string)$value), 'UTF-8');
$len = mb_strlen($norm, 'UTF-8');
$tokens = [];
if ($len <= 3) {
$tokens[] = $this->_token($norm);
} else {
for ($i = 0; $i <= $len - 3; $i++) {
$tokens[] = $this->_token(mb_substr($norm, $i, 3, 'UTF-8'));
}
}
return json_encode(array_values(array_unique($tokens)));
}
// Hasilkan array trigram token dari query string (untuk WHERE JSON_CONTAINS)
public function query_tokens($value)
{
if ($value === null || $value === '') return [];
$norm = mb_strtolower(trim((string)$value), 'UTF-8');
$len = mb_strlen($norm, 'UTF-8');
$tokens = [];
if ($len <= 3) {
$tokens[] = $this->_token($norm);
} else {
for ($i = 0; $i <= $len - 3; $i++) {
$tokens[] = $this->_token(mb_substr($norm, $i, 3, 'UTF-8'));
}
}
return array_values(array_unique($tokens));
}
private function _token($str)
{
return hash_hmac('sha256', $str, $this->search_key);
}
}

View File

@@ -10,6 +10,7 @@ class Ibl_merge_report_gateway
{
$this->CI = &get_instance();
$this->db_onedev = $this->CI->load->database('onedev', true);
$this->CI->load->library('ibl_patient_decrypt');
}
public function create_merge_request_from_lab_number($labNumber, $creatorUserId, $customName = '')
@@ -561,6 +562,56 @@ class Ibl_merge_report_gateway
);
}
public function stream_from_qr_printout($orderHeaderId)
{
$query = $this->db_onedev->query(
"SELECT QR_PrintOutReportURLElectronic
FROM qr_printout
WHERE QR_PrintOutT_OrderHeaderID = ?
AND QR_PrintOutReportURLElectronic != ''
AND QR_PrintOutIsActive = 1
ORDER BY QR_PrintOutGroup_ResultID ASC",
array((int) $orderHeaderId)
);
if (!$query || $query->num_rows() === 0) {
return $this->error('QR_PRINTOUT_NOT_FOUND', 'Tidak ada URL report di qr_printout untuk order ini.');
}
$urls = array();
$seen = array();
foreach ($query->result_array() as $row) {
$url = trim($row['QR_PrintOutReportURLElectronic']);
if ($url === '' || isset($seen[$url])) {
continue;
}
$seen[$url] = true;
$url = str_replace('http://localhost/', 'http://127.0.0.1/', $url);
$urls[] = $url;
}
if (count($urls) === 0) {
return $this->error('QR_PRINTOUT_EMPTY', 'URL report kosong setelah normalisasi.');
}
// Populate decrypt cache sebelum Go merge service fetch PDF dari BIRT
$cache_id = $this->CI->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId);
$payload = array(
'name' => 'merge-' . (int) $orderHeaderId . '.pdf',
'urls' => $urls,
'mergeRequestID' => (int) $orderHeaderId,
'T_OrderHeaderID' => (int) $orderHeaderId,
);
$result = $this->call_merge_service($payload);
// Hapus cache setelah merge service selesai
$this->CI->ibl_patient_decrypt->delete_cache($cache_id);
return $result;
}
protected function call_merge_service(array $payload)
{
$config = $this->get_system_config();

View File

@@ -0,0 +1,132 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Helper untuk decrypt PII pasien sebelum call SP/BIRT
* Populate patient_print_cache, run callback, delete cache
*/
class Ibl_patient_decrypt
{
private $db;
private $enc;
public function __construct()
{
$CI = &get_instance();
$this->db = $CI->load->database('onedev', true);
$CI->load->library('ibl_encryptor');
$this->enc = $CI->ibl_encryptor;
}
// Populate cache, return cache_id untuk cleanup
public function populate_cache_by_order($order_id)
{
$order_id = intval($order_id);
if (!$order_id) return null;
$patient = $this->db->query(
"SELECT M_PatientID, M_PatientName_enc, M_PatientDOB_enc,
M_PatientHP_enc, M_PatientEmail_enc, M_PatientDOB
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ? LIMIT 1",
[$order_id]
)->row_array();
if (!$patient) return null;
$addr = $this->db->query(
"SELECT M_PatientAddressDescription_enc FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = ?
AND M_PatientAddressIsActive = 'Y'
AND M_PatientAddressNote = 'Utama'
LIMIT 1",
[$patient['M_PatientID']]
)->row_array();
$enc = $this->enc;
$this->_insert_cache(
$order_id,
$patient['M_PatientID'],
$enc->decrypt($patient['M_PatientName_enc'] ?? '') ?? '',
$enc->decrypt($patient['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($patient['M_PatientDOB'] ?? 'now')),
$enc->decrypt($patient['M_PatientHP_enc'] ?? '') ?? '',
$enc->decrypt($patient['M_PatientEmail_enc']?? '') ?? '',
$enc->decrypt($addr['M_PatientAddressDescription_enc'] ?? '') ?? ''
);
return $this->db->insert_id();
}
// Decrypt langsung dari query result (untuk controller dengan SQL sendiri)
public function decrypt_row(array $row): array
{
$enc = $this->enc;
if (!empty($row['M_PatientName_enc'])) $row['M_PatientName'] = $enc->decrypt($row['M_PatientName_enc']) ?? $row['M_PatientName'] ?? '';
if (!empty($row['M_PatientDOB_enc'])) $row['M_PatientDOB'] = $enc->decrypt($row['M_PatientDOB_enc']) ?? $row['M_PatientDOB'] ?? '';
if (!empty($row['M_PatientHP_enc'])) $row['M_PatientHP'] = $enc->decrypt($row['M_PatientHP_enc']) ?? '';
if (!empty($row['M_PatientEmail_enc'])) $row['M_PatientEmail'] = $enc->decrypt($row['M_PatientEmail_enc']) ?? '';
if (!empty($row['M_PatientPOB_enc'])) $row['M_PatientPOB'] = $enc->decrypt($row['M_PatientPOB_enc']) ?? '';
if (!empty($row['M_PatientAddressDescription_enc'])) $row['M_PatientAddressDescription'] = $enc->decrypt($row['M_PatientAddressDescription_enc']) ?? '';
if (!empty($row['phone_enc'])) $row['phone'] = $enc->decrypt($row['phone_enc']) ?? '';
if (!empty($row['alamat_enc'])) $row['alamat'] = $enc->decrypt($row['alamat_enc']) ?? '';
if (!empty($row['dob_enc'])) $row['dob'] = $enc->decrypt($row['dob_enc']) ?? '';
foreach (array_keys($row) as $k) { if (substr($k, -4) === '_enc') unset($row[$k]); }
return $row;
}
// Hapus cache by id
public function delete_cache($cache_id)
{
if ($cache_id) {
$this->db->query("DELETE FROM patient_print_cache WHERE ppc_id = ?", [$cache_id]);
}
// Cleanup expired juga
$this->db->query("DELETE FROM patient_print_cache WHERE ppc_created < NOW() - INTERVAL 5 MINUTE");
}
// Fetch PDF dari BIRT dengan auto decrypt cache
// Ganti semua file_get_contents($birt_url) dengan ini
public function fetch_birt_pdf($birt_relative_url)
{
// Parse PID dari URL query string
parse_str(parse_url($birt_relative_url, PHP_URL_QUERY) ?? '', $params);
$order_id = intval($params['PID'] ?? 0);
$cache_id = $order_id ? $this->populate_cache_by_order($order_id) : null;
// Build full internal URL ke BIRT server
$full_url = 'http://localhost:8080' . $birt_relative_url;
$context = stream_context_create(['http' => ['timeout' => 120]]);
$pdf = @file_get_contents($full_url, false, $context);
$this->delete_cache($cache_id);
return $pdf;
}
// Populate cache lalu return URL (untuk controller yang return URL ke frontend)
// Cache hidup 5 menit — cukup untuk browser buka BIRT
public function pre_cache_and_get_url($birt_relative_url)
{
parse_str(parse_url($birt_relative_url, PHP_URL_QUERY) ?? '', $params);
$order_id = intval($params['PID'] ?? 0);
if ($order_id) {
$this->populate_cache_by_order($order_id);
}
return $birt_relative_url;
}
private function _insert_cache($order_id, $patient_id, $name, $dob, $hp, $email, $address)
{
$this->db->query(
"DELETE FROM patient_print_cache WHERE ppc_order_id = ? OR ppc_created < NOW() - INTERVAL 5 MINUTE",
[$order_id]
);
$this->db->query(
"INSERT INTO patient_print_cache (ppc_order_id, ppc_patient_id, ppc_name, ppc_dob, ppc_hp, ppc_email, ppc_address, ppc_created)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())",
[$order_id, $patient_id, $name, $dob, $hp, $email, $address]
);
}
}

View File

@@ -0,0 +1,546 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Pengganti fn_sampling_get_normal, sp_sampling_check_normal_setting,
* dan sp_sampling_fix_normal_by_orderdetail (MySQL).
*
* Alasan: SP/fungsi MySQL membaca M_PatientDOB langsung dari m_patient,
* yang NULL setelah enkripsi PDP. Library ini decrypt M_PatientDOB_enc
* di PHP sebelum menghitung usia.
*/
class Ibl_sampling_normal
{
private $db;
public function __construct()
{
$CI = &get_instance();
$this->db = $CI->load->database('onedev', true);
}
// -------------------------------------------------------------------------
// Public API
// -------------------------------------------------------------------------
/**
* Cari Nat_NormalValueID sesuai methode, test, sex, dan umur (dalam hari).
* Urutan prioritas:
* Type 1 — sex + age range
* Type 2 — age range saja
* Type 3 — sex saja
* Type 4 — fallback
*/
public function get_normal_value_id($methodeID, $natTestID, $sexID, $ageInDay)
{
$methodeID = intval($methodeID);
$natTestID = intval($natTestID);
$sexID = intval($sexID);
$ageInDay = intval($ageInDay);
$min_days = $this->_age_sql('Nat_NormalValueMinAge');
$max_days = $this->_age_sql('Nat_NormalValueMaxAge');
$age_filter = "
AND (
(Nat_NormalValueMinAgeInclusive = 'Y' AND ($min_days) <= $ageInDay)
OR (Nat_NormalValueMinAgeInclusive = 'N' AND ($min_days) < $ageInDay)
)
AND (
(Nat_NormalValueMaxAgeInclusive = 'Y' AND ($max_days) >= $ageInDay)
OR (Nat_NormalValueMaxAgeInclusive = 'N' AND ($max_days) > $ageInDay)
)";
$base = "FROM nat_normalvalue
WHERE (Nat_NormalValueValidDate IS NULL OR Nat_NormalValueValidDate < NOW())
AND Nat_NormalValueIsActive = 'Y'
AND Nat_NormalValueIsAbnormal = 'N'
AND Nat_NormalValueNat_MethodeID = $methodeID
AND Nat_NormalValueNat_TestID = $natTestID
AND Nat_NormalValueNat_FlagID = 1";
$id = $this->_query_one("SELECT Nat_NormalValueID $base
AND Nat_NormalValueNat_NormalValueTypeID = 1
AND Nat_NormalValueNat_SexID = $sexID
$age_filter LIMIT 1");
if ($id) return $id;
$id = $this->_query_one("SELECT Nat_NormalValueID $base
AND Nat_NormalValueNat_NormalValueTypeID = 2
$age_filter LIMIT 1");
if ($id) return $id;
$id = $this->_query_one("SELECT Nat_NormalValueID $base
AND Nat_NormalValueNat_NormalValueTypeID = 3
AND Nat_NormalValueNat_SexID = $sexID
LIMIT 1");
if ($id) return $id;
$id = $this->_query_one("SELECT Nat_NormalValueID $base
AND Nat_NormalValueNat_NormalValueTypeID = 4
LIMIT 1");
if ($id) return $id;
return 0;
}
/**
* Pengganti sp_sampling_check_normal_setting.
* Mengembalikan array compat dengan call_multi_result_procedure():
* ok, message, result_sets[0]=summary, [1]=checks, [2]=branches, [3]=candidates
*/
public function check_setting_by_order_detail($orderDetailID)
{
$orderDetailID = intval($orderDetailID);
$data = $this->_load_order_detail_data($orderDetailID);
if ($data === null) {
return [
'ok' => false,
'message' => "T_OrderDetailID {$orderDetailID} tidak ditemukan atau tidak aktif",
'result_sets' => [],
];
}
list($sexID, $dobStr, $orderDate, $ageInDay,
$orderHeaderID, $patientID, $natTestID,
$row) = $data;
$methods = $this->_resolve_method_sources($natTestID);
list($selID, $selName, $selSource) = $this->_pick_method($methods);
$normalValueID = 0;
if ($selID > 0 && $natTestID > 0 && $sexID > 0 && $ageInDay !== null) {
$normalValueID = $this->get_normal_value_id($selID, $natTestID, $sexID, $ageInDay);
}
$summary = [[
'T_OrderDetailID' => $orderDetailID,
'T_OrderHeaderID' => $orderHeaderID,
'M_PatientID' => $patientID,
'M_PatientM_SexID' => $sexID ?: null,
'M_PatientDOB' => $dobStr ?: null,
'T_OrderHeaderDate' => $row['order_date'],
'AgeInDay' => $ageInDay,
'T_TestID' => intval($row['t_test_id']),
'T_TestName' => $row['t_test_name'],
'Nat_TestID' => $natTestID ?: null,
'Nat_TestCode' => $row['nat_test_code'],
'Nat_TestName' => $row['nat_test_name'],
'T_OrderDetailNat_MethodeID'=> intval($row['order_detail_methode_id']),
'MethodeIDFromPriority' => $methods['priority_id'] ?: null,
'MethodeNameFromPriority' => $methods['priority_name'],
'MethodeIDFromInstrument' => $methods['instrument_id'] ?: null,
'MethodeNameFromInstrument' => $methods['instrument_name'],
'MethodeIDFromNormalValue' => $methods['normalvalue_id'] ?: null,
'MethodeNameFromNormalValue'=> $methods['normalvalue_name'],
'SelectedMethodeID' => $selID,
'SelectedMethodeName' => $selName,
'SelectedMethodeSource' => $selSource,
'FnSamplingGetNormalResult' => $normalValueID,
]];
$nvCount = 0;
if ($natTestID > 0) {
$r = $this->db->query(
"SELECT COUNT(*) AS cnt FROM nat_normalvalue
WHERE Nat_NormalValueNat_TestID = ?
AND Nat_NormalValueNat_MethodeID = ?
AND Nat_NormalValueIsActive = 'Y'
AND Nat_NormalValueIsAbnormal = 'N'",
[$natTestID, $selID ?: 0]
)->row_array();
$nvCount = intval($r['cnt'] ?? 0);
}
$checks = [
$this->_chk('ORDER_DETAIL_ACTIVE', true, (string) $orderDetailID, 't_orderdetail aktif harus ditemukan'),
$this->_chk('PATIENT_SEX', $sexID > 0, (string) ($sexID ?: 'NULL'), 'Dipakai pada type 1 dan type 3'),
$this->_chk('PATIENT_DOB', $dobStr !== '', ($dobStr ?: 'NULL'), 'DOB dipakai untuk hitung AgeInDay'),
$this->_chk('ORDER_DATE', !empty($row['order_date']), ($row['order_date'] ?: 'NULL'), 'Tanggal order dipakai untuk hitung AgeInDay'),
$this->_chk('AGE_IN_DAY', $ageInDay !== null, ($ageInDay !== null ? (string) $ageInDay : 'NULL'), 'Dipakai pada type 1 dan type 2'),
$this->_chk('NAT_TEST', $natTestID > 0, (string) ($natTestID ?: 'NULL'), 'Harus ada mapping T_TestNat_TestID'),
$this->_chk('METHOD_PRIORITY', $methods['priority_id'] > 0, (string) ($methods['priority_id'] ?: 'NULL'), 'Sumber metode prioritas pertama'),
$this->_chk('METHOD_INSTRUMENT', $methods['instrument_id'] > 0, (string) ($methods['instrument_id'] ?: 'NULL'), 'Fallback metode kedua'),
$this->_chk('METHOD_NORMALVALUE', $methods['normalvalue_id'] > 0, (string) ($methods['normalvalue_id'] ?: 'NULL'), 'Fallback metode ketiga'),
$this->_chk('SELECTED_METHOD', $selID > 0, (string) ($selID ?: 'NULL'), ($selSource ?: 'Tidak ada metode yang bisa dipakai')),
$this->_chk('NORMALVALUE_TEST_METHOD_ACTIVE', $nvCount > 0, (string) $nvCount, 'Jumlah nat_normalvalue aktif non-abnormal untuk test + metode terpilih'),
['check_name' => 'FN_RESULT', 'check_status' => ($normalValueID > 0 ? 'OK' : 'NOT_FOUND'), 'check_value' => (string) $normalValueID, 'check_note' => 'Hasil akhir fn_sampling_get_normal'],
];
$branches = $selID > 0
? $this->_query_branches($selID, $natTestID, $sexID, $ageInDay)
: [['branch_name' => 'NO_SELECTED_METHOD', 'matched_count' => 0, 'first_normalvalue_id' => null]];
$candidates = $natTestID > 0 ? $this->_query_candidates($selID, $natTestID, $sexID, $ageInDay) : [];
return [
'ok' => true,
'message' => '',
'result_sets' => [$summary, $checks, $branches, $candidates],
];
}
/**
* Pengganti sp_sampling_fix_normal_by_orderdetail.
* Mengembalikan array compat dengan call_multi_result_procedure():
* ok, message, result_sets[0]=result_row, [1]=order_detail_row
*/
public function fix_by_order_detail($orderDetailID)
{
$orderDetailID = intval($orderDetailID);
$data = $this->_load_order_detail_data($orderDetailID);
if ($data === null) {
$errRow = [[
'status' => 'ERR',
'message' => "T_OrderDetailID {$orderDetailID} tidak ditemukan atau tidak aktif",
'selected_methode_id' => null,
'selected_methode_name'=> null,
'selected_methode_source' => null,
'nat_normalvalue_id' => null,
'updated_rows' => 0,
]];
return ['ok' => true, 'message' => '', 'result_sets' => [$errRow, []]];
}
list($sexID, $dobStr, $orderDate, $ageInDay,
$orderHeaderID, $patientID, $natTestID,
$row) = $data;
$methods = $this->_resolve_method_sources($natTestID);
list($selID, $selName, $selSource) = $this->_pick_method($methods);
$normalValueID = 0;
if ($selID > 0 && $natTestID > 0 && $sexID > 0 && $ageInDay !== null) {
$normalValueID = $this->get_normal_value_id($selID, $natTestID, $sexID, $ageInDay);
}
if ($normalValueID > 0) {
$this->db->query(
"UPDATE t_orderdetail od
JOIN nat_normalvalue nn ON nn.Nat_NormalValueID = ?
SET od.T_OrderDetailNat_NormalValueID = nn.Nat_NormalValueID,
od.T_OrderDetailNormalValueNote = nn.Nat_NormalValueNote,
od.T_OrderDetailNormalValueDescription = nn.Nat_NormalValueDescription,
od.T_OrderDetailMinValue = nn.Nat_NormalValueMinValue,
od.T_OrderDetailMaxValue = nn.Nat_NormalValueMaxValue,
od.T_OrderDetailMinValueInclusive = nn.Nat_NormalValueMinValueInclusive,
od.T_OrderDetailMaxValueInclusive = nn.Nat_NormalValueMaxValueInclusive,
od.T_OrderDetailNat_MethodeID = ?,
od.T_OrderdetailNat_MethodeName = ?
WHERE od.T_OrderDetailID = ?
AND od.T_OrderDetailIsActive = 'Y'",
[$normalValueID, $selID, $selName, $orderDetailID]
);
$updatedRows = $this->db->affected_rows();
$resultRow = [[
'status' => 'OK',
'message' => "Normal value berhasil diupdate untuk T_OrderDetailID {$orderDetailID}",
'selected_methode_id' => $selID,
'selected_methode_name'=> $selName,
'selected_methode_source' => $selSource,
'nat_normalvalue_id' => $normalValueID,
'updated_rows' => $updatedRows,
]];
} else {
$resultRow = [[
'status' => 'NOT_FOUND',
'message' => "Normal value tidak ditemukan untuk T_OrderDetailID {$orderDetailID}",
'selected_methode_id' => $selID,
'selected_methode_name'=> $selName,
'selected_methode_source' => $selSource,
'nat_normalvalue_id' => $normalValueID,
'updated_rows' => 0,
]];
}
$odRow = $this->db->query(
"SELECT T_OrderDetailID, T_OrderDetailNat_NormalValueID, T_OrderDetailNat_MethodeID,
T_OrderdetailNat_MethodeName, T_OrderDetailNormalValueDescription,
T_OrderDetailMinValue, T_OrderDetailMaxValue,
T_OrderDetailMinValueInclusive, T_OrderDetailMaxValueInclusive
FROM t_orderdetail WHERE T_OrderDetailID = ? LIMIT 1",
[$orderDetailID]
)->result_array();
return ['ok' => true, 'message' => '', 'result_sets' => [$resultRow, $odRow]];
}
// -------------------------------------------------------------------------
// Private helpers
// -------------------------------------------------------------------------
/** Inline pengganti fn_normal_get_age: konversi kolom usia ke hari di SQL. */
private function _age_sql($col)
{
return "CASE Nat_NormalValueAgeUnit
WHEN 'HARI' THEN {$col}
WHEN 'BULAN' THEN {$col} * 30
WHEN 'TAHUN' THEN {$col} * 365
ELSE {$col}
END";
}
/**
* Ambil data dasar order detail termasuk DOB terenkripsi.
* Return [sexID, dobStr, orderDate, ageInDay, orderHeaderID, patientID, natTestID, $row]
* atau null jika tidak ditemukan.
*/
private function _load_order_detail_data($orderDetailID)
{
$row = $this->db->query(
"SELECT
od.T_OrderDetailT_OrderHeaderID AS order_header_id,
oh.T_OrderHeaderM_PatientID AS patient_id,
p.M_PatientM_SexID AS sex_id,
p.M_PatientDOB_enc,
p.M_PatientDOB,
DATE(oh.T_OrderHeaderDate) AS order_date,
od.T_OrderDetailT_TestID AS t_test_id,
tt.T_TestName AS t_test_name,
tt.T_TestNat_TestID AS nat_test_id,
nt.Nat_TestCode AS nat_test_code,
nt.Nat_TestName AS nat_test_name,
od.T_OrderDetailNat_MethodeID AS order_detail_methode_id
FROM t_orderdetail od
JOIN t_orderheader oh ON oh.T_OrderHeaderID = od.T_OrderDetailT_OrderHeaderID
JOIN m_patient p ON p.M_PatientID = oh.T_OrderHeaderM_PatientID
LEFT JOIN t_test tt ON tt.T_TestID = od.T_OrderDetailT_TestID
LEFT JOIN nat_test nt ON nt.Nat_TestID = tt.T_TestNat_TestID
WHERE od.T_OrderDetailID = ?
AND od.T_OrderDetailIsActive = 'Y'
LIMIT 1",
[$orderDetailID]
)->row_array();
if (!$row) return null;
$dobStr = '';
if (!empty($row['M_PatientDOB_enc'])) {
$CI = &get_instance();
$CI->load->library('ibl_encryptor');
$dobStr = (string) ($CI->ibl_encryptor->decrypt($row['M_PatientDOB_enc']) ?? '');
}
if ($dobStr === '' && !empty($row['M_PatientDOB'])) {
$dobStr = $row['M_PatientDOB'];
}
$ageInDay = null;
if ($dobStr !== '' && !empty($row['order_date'])) {
try {
$dob = new DateTime($dobStr);
$oDate = new DateTime($row['order_date']);
$ageInDay = (int) $oDate->diff($dob)->days;
} catch (Exception $e) {}
}
return [
intval($row['sex_id']),
$dobStr,
$row['order_date'],
$ageInDay,
intval($row['order_header_id']),
intval($row['patient_id']),
intval($row['nat_test_id']),
$row,
];
}
/** Resolusi metode dari 3 sumber: priority → instrument → normalvalue. */
private function _resolve_method_sources($natTestID)
{
$r = $this->db->query(
"SELECT mp.M_MethodePriorityNat_MethodeID, nm.Nat_MethodeName
FROM m_methode_priority mp
JOIN nat_methode nm ON nm.Nat_MethodeID = mp.M_MethodePriorityNat_MethodeID AND nm.Nat_MethodeIsActive = 'Y'
JOIN m_instrumentmethode im ON im.M_InstrumentMethodeNat_MethodeID = nm.Nat_MethodeID AND im.M_InstrumentMethodeIsActive = 'Y'
JOIN nat_instrument ni ON ni.Nat_InstrumentID = im.M_InstrumentMethodeNat_InstrumentID AND ni.Nat_InstrumentIsActive = 'Y'
WHERE mp.M_MethodePriorityIsActive = 'Y'
AND mp.M_MethodePriorityNat_TestID = ?
AND mp.M_MethodePriorityM_DayOfWeekID = DAYOFWEEK(NOW())
ORDER BY mp.M_MethodePriorityNumber DESC LIMIT 1",
[$natTestID]
)->row_array();
$priorityID = intval($r['M_MethodePriorityNat_MethodeID'] ?? 0);
$priorityName = $r['Nat_MethodeName'] ?? null;
$r = $this->db->query(
"SELECT im.M_InstrumentMethodeNat_MethodeID, nm.Nat_MethodeName
FROM m_instrumentmethode im
JOIN nat_methode nm ON nm.Nat_MethodeID = im.M_InstrumentMethodeNat_MethodeID AND nm.Nat_MethodeIsActive = 'Y'
JOIN nat_instrument ni ON ni.Nat_InstrumentID = im.M_InstrumentMethodeNat_InstrumentID AND ni.Nat_InstrumentIsActive = 'Y'
WHERE im.M_InstrumentMethodeNat_TestID = ?
AND im.M_InstrumentMethodeIsActive = 'Y'
ORDER BY im.M_InstrumentMethodePriority DESC LIMIT 1",
[$natTestID]
)->row_array();
$instrumentID = intval($r['M_InstrumentMethodeNat_MethodeID'] ?? 0);
$instrumentName = $r['Nat_MethodeName'] ?? null;
$r = $this->db->query(
"SELECT nn.Nat_NormalValueNat_MethodeID, nm.Nat_MethodeName
FROM nat_normalvalue nn
JOIN nat_methode nm ON nm.Nat_MethodeID = nn.Nat_NormalValueNat_MethodeID AND nm.Nat_MethodeIsActive = 'Y'
WHERE nn.Nat_NormalValueNat_TestID = ?
AND nn.Nat_NormalValueIsActive = 'Y'
ORDER BY nn.Nat_NormalValueID LIMIT 1",
[$natTestID]
)->row_array();
$normalID = intval($r['Nat_NormalValueNat_MethodeID'] ?? 0);
$normalName = $r['Nat_MethodeName'] ?? null;
return [
'priority_id' => $priorityID,
'priority_name' => $priorityName,
'instrument_id' => $instrumentID,
'instrument_name'=> $instrumentName,
'normalvalue_id' => $normalID,
'normalvalue_name' => $normalName,
];
}
/** Pilih metode terbaik dari hasil _resolve_method_sources(). */
private function _pick_method(array $methods)
{
if ($methods['priority_id'] > 0) {
return [$methods['priority_id'], $methods['priority_name'], 'm_methode_priority'];
}
if ($methods['instrument_id'] > 0) {
return [$methods['instrument_id'], $methods['instrument_name'], 'm_instrumentmethode'];
}
if ($methods['normalvalue_id'] > 0) {
return [$methods['normalvalue_id'], $methods['normalvalue_name'], 'nat_normalvalue'];
}
return [0, null, null];
}
/** Hitung matched_count per TYPE (1..4) untuk branch summary. */
private function _query_branches($methodeID, $natTestID, $sexID, $ageInDay)
{
$min = $this->_age_sql('Nat_NormalValueMinAge');
$max = $this->_age_sql('Nat_NormalValueMaxAge');
$base = "FROM nat_normalvalue
WHERE (Nat_NormalValueValidDate IS NULL OR Nat_NormalValueValidDate < NOW())
AND Nat_NormalValueIsActive = 'Y'
AND Nat_NormalValueIsAbnormal = 'N'
AND Nat_NormalValueNat_FlagID = 1
AND Nat_NormalValueNat_MethodeID = $methodeID
AND Nat_NormalValueNat_TestID = $natTestID";
$ageFilter = "
AND ((Nat_NormalValueMinAgeInclusive='Y' AND ($min)<=$ageInDay) OR (Nat_NormalValueMinAgeInclusive='N' AND ($min)<$ageInDay))
AND ((Nat_NormalValueMaxAgeInclusive='Y' AND ($max)>=$ageInDay) OR (Nat_NormalValueMaxAgeInclusive='N' AND ($max)>$ageInDay))";
$types = [
['TYPE_1', "AND Nat_NormalValueNat_NormalValueTypeID=1 AND Nat_NormalValueNat_SexID=$sexID $ageFilter"],
['TYPE_2', "AND Nat_NormalValueNat_NormalValueTypeID=2 $ageFilter"],
['TYPE_3', "AND Nat_NormalValueNat_NormalValueTypeID=3 AND Nat_NormalValueNat_SexID=$sexID"],
['TYPE_4', "AND Nat_NormalValueNat_NormalValueTypeID=4"],
];
$branches = [];
foreach ($types as list($label, $filter)) {
$r = $this->db->query("SELECT COUNT(*) AS cnt, MIN(Nat_NormalValueID) AS first_id $base $filter")->row_array();
$branches[] = [
'branch_name' => $label,
'matched_count' => intval($r['cnt'] ?? 0),
'first_normalvalue_id'=> $r['first_id'] ?? null,
];
}
return $branches;
}
/** Ambil semua kandidat nat_normalvalue dengan kolom diagnostik match/fail. */
private function _query_candidates($methodeID, $natTestID, $sexID, $ageInDay)
{
if (!$natTestID) return [];
$min = $this->_age_sql('Nat_NormalValueMinAge');
$max = $this->_age_sql('Nat_NormalValueMaxAge');
$a = intval($ageInDay);
$s = intval($sexID);
$m = intval($methodeID);
$matchMinAge = "(
(Nat_NormalValueMinAgeInclusive='Y' AND ($min)<=$a)
OR (Nat_NormalValueMinAgeInclusive='N' AND ($min)<$a))";
$matchMaxAge = "(
(Nat_NormalValueMaxAgeInclusive='Y' AND ($max)>=$a)
OR (Nat_NormalValueMaxAgeInclusive='N' AND ($max)>$a))";
$sql = "SELECT
nn.Nat_NormalValueID,
nn.Nat_NormalValueNat_TestID,
nn.Nat_NormalValueNat_MethodeID,
nm.Nat_MethodeName,
nn.Nat_NormalValueNat_NormalValueTypeID,
nn.Nat_NormalValueNat_SexID,
nn.Nat_NormalValueValidDate,
nn.Nat_NormalValueMinAge,
nn.Nat_NormalValueMaxAge,
nn.Nat_NormalValueAgeUnit,
nn.Nat_NormalValueMinAgeInclusive,
nn.Nat_NormalValueMaxAgeInclusive,
($min) AS MinAgeInDay,
($max) AS MaxAgeInDay,
nn.Nat_NormalValueNat_FlagID,
nn.Nat_NormalValueIsAbnormal,
nn.Nat_NormalValueIsActive,
IF((nn.Nat_NormalValueValidDate IS NULL OR nn.Nat_NormalValueValidDate < NOW()),'Y','N') AS MatchValidDate,
IF(nn.Nat_NormalValueIsActive='Y','Y','N') AS MatchActive,
IF(nn.Nat_NormalValueIsAbnormal='N','Y','N') AS MatchNotAbnormal,
IF(nn.Nat_NormalValueNat_FlagID=1,'Y','N') AS MatchFlag,
IF(nn.Nat_NormalValueNat_TestID=$natTestID,'Y','N') AS MatchNatTest,
IF($m>0 AND nn.Nat_NormalValueNat_MethodeID=$m,'Y','N') AS MatchSelectedMethod,
IF(nn.Nat_NormalValueNat_SexID=$s,'Y','N') AS MatchSex,
IF($matchMinAge,'Y','N') AS MatchMinAge,
IF($matchMaxAge,'Y','N') AS MatchMaxAge,
CASE nn.Nat_NormalValueNat_NormalValueTypeID
WHEN 1 THEN IF(nn.Nat_NormalValueNat_SexID=$s AND $matchMinAge AND $matchMaxAge,'Y','N')
WHEN 2 THEN IF($matchMinAge AND $matchMaxAge,'Y','N')
WHEN 3 THEN IF(nn.Nat_NormalValueNat_SexID=$s,'Y','N')
WHEN 4 THEN 'Y'
ELSE 'N'
END AS MatchTypeRule,
CONCAT_WS('; ',
IF(NOT(nn.Nat_NormalValueValidDate IS NULL OR nn.Nat_NormalValueValidDate<NOW()),'valid_date_not_passed',NULL),
IF(nn.Nat_NormalValueIsActive<>'Y','inactive',NULL),
IF(nn.Nat_NormalValueIsAbnormal<>'N','abnormal_row',NULL),
IF(nn.Nat_NormalValueNat_FlagID<>1,'flag_not_1',NULL),
IF(nn.Nat_NormalValueNat_TestID<>$natTestID,'nat_test_mismatch',NULL),
IF($m>0 AND nn.Nat_NormalValueNat_MethodeID<>$m,'methode_mismatch',NULL),
IF(nn.Nat_NormalValueNat_NormalValueTypeID IN(1,3) AND nn.Nat_NormalValueNat_SexID<>$s,'sex_mismatch',NULL),
IF(nn.Nat_NormalValueNat_NormalValueTypeID IN(1,2) AND NOT $matchMinAge,'age_below_min',NULL),
IF(nn.Nat_NormalValueNat_NormalValueTypeID IN(1,2) AND NOT $matchMaxAge,'age_above_max',NULL)
) AS FailReason
FROM nat_normalvalue nn
LEFT JOIN nat_methode nm ON nm.Nat_MethodeID = nn.Nat_NormalValueNat_MethodeID
WHERE nn.Nat_NormalValueNat_TestID = $natTestID
AND nn.Nat_NormalValueIsActive = 'Y'
AND nn.Nat_NormalValueIsAbnormal = 'N'
ORDER BY
CASE WHEN $m>0 AND nn.Nat_NormalValueNat_MethodeID=$m THEN 0 ELSE 1 END,
nn.Nat_NormalValueNat_MethodeID,
nn.Nat_NormalValueNat_NormalValueTypeID,
nn.Nat_NormalValueID";
$qry = $this->db->query($sql);
return $qry ? $qry->result_array() : [];
}
private function _chk($name, $ok, $value, $note)
{
return [
'check_name' => $name,
'check_status' => $ok ? 'OK' : 'MISSING',
'check_value' => $value,
'check_note' => $note,
];
}
private function _query_one($sql)
{
$qry = $this->db->query($sql);
if (!$qry) return 0;
$row = $qry->row_array();
return intval($row['Nat_NormalValueID'] ?? 0);
}
}

View File

@@ -222,33 +222,69 @@ class ReportUrl
return $rows_return;
}
// Inject decrypted patient PII ke params jika ada PT_OrderHeaderID
// SP header BIRT akan terima PDecryptName, PDecryptDOB, dll sebagai parameter IN
private function _inject_patient_decrypt($params) {
$order_id = isset($params['PT_OrderHeaderID']) ? intval($params['PT_OrderHeaderID']) : 0;
if (!$order_id) return $params;
$CI = &get_instance();
$CI->load->library('ibl_encryptor');
$sql = "SELECT M_PatientName_enc, M_PatientHP_enc, M_PatientEmail_enc,
M_PatientDOB_enc, M_PatientPOB_enc, M_PatientNIK_enc,
M_PatientIDNumber_enc, M_PatientDOB
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
WHERE T_OrderHeaderID = ? LIMIT 1";
$row = $this->db->query($sql, [$order_id])->row_array();
if (!$row) return $params;
$enc = $CI->ibl_encryptor;
$addr_sql = "SELECT M_PatientAddressDescription_enc FROM m_patientaddress
WHERE M_PatientAddressM_PatientID = (
SELECT T_OrderHeaderM_PatientID FROM t_orderheader WHERE T_OrderHeaderID = ?
) AND M_PatientAddressIsActive = 'Y' AND M_PatientAddressNote = 'Utama' LIMIT 1";
$addr_row = $this->db->query($addr_sql, [$order_id])->row_array();
$params['PDecryptName'] = urlencode($enc->decrypt($row['M_PatientName_enc'] ?? '') ?? '');
$params['PDecryptDOB'] = urlencode($enc->decrypt($row['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($row['M_PatientDOB'] ?? 'now')));
$params['PDecryptHP'] = urlencode($enc->decrypt($row['M_PatientHP_enc'] ?? '') ?? '');
$params['PDecryptEmail'] = urlencode($enc->decrypt($row['M_PatientEmail_enc'] ?? '') ?? '');
$params['PDecryptNIK'] = urlencode($enc->decrypt($row['M_PatientNIK_enc'] ?? '') ?? '');
$params['PDecryptIDNum'] = urlencode($enc->decrypt($row['M_PatientIDNumber_enc']?? '') ?? '');
$params['PDecryptAddr'] = urlencode($enc->decrypt($addr_row['M_PatientAddressDescription_enc'] ?? '') ?? '');
return $params;
}
function get_report_url_by_code($report_code, $params = array()){
$CI = &get_instance();
$this->db = $CI->load->database("onedev", true);
// Get report data by code
$report_data = $this->get_report_by_code($report_code, $params);
if (!$report_data || empty($report_data)) {
return array(false, "Report code not found: " . $report_code);
}
// Inject decrypted patient PII jika ada order ID
$params = $this->_inject_patient_decrypt($params);
// Get URL template
$url_template = $report_data['Print_TransactionUrl'];
if (empty($url_template)) {
return array(false, "URL template is empty for report code: " . $report_code);
}
// Replace placeholders with actual parameter values
$final_url = $url_template;
foreach ($params as $param_key => $param_value) {
// Determine if value should be quoted (string) or not (numeric)
$replacement_value = is_numeric($param_value) ? $param_value : "'" . $param_value . "'";
// Replace placeholder in URL
$final_url = str_replace($param_key, $replacement_value, $final_url);
$replacement_value = is_numeric($param_value) ? $param_value : urldecode($param_value);
$final_url = str_replace($param_key, urlencode($replacement_value), $final_url);
}
return array(true, $final_url);

View File

@@ -56,21 +56,57 @@ class Reporturl
}
if($show == 'N'){
return array(true, $final_url);
return array(true, $this->build_proxy_stream_url($report_code, $params));
}
else{
$final_url = 'http'.($_SERVER['HTTPS'] == 'on' ? 's' : '').'://'.$_SERVER['HTTP_HOST'].$final_url;
$response = file_get_contents($final_url);
$CI->load->library('ibl_patient_decrypt');
$pdf = $CI->ibl_patient_decrypt->fetch_birt_pdf($final_url);
header("Content-type: application/pdf");
header(
'Content-Disposition: inline; filename="' .
$output_file_name .
'"'
);
echo ($response);
header('Content-Disposition: inline; filename="' . ($output_file_name ?? 'report.pdf') . '"');
echo $pdf;
}
}
private function build_proxy_stream_url($report_code, $params = array())
{
$CI = &get_instance();
$token = $this->resolve_request_token($CI);
$query = array(
'report_code' => $report_code,
'token' => $token,
'PT_OrderHeaderID' => $params['PT_OrderHeaderID'] ?? 0,
);
if (isset($params['PPaymentID'])) {
$query['PPaymentID'] = $params['PPaymentID'];
}
if (isset($params['PUsername'])) {
$query['PUsername'] = $params['PUsername'];
}
return '/one-api-lab/tools/birt_proxy/stream_by_code?' . http_build_query($query);
}
private function resolve_request_token($CI)
{
$rawInput = json_decode($CI->input->raw_input_stream, true);
if (is_array($rawInput) && !empty($rawInput['token'])) {
return trim($rawInput['token']);
}
$postToken = $CI->input->post('token', true);
if (!empty($postToken)) {
return trim($postToken);
}
$getToken = $CI->input->get('token', true);
if (!empty($getToken)) {
return trim($getToken);
}
return '';
}
/*
CREATE TABLE `print_transaction` (

View File

@@ -0,0 +1,24 @@
# Catatan Meeting Klinik Internal
## Task List IBL
### Registrasi Klinik (`klinik/Registrationv3.php`)
- [x] Tambah `searchregion` dan `search_countries` (samakan dg `ibl_registration/Patient.php`)
- [x] Fix `search()` — hapus kelurahan sub-query, tambah PDP decrypt inline
- [x] Fix `getaddress()` — gunakan tabel `regional` via `M_PatientAddressRegionalCd`
- [x] Fix `newpatient()` — simpan `M_PatientAddressRegionalCd`, City, State, District, Village, Country, CountryCode, Note
- [x] Fix `editpatient()` — terapkan `_mask_dob()` ke `M_PatientDOB`
- [x] Tambah `_mask_dob()` — masking DOB sesuai PDP UU No. 27/2022
### Screening Klinik (`klinik/screening/Screening.php`)
- [x] Fix `search()` — hapus kelurahan sub-query, tambah PDP decrypt
- [x] Tambah `getsexreg()` — samakan dg Registrationv3
### Sampling Call (`mockup/doctorclinicv2/Samplingcall.php`)
- [x] Tambah UNION SELECT dari `one_klinik.order` agar antrian klinik muncul
- [ ] Debug: order klinik masih belum muncul di antrian — cek params `locationid`, `stationid`, `xdate`
### Pending / Belum Dikerjakan
- [ ]
- [ ]
- [ ]

View File

@@ -0,0 +1,475 @@
# Runbook: Implementasi Enkripsi PII Pasien (UU PDP) — Production
> Dibuat: 2026-05-31 | Task: FHM31052601IBL
> Teknologi: AES-256-GCM, Trigram Blind Index, PHP CodeIgniter 3, MariaDB
---
## Arsitektur
```
.env (passphrase)
Ibl_encryptor library
├── encrypt/decrypt → kolom _enc (AES-256-GCM)
└── search_bidx → kolom _bidx (HMAC-SHA256 trigram, untuk search)
m_patient (plaintext kolom lama = MASKED)
├── M_PatientName → "FAJRI H*******" (masked)
├── M_PatientName_enc → "base64ciphertext" (encrypted, real value)
├── M_PatientName_bidx→ ["tok1","tok2",...] (search index)
└── ...field lainnya
Search patient: nama + HP + DOB + NIK via JSON_CONTAINS(_bidx)
Read patient : decrypt _enc di PHP sebelum return ke client
```
---
## Pre-flight Checklist
```bash
# 1. Pastikan disk minimal 10GB free
df -h /
# 2. Catat ukuran DB sebelum (untuk verifikasi)
mysql -e "SELECT table_name, ROUND((data_length+index_length)/1024/1024,1) MB
FROM information_schema.tables WHERE table_schema='one_lab'
ORDER BY (data_length+index_length) DESC LIMIT 15;"
# 3. Pastikan tidak ada proses berat berjalan
pgrep -a php
```
> ⚠️ **Disk Space**: Enkripsi menambah ~1-2GB ke database (kolom _enc + _bidx).
> Jika disk penuh, lihat bagian **Troubleshooting** di bawah.
---
## Urutan Eksekusi di Production
### Step 1 — Buat file `.env`
```bash
cat > /path/to/one-api-lab/.env << 'EOF'
IBL_ENCRYPT_KEY=<passphrase-kamu>
IBL_ENCRYPT_SEARCH_KEY=<passphrase-search-kamu>
EOF
chmod 600 /path/to/one-api-lab/.env
```
> ⚠️ **WAJIB** simpan kedua passphrase di password manager sebelum lanjut.
> Key hilang = data di `_enc` tidak bisa didekripsi selamanya.
---
### Step 2 — Backup Database
```bash
bash scripts/backup_pdp_tables.sh
# Backup tersimpan di: ~/backup_pdp_YYYY_MM_DD_HHMMSS/
# Verifikasi backup ada dan tidak kosong
ls -lh ~/backup_pdp_*/
```
---
### Step 3 — Jalankan SQL Migration (tambah kolom + update trigger)
```bash
# Tambah kolom _enc dan _bidx
mysql one_lab < sql/manual_changes/2026-05-31-pdp-encrypt-columns.sql
# Update trigger m_patient & m_patientaddress (pakai _enc di log JSON)
mysql one_lab < sql/manual_changes/2026-05-31-pdp-update-triggers-enc.sql
# Ubah M_PatientDOB dari DATE ke VARCHAR(20) agar masked value tersimpan
mysql one_lab < sql/manual_changes/2026-06-11-alter-m-patient-dob-to-varchar.sql
# Ubah Mcu_PreregisterPatientsDOB dari DATE ke VARCHAR(20) (sama)
mysql one_lab < sql/manual_changes/2026-06-11-alter-mcu-preregister-dob-to-varchar.sql
# Verifikasi kolom terbentuk
mysql -e "SHOW COLUMNS FROM one_lab.m_patient LIKE '%_enc';"
mysql -e "SHOW COLUMNS FROM one_lab.m_patient LIKE '%_bidx';"
mysql -e "SHOW COLUMNS FROM one_lab.m_patientaddress LIKE '%_enc';"
mysql -e "SHOW COLUMNS FROM one_lab.m_patient WHERE Field = 'M_PatientDOB'\G"
```
---
### Step 4 — Encrypt Data Pasien (m_patient)
```bash
# Estimasi: 30-60 menit untuk 178K rows
php scripts/migrate_encrypt_patient.php
# Verifikasi
mysql -e "SELECT COUNT(*) total, COUNT(M_PatientName_enc) done
FROM one_lab.m_patient;"
# Expected: total == done
```
---
### Step 5 — Populate NIK Bidx
```bash
# Isi search index untuk NIK (dari _enc yang sudah ada)
# Estimasi: 5-10 menit
php scripts/migrate_nik_bidx.php
# Verifikasi
mysql -e "SELECT COUNT(*) total, COUNT(M_PatientNIK_bidx) done
FROM one_lab.m_patient WHERE M_PatientNIK_enc IS NOT NULL;"
```
---
### Step 6 — Encrypt Alamat Pasien (m_patientaddress)
```bash
# Enkripsi alamat TANPA bidx (hemat disk)
# Estimasi: 15-30 menit untuk 133K rows
php scripts/migrate_address_enc.php
# Verifikasi
mysql -e "SELECT COUNT(*) total, COUNT(M_PatientAddressDescription_enc) done
FROM one_lab.m_patientaddress;"
# Expected: total == done
```
---
### Step 6b — Encrypt Address (m_patientaddress)
```bash
# 357 baris tersisa di dev — jalankan sampai selesai di prod
php scripts/migrate_address_enc.php
# Verifikasi
mysql -e "SELECT COUNT(*) total, COUNT(M_PatientAddressDescription_enc) done FROM one_lab.m_patientaddress;"
```
---
### Step 7 — Encrypt Tujuan Pengiriman Hasil (t_orderdelivery)
```bash
# HANYA t_orderdelivery — berisi email/HP pasien (PII nyata, bisa dimasking)
# Tabel hasil lab (t_orderdetail, so_resultentry*, dll) TIDAK dienkripsi —
# lihat bagian "Keputusan Arsitektur" di bawah
php scripts/migrate_encrypt_orderdelivery.php
# Verifikasi
mysql -e "SELECT COUNT(*) total, COUNT(T_OrderDeliveryDestination_enc) done
FROM one_lab.t_orderdelivery;"
```
---
### Step 8 — Masking Kolom Plaintext
```bash
# Masking semua kolom PII lama di m_patient & m_patientaddress
# Format nama: "FAJRI H*******" (kata pertama penuh + inisial)
php scripts/mask_patient_plaintext.php
# Re-mask nama dengan format terbaru (jika sudah pernah dimasking sebelumnya)
php scripts/remask_patient_name.php
# Verifikasi: cek beberapa baris
mysql -e "SELECT M_PatientID, M_PatientName, M_PatientHP, M_PatientEmail
FROM one_lab.m_patient ORDER BY RAND() LIMIT 5;"
# Expected: tampil "BUDI S******", "0812*****890", "bu***@gmail.com"
```
---
### Step 8b — Buat patient_print_cache (untuk BIRT decrypt)
```bash
mysql one_lab < sql/manual_changes/2026-05-31-pdp-birt-sp-cache-join.sql
```
Tabel ini dibutuhkan oleh:
- 6 SP header BIRT (sp_rpt_hasil_header, _2, _eng, sp_rpt_fo_001, sp_rpt_card_patient, sp_rpt_t_002)
- Birt_proxy.php controller
- Ibl_patient_decrypt library
- Qr_report_uploader, Ibl_merge_report_gateway, send_email.php
---
### Step 8c — Update sp_rpt_t_002_eng (jika dipakai)
```bash
# Cek apakah sp_rpt_t_002_eng ada
mysql -e "SHOW PROCEDURE STATUS WHERE Db='one_lab' AND Name='sp_rpt_t_002_eng'\G"
# Jika ada, update manual dengan LEFT JOIN ke patient_print_cache
# (sama seperti sp_rpt_t_002 di 2026-05-31-pdp-birt-sp-cache-join.sql)
```
---
### Catatan Disk Space untuk Production
> ⚠️ **Sangat penting**: Pastikan disk minimal **10GB free** sebelum mulai.
> Trigger m_patient + m_patientaddress nulis ke log_patient setiap UPDATE.
> Untuk migration + masking 178K+133K rows → log bisa 2-3GB.
>
> **Strategi aman**:
> 1. DROP trigger dulu (`vm_patient_ai`, `vm_patient_bu`, `m_patientaddress_ai`, `m_patientaddress_bu`)
> 2. Jalankan semua migration + masking scripts
> 3. Recreate trigger: `mysql one_lab < sql/manual_changes/2026-05-31-pdp-update-triggers-enc.sql`
>
> File trigger SQL ada di: `sql/manual_changes/2026-05-31-pdp-update-triggers-enc.sql`
---
### Step 9 — Truncate Log Lama (Opsional tapi Direkomendasikan)
```bash
# log_patient berisi JSON plaintext PII dari sebelum enkripsi
# Truncate meningkatkan compliance (hapus data PII lama yang tidak terenkripsi)
mysql one_lab_log -e 'TRUNCATE TABLE log_patient;'
# Verifikasi
mysql -e "SELECT COUNT(*) FROM one_lab_log.log_patient;"
# Expected: 0
```
---
### Step 10 — Verifikasi End-to-End
```bash
# 1. Cek search patient berjalan
curl -s -X POST https://[SERVER]/mockup/fo/ibl_registration/patient/search \
-H "Content-Type: application/json" \
-d '{"token":"[VALID_TOKEN]","search":"BUDI","noreg":"","current_page":1}' \
| python3 -m json.tool | head -20
# 2. Cek data terdekripsi dengan benar (nama muncul lengkap, bukan masked)
# Expected di response: "M_PatientName": "BUDI SANTOSO" (bukan "BUDI S******")
# 3. Cek disk usage akhir
df -h /
# 4. Cek MySQL masih sehat
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
```
---
## Field yang Dienkripsi
### `one_lab.m_patient`
| Field | Tipe kolom plain | `_enc` | `_bidx` (search) | Catatan |
|-------|:---:|:------:|:----------------:|---------|
| M_PatientName | VARCHAR | ✅ | ✅ | |
| M_PatientHP | VARCHAR | ✅ | ✅ | |
| M_PatientDOB | **VARCHAR(20)** | ✅ | ✅ | Diubah dari DATE → VARCHAR agar mask `**-**-YYYY` tersimpan |
| M_PatientNIK | VARCHAR | ✅ | ✅ | ⚠️ `M_PatientNIK_bidx` diisi dari **`M_PatientIDNumber`**, bukan NIK |
| M_PatientEmail | VARCHAR | ✅ | — | |
| M_PatientPhone | VARCHAR | ✅ | — | |
| M_PatientPOB | VARCHAR | ✅ | — | |
| M_PatientIDNumber | VARCHAR | ✅ | — | Masked plain, bidx-nya lewat `M_PatientNIK_bidx` |
| M_PatientNIP | VARCHAR | ✅ | — | |
### `one_lab.m_patientaddress`
| Field | `_enc` | `_bidx` |
|-------|:------:|:-------:|
| M_PatientAddressDescription | ✅ | — (dihapus, hemat disk) |
| M_PatientAddressEmail | ✅ | — |
| M_PatientAddressPhone | ✅ | — |
### Tujuan Pengiriman Hasil (PII nyata)
| Tabel | Field |
|-------|-------|
| `t_orderdelivery` | T_OrderDeliveryDestination (email/HP) |
### Log
| Tabel | Field |
|-------|-------|
| `one_lab_log.log_patient` | Log_PatientJsonBefore/After (**di-truncate di production**) |
| `one_lab_log.log_fo` | Log_FoJson |
| `one_lab_log.log_resultentry` | Log_ResultEntryJSONBefore/After |
### TIDAK Dienkripsi (keputusan disengaja)
| Tabel | Alasan |
|-------|--------|
| `t_orderdetail`, `t_orderheader` | Nilai hasil lab bukan PII tanpa identitas pasien. Trigger butuh plaintext untuk flag H/L/N. |
| `so_resultentry_*`, `member_eligible` | Nilai klinis, bukan PII langsung. Plaintext dibutuhkan proses operasional. |
| `mcu_resume_results` | JSON nilai lab tanpa PII. Enkripsi memberatkan global MCU report. |
**Perlindungan hasil lab** tetap via: identitas pasien terenkripsi di `m_patient` + access control + audit log.
---
## Format Masking Kolom Plaintext
| Field | Format | Contoh |
|-------|--------|--------|
| Nama (multi kata) | Kata pertama penuh + inisial+bintang per kata | `FAJRI H******* M****` |
| Nama (satu kata) | 2 karakter pertama + bintang sisanya | `SI**` / `BI*****` |
| HP/Phone | 4 digit pertama + bintang + 3 digit akhir | `0812*****890` |
| Email | 2 huruf pertama + *** + @domain | `fa***@gmail.com` |
| NIK/IDNumber | 4 digit pertama + *** + 2 digit akhir | `3201***01` |
| POB | 2 huruf pertama + *** | `JA***` |
| Alamat | 5 karakter pertama + *** | `Jl. S***` |
---
## Search Pasien
Parameter via `+` separator di field `search`:
```
search=NAMA+HP+DOB+NIK
```
| Posisi | Field | Contoh |
|--------|-------|--------|
| `e[0]` | Nama (min 3 karakter) | `BUD` |
| `e[1]` | HP (min 3 karakter) | `081` |
| `e[2]` | DOB format dd-mm-yyyy | `25-0` |
| `e[3]` | NIK (min 3 karakter) | `320` |
---
## BIRT Report & FPDF
### Strategi Decrypt untuk Report
**BIRT Reports** (`print_transaction`):
- PHP `Birt_proxy.php` → decrypt PII → INSERT `patient_print_cache` → call BIRT
- 6 SP header yang diupdate dengan LEFT JOIN ke cache: `sp_rpt_hasil_header`, `sp_rpt_hasil_header_2`, `sp_rpt_hasil_header_eng`, `sp_rpt_fo_001`, `sp_rpt_card_patient`, `sp_rpt_t_002`
- SP signature tidak berubah — `.rptdesign` tidak perlu diupdate
- Cache TTL: 5 menit, auto-cleanup di request berikutnya
**Endpoint Birt_proxy:**
- `POST /tools/birt_proxy/stream` — return PDF binary langsung
- `POST /tools/birt_proxy/get_url` — return URL untuk buka di browser
**FPDF Controllers** (`tools/`):
- `Inform_consent.php`, `Medical_checkup_report.php` — decrypt langsung dari `_enc` (direct SQL)
- `Kartu_kontrol.php`, `Rpt_t_002.php`, `Rpt_t_002_eng.php` — populate cache → call SP → delete cache
**SQL produksi yang perlu dijalankan:**
```bash
mysql one_lab < sql/manual_changes/2026-05-31-pdp-birt-sp-cache-join.sql
```
---
## Controller yang Sudah Diupdate (Decrypt + Encrypt)
| Controller | Fungsi |
|-----------|--------|
| `mockup/fo/ibl_registration/Patient.php` | Search, add, edit pasien FO |
| `mockup/fo/ibl_registration/Order.php` | Order management (nama, email, HP) |
| `mockup/fo/ibl_registration/Payment.php` | Kasir (nama pasien) |
| `mockup/fo/ibl_registration/History.php` | History delivery (email/HP) |
| `mockup/fo/ibl_registration/Delivery.php` | Pengiriman hasil (email/HP) |
| `mockup/fo/ibl_registration/Order copy.php` | Order MCU |
| `mockup/masterdata/Patientv4.php` | Masterdata pasien — tampil data **lengkap** |
| `klinik/Registrationv3.php` | Registrasi & edit pasien klinik (`save()`, `newpatient()`) |
| `mockup/setupmcuoffline-ibl/Preregister.php` | MCU offline IBL — batch CSV & newpatient |
| `mockup/mcuoffline/Preregisterapp.php` | MCU offline app — batch CSV, search, newpatient |
## mcu_preregister_patients — Masking
Data PII yang dimasking saat INSERT ke staging table `mcu_preregister_patients`:
| Field | Tipe kolom | Mask |
|-------|:---:|------|
| `*PatientName` | VARCHAR | `_mask_name()` |
| `*KTP` | VARCHAR | `_mask_id()` |
| `*NIP` / `*NIK` | VARCHAR | `_mask_id()` |
| `*Email` | VARCHAR | `_mask_email()` |
| `*Hp` | VARCHAR | `_mask_phone()` |
| `*DOB` | **VARCHAR(20)** | `_mask_dob()` — diubah dari DATE → VARCHAR agar mask `**-**-YYYY` tersimpan |
Tidak ada kolom `_enc` di tabel ini — data asli bisa diambil dari `m_patient` via `*M_PatientID`.
4 lokasi INSERT yang sudah diupdate: `savecsv()` & `save()` di setupmcuoffline-ibl, `save()` & `savenewform()` di mcuoffline/Preregisterapp.
## one_lab_dashboard.mcu_patient — Enkripsi via SP
Kolom `Mcu_PatientName` dan `Mcu_PatientDOB` diubah ke `TEXT` dan menyimpan
nilai `_enc` dari `one_lab.m_patient` (JOIN via `Mcu_PreregisterPatientsM_PatientID`).
SP yang diupdate:
- `sp_upsert_mcu_patient_by_preregister_id`
- `sp_upsert_mcu_patient_by_mgm_mcuid`
SQL: `sql/manual_changes/2026-05-31-pdp-mcu-patient-dashboard-enc-sp.sql`
```bash
mysql one_lab < sql/manual_changes/2026-05-31-pdp-mcu-patient-dashboard-enc-sp.sql
```
## Controller yang Belum Diupdate (Tampil Data Masked)
Semua ~300+ controller lain otomatis tampilkan data termasking karena kolom
plaintext sudah dimasking. Tidak perlu update satu-satu untuk compliance dasar.
**Sprint berikutnya** — update controller prioritas yang butuh data lengkap:
- Sampling (samplinglab-vvii, samplingelectromedisnew)
- Result entry (resultentrysoothers-v20, resultentrysoxray-v8, resultentrysoelectromedis-v8)
- Result verification
- MCU resume (resumeindividufacelift)
---
## Troubleshooting
### Disk Penuh Saat Migration
```bash
# Cek pemakai disk terbesar
du -sh /home/one/* /tmp/* 2>/dev/null | sort -rh | head -20
# Bersihkan file lama yang aman dihapus:
# - /home/one/project/one/dump_*.sql (backup lama)
# - /home/one/project/one/*.tar.gz (archive lama)
# - /tmp/intelephense (cache IDE)
# Bersihkan journal (butuh sudo)
sudo journalctl --vacuum-size=300M
sudo truncate -s 0 /var/log/btmp
```
### MySQL Crash (Disk Penuh)
```bash
# Restart MySQL setelah disk dibebaskan
sudo systemctl start mariadb
# atau
sudo service mysql start
```
### Migration Lambat (Trigger Overhead)
Trigger `m_patientaddress_bu` nulis ke `log_patient` setiap UPDATE.
Jika log_patient sangat besar (>1GB) dan disk hampir penuh:
```bash
# Truncate log lama (aman — data PII lama di log justru harus dihapus untuk compliance)
mysql one_lab_log -e 'TRUNCATE TABLE log_patient;'
```
### Restore jika Ada Masalah
```bash
mysql one_lab < ~/backup_pdp_YYYY_MM_DD_HHMMSS/one_lab_tables.sql
mysql one_lab_log < ~/backup_pdp_YYYY_MM_DD_HHMMSS/one_lab_log_tables.sql
```
---
## Catatan Key Management
- Key disimpan di `.env`**JANGAN commit ke git** (sudah ada di `.gitignore`)
- Backup passphrase di: password manager + file enkripsi di lokasi terpisah dari server
- Key rotation di masa depan: perlu re-encrypt semua data (decrypt lama → encrypt baru)
- Tidak ada recovery jika key hilang

View File

@@ -0,0 +1,109 @@
# Prompt Implementasi UU PDP — IBL Production Server
> Gunakan prompt ini untuk menginstruksikan agent/Claude saat implementasi di IBL server produksi.
> Branch: `main` | Repo: `BE_IBL/one-api-lab`
---
## Prompt untuk Agent
```
Kamu akan mengimplementasikan enkripsi PII pasien (UU PDP) ke IBL production server.
PENTING: Baca seluruh runbook di `docs/pdp-encryption-runbook.md` sebelum memulai.
KONTEKS:
- Server IBL: SSH config "devibl" (atau sesuai config SSH yang tersedia)
- Project path di server: /home/one/project/one/one-api-lab/ (atau sesuai deployment IBL)
- Database: one_lab, one_lab_log
- Enkripsi: AES-256-GCM, key dari .env (passphrase, bukan hex)
- PHP: 7.2 (hindari syntax fn() arrow function)
LANGKAH WAJIB SEBELUM APAPUN:
1. Cek disk space: minimal 10GB free
- Jika kurang, hapus file lama di /home/one/ (bukan backup PDP, bukan MySQL data)
- Journal logs butuh sudo: sudo journalctl --vacuum-size=300M
2. BACKUP DATABASE DULU:
bash scripts/backup_pdp_tables.sh
Verifikasi backup ada dan tidak kosong sebelum lanjut.
3. Buat .env di server (isi passphrase dari password manager IBL):
IBL_ENCRYPT_KEY=<passphrase-dari-password-manager>
IBL_ENCRYPT_SEARCH_KEY=<passphrase-search-dari-password-manager>
chmod 600 .env
URUTAN EKSEKUSI (ikuti runbook):
1. Backup database
2. Buat .env
3. Jalankan SQL migration:
- sql/manual_changes/2026-05-31-pdp-encrypt-columns.sql
- sql/manual_changes/2026-05-31-pdp-update-triggers-enc.sql
- sql/manual_changes/2026-05-31-pdp-birt-sp-cache-join.sql (buat patient_print_cache)
- sql/manual_changes/2026-06-08-pdp-fo-birt-sp-patient-print-cache.sql (update keluarga SP report FO lama: invoice, kwitansi, nota, billing)
4. DROP triggers sebelum migration data:
mysql one_lab -e 'DROP TRIGGER IF EXISTS vm_patient_ai; DROP TRIGGER IF EXISTS vm_patient_bu; DROP TRIGGER IF EXISTS m_patient_au; DROP TRIGGER IF EXISTS m_patientaddress_ai; DROP TRIGGER IF EXISTS m_patientaddress_bu;'
5. Encrypt m_patient: php scripts/migrate_encrypt_patient.php
6. Populate NIK bidx: php scripts/migrate_nik_bidx.php
7. Encrypt address: php scripts/migrate_address_enc.php
8. Encrypt orderdelivery: php scripts/migrate_encrypt_orderdelivery.php
9. Masking plaintext (setelah encrypt selesai):
php scripts/mask_patient_plaintext.php
php scripts/remask_patient_name.php
10. Recreate triggers:
mysql one_lab < sql/manual_changes/2026-05-31-pdp-update-triggers-enc.sql
11. Truncate log lama: mysql one_lab_log -e 'TRUNCATE TABLE log_patient; TRUNCATE TABLE order_log;'
12. Verifikasi: cek sample data, cek disk, cek MySQL
PERHATIAN DISK:
- Setiap kali masking banyak baris, log_patient bisa penuh
- Jika disk penuh: sudo systemctl start mariadb (setelah hapus file), truncate log_patient, drop trigger, lanjut
- Selalu DROP trigger sebelum masking, recreate sesudahnya
- Jangan hapus: backup_pdp_*, one_lab_tables.sql
VERIFIKASI SETIAP STEP:
- Setelah encrypt: SELECT COUNT(*), COUNT(M_PatientName_enc) FROM m_patient;
- Setelah masking: SELECT M_PatientName, M_PatientHP FROM m_patient LIMIT 5; (harus tampil "NAMA A***", "0812***")
- Cek disk: df -h /
- Test search patient: pastikan search by nama (3+ karakter) masih bekerja via API
JANGAN LAKUKAN:
- Jangan hapus backup_pdp_* files
- Jangan delete MySQL data files (/var/lib/mysql/ibdata*)
- Jangan commit .env ke git
- Jangan lanjut kalau disk < 2GB free
- Jangan skip backup
File referensi lengkap: docs/pdp-encryption-runbook.md
```
---
## Checklist Pre-Implementasi
Sebelum mulai, pastikan:
- [ ] SSH ke IBL server bisa
- [ ] Disk minimal 10GB free
- [ ] Passphrase key sudah disiapkan (dari password manager)
- [ ] Ada window maintenance (user tidak aktif)
- [ ] Backup terverifikasi sebelum lanjut ke step berikutnya
- [ ] Tim tahu ada maintenance (beri tahu jika ada downtime)
## File Penting
| File | Fungsi |
|------|--------|
| `docs/pdp-encryption-runbook.md` | Runbook lengkap step by step |
| `.env` | Key enkripsi (buat manual di server, JANGAN commit) |
| `scripts/backup_pdp_tables.sh` | Script backup sebelum migration |
| `sql/manual_changes/2026-05-31-pdp-encrypt-columns.sql` | Tambah kolom _enc + _bidx |
| `sql/manual_changes/2026-05-31-pdp-update-triggers-enc.sql` | Update trigger pakai _enc |
| `sql/manual_changes/2026-05-31-pdp-birt-sp-cache-join.sql` | patient_print_cache + update 6 SP BIRT |
| `sql/manual_changes/2026-06-08-pdp-fo-birt-sp-patient-print-cache.sql` | Update keluarga SP report FO lama agar baca `patient_print_cache` |
| `scripts/migrate_encrypt_patient.php` | Encrypt 178K patient rows |
| `scripts/migrate_nik_bidx.php` | Populate NIK search index |
| `scripts/migrate_address_enc.php` | Encrypt address rows |
| `scripts/migrate_encrypt_orderdelivery.php` | Encrypt delivery destination |
| `scripts/mask_patient_plaintext.php` | Masking HP/email/POB/NIK/alamat |
| `scripts/remask_patient_name.php` | Remask nama format "NAMA A***" |

View File

@@ -0,0 +1,878 @@
# ibl_merge_report_service Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a standalone Go HTTP service (`ibl_merge_report_service`) that fetches multiple PDF URLs, merges them into one PDF, and streams the result — called internally by the one-api-lab PHP gateway.
**Architecture:** The service exposes a single `POST /merge` endpoint. It validates the `X-Internal-Secret` request header, parses the JSON body, fetches each PDF URL sequentially, merges them with pdfcpu, then writes the merged PDF as `application/pdf`. If any source fetch fails, the entire request fails with a mapped HTTP status code. No files are cached on disk.
**Tech Stack:** Go 1.21+, `github.com/pdfcpu/pdfcpu` for in-memory PDF merging, standard library `net/http`.
---
## File Map
| File | Responsibility |
|------|---------------|
| `services/ibl_merge_report_service/go.mod` | Module definition |
| `services/ibl_merge_report_service/main.go` | Entry point — read env config, register route, start HTTP server |
| `services/ibl_merge_report_service/handler.go` | `mergeRequest` struct, `mergeHandler`, validation, `writeError` |
| `services/ibl_merge_report_service/handler_test.go` | Unit tests for all handler branches using a mock `mergeFunc` |
| `services/ibl_merge_report_service/merger.go` | `fetchPDF`, `mergePDFs`, `fetchError` type |
| `services/ibl_merge_report_service/merger_test.go` | httptest-based tests for `fetchPDF` error paths |
| `scripts/build-merge-service.sh` | Cross-compile for Linux amd64, output binary to `up/` |
| `services/ibl_merge_report_service/ibl-merge-report.service` | Systemd unit template for IBL server |
---
### Task 1: Scaffold — module init + empty stubs
**Files:**
- Create: `services/ibl_merge_report_service/go.mod`
- Create: `services/ibl_merge_report_service/main.go`
- Create: `services/ibl_merge_report_service/handler.go`
- Create: `services/ibl_merge_report_service/merger.go`
- [ ] **Step 1.1: Create go.mod**
```bash
mkdir -p /Users/fajrihardhitamurti/REPO_GITEA_IBL/BE_IBL/one-api-lab/services/ibl_merge_report_service
```
`services/ibl_merge_report_service/go.mod`:
```
module ibl-merge-report-service
go 1.21
```
- [ ] **Step 1.2: Create handler.go stub**
`services/ibl_merge_report_service/handler.go`:
```go
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
)
type mergeRequest struct {
Name string `json:"name"`
URLs []string `json:"urls"`
MergeRequestID int64 `json:"mergeRequestID"`
TOrderHeaderID int64 `json:"T_OrderHeaderID"`
}
type mergeFunc func(urls []string) ([]byte, error)
type mergeHandler struct {
secret string
merge mergeFunc
}
func newMergeHandler(secret string, merge mergeFunc) http.Handler {
return &mergeHandler{secret: secret, merge: merge}
}
func (h *mergeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO: implement in Task 3
writeError(w, http.StatusNotImplemented, "not implemented")
}
func validateMergeRequest(req *mergeRequest) error {
// TODO: implement in Task 2
return nil
}
func writeError(w http.ResponseWriter, code int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(map[string]string{"error": msg})
}
// suppress unused-import errors until tasks fill in usage
var _ = errors.As
var _ = fmt.Errorf
```
- [ ] **Step 1.3: Create merger.go stub**
`services/ibl_merge_report_service/merger.go`:
```go
package main
import (
"fmt"
"net/http"
"time"
)
var httpClient = &http.Client{Timeout: 25 * time.Second}
type fetchError struct {
URL string
HTTPStatus int // 0 = network/timeout
}
func (e *fetchError) Error() string {
if e.HTTPStatus != 0 {
return fmt.Sprintf("source %s returned HTTP %d", e.URL, e.HTTPStatus)
}
return fmt.Sprintf("failed to fetch source %s", e.URL)
}
func fetchPDF(url string) ([]byte, error) {
// TODO: implement in Task 4
return nil, fmt.Errorf("not implemented")
}
func mergePDFs(urls []string) ([]byte, error) {
// TODO: implement in Task 5
return nil, fmt.Errorf("not implemented")
}
```
- [ ] **Step 1.4: Create main.go**
`services/ibl_merge_report_service/main.go`:
```go
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
secret := os.Getenv("MERGE_INTERNAL_SECRET")
if secret == "" {
secret = "ibl-merge-secret"
}
addr := os.Getenv("MERGE_LISTEN_ADDR")
if addr == "" {
addr = "127.0.0.1:8005"
}
mux := http.NewServeMux()
mux.HandleFunc("/merge", func(w http.ResponseWriter, r *http.Request) {
newMergeHandler(secret, mergePDFs).ServeHTTP(w, r)
})
log.Printf("ibl_merge_report_service listening on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
fmt.Fprintf(os.Stderr, "fatal: %v\n", err)
os.Exit(1)
}
}
```
- [ ] **Step 1.5: Add pdfcpu dependency and verify build**
```bash
cd services/ibl_merge_report_service && go get github.com/pdfcpu/pdfcpu@latest && go mod tidy
```
```bash
cd services/ibl_merge_report_service && go build ./...
```
Expected: builds without errors (stubs compile).
---
### Task 2: Validate request TDD
**Files:**
- Modify: `services/ibl_merge_report_service/handler.go` — implement `validateMergeRequest`
- Create: `services/ibl_merge_report_service/handler_test.go`
- [ ] **Step 2.1: Write failing tests for validateMergeRequest**
`services/ibl_merge_report_service/handler_test.go`:
```go
package main
import (
"testing"
)
func TestValidateMergeRequest_MissingName(t *testing.T) {
req := &mergeRequest{URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1}
if err := validateMergeRequest(req); err == nil {
t.Fatal("expected error for missing name")
}
}
func TestValidateMergeRequest_EmptyURLs(t *testing.T) {
req := &mergeRequest{Name: "a.pdf", URLs: []string{}, MergeRequestID: 1, TOrderHeaderID: 1}
if err := validateMergeRequest(req); err == nil {
t.Fatal("expected error for empty urls")
}
}
func TestValidateMergeRequest_NilURLs(t *testing.T) {
req := &mergeRequest{Name: "a.pdf", URLs: nil, MergeRequestID: 1, TOrderHeaderID: 1}
if err := validateMergeRequest(req); err == nil {
t.Fatal("expected error for nil urls")
}
}
func TestValidateMergeRequest_MissingMergeRequestID(t *testing.T) {
req := &mergeRequest{Name: "a.pdf", URLs: []string{"http://x"}, MergeRequestID: 0, TOrderHeaderID: 1}
if err := validateMergeRequest(req); err == nil {
t.Fatal("expected error for missing mergeRequestID")
}
}
func TestValidateMergeRequest_MissingTOrderHeaderID(t *testing.T) {
req := &mergeRequest{Name: "a.pdf", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 0}
if err := validateMergeRequest(req); err == nil {
t.Fatal("expected error for missing T_OrderHeaderID")
}
}
func TestValidateMergeRequest_Valid(t *testing.T) {
req := &mergeRequest{Name: "a.pdf", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1}
if err := validateMergeRequest(req); err != nil {
t.Fatalf("expected no error, got: %v", err)
}
}
```
- [ ] **Step 2.2: Run tests — verify they fail**
```bash
cd services/ibl_merge_report_service && go test ./... -run TestValidate -v
```
Expected: `TestValidateMergeRequest_Valid` fails (stub returns nil for everything).
- [ ] **Step 2.3: Implement validateMergeRequest**
Replace the `validateMergeRequest` stub in `handler.go`:
```go
func validateMergeRequest(req *mergeRequest) error {
if req.Name == "" {
return fmt.Errorf("name is required")
}
if len(req.URLs) == 0 {
return fmt.Errorf("urls must not be empty")
}
if req.MergeRequestID <= 0 {
return fmt.Errorf("mergeRequestID is required")
}
if req.TOrderHeaderID <= 0 {
return fmt.Errorf("T_OrderHeaderID is required")
}
return nil
}
```
Remove the now-unused stub lines at the bottom of handler.go:
```go
// Remove these two lines:
var _ = errors.As
var _ = fmt.Errorf
```
- [ ] **Step 2.4: Run tests — verify they pass**
```bash
cd services/ibl_merge_report_service && go test ./... -run TestValidate -v
```
Expected: all 6 `TestValidateMergeRequest_*` tests PASS.
---
### Task 3: Handler ServeHTTP TDD
**Files:**
- Modify: `services/ibl_merge_report_service/handler.go` — implement `ServeHTTP`
- Modify: `services/ibl_merge_report_service/handler_test.go` — add handler tests
- [ ] **Step 3.1: Write failing handler tests**
Append to `handler_test.go`:
```go
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
)
func TestHandler_WrongMethod(t *testing.T) {
h := newMergeHandler("secret", nil)
req := httptest.NewRequest(http.MethodGet, "/merge", nil)
req.Header.Set("X-Internal-Secret", "secret")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected 405, got %d", w.Code)
}
}
func TestHandler_WrongSecret(t *testing.T) {
h := newMergeHandler("correct", nil)
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBufferString("{}"))
req.Header.Set("X-Internal-Secret", "wrong")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("expected 401, got %d", w.Code)
}
}
func TestHandler_InvalidJSON(t *testing.T) {
h := newMergeHandler("s", nil)
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBufferString("{bad"))
req.Header.Set("X-Internal-Secret", "s")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d", w.Code)
}
}
func TestHandler_ValidationError(t *testing.T) {
h := newMergeHandler("s", nil)
body, _ := json.Marshal(mergeRequest{Name: "", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1})
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBuffer(body))
req.Header.Set("X-Internal-Secret", "s")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusUnprocessableEntity {
t.Fatalf("expected 422, got %d", w.Code)
}
}
func TestHandler_FetchError404(t *testing.T) {
mockMerge := func(urls []string) ([]byte, error) {
return nil, &fetchError{URL: urls[0], HTTPStatus: http.StatusNotFound}
}
h := newMergeHandler("s", mockMerge)
body, _ := json.Marshal(mergeRequest{Name: "x.pdf", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1})
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBuffer(body))
req.Header.Set("X-Internal-Secret", "s")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusNotFound {
t.Fatalf("expected 404, got %d", w.Code)
}
}
func TestHandler_FetchErrorNetwork(t *testing.T) {
mockMerge := func(urls []string) ([]byte, error) {
return nil, &fetchError{URL: urls[0], HTTPStatus: 0}
}
h := newMergeHandler("s", mockMerge)
body, _ := json.Marshal(mergeRequest{Name: "x.pdf", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1})
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBuffer(body))
req.Header.Set("X-Internal-Secret", "s")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusBadGateway {
t.Fatalf("expected 502, got %d", w.Code)
}
}
func TestHandler_MergeError(t *testing.T) {
mockMerge := func(urls []string) ([]byte, error) {
return nil, fmt.Errorf("pdfcpu: corrupt PDF")
}
h := newMergeHandler("s", mockMerge)
body, _ := json.Marshal(mergeRequest{Name: "x.pdf", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1})
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBuffer(body))
req.Header.Set("X-Internal-Secret", "s")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusInternalServerError {
t.Fatalf("expected 500, got %d", w.Code)
}
}
func TestHandler_Success(t *testing.T) {
fakeBytes := []byte("%PDF-1.4 fake-merged")
mockMerge := func(urls []string) ([]byte, error) { return fakeBytes, nil }
h := newMergeHandler("s", mockMerge)
body, _ := json.Marshal(mergeRequest{
Name: "result.pdf", URLs: []string{"http://x"}, MergeRequestID: 1, TOrderHeaderID: 1,
})
req := httptest.NewRequest(http.MethodPost, "/merge", bytes.NewBuffer(body))
req.Header.Set("X-Internal-Secret", "s")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
if ct := w.Header().Get("Content-Type"); ct != "application/pdf" {
t.Fatalf("expected application/pdf, got %s", ct)
}
if !bytes.Equal(w.Body.Bytes(), fakeBytes) {
t.Fatal("response body does not match expected PDF bytes")
}
}
```
The top of `handler_test.go` must have a single `import` block with all imports. Replace the import at the top of the file with:
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
```
- [ ] **Step 3.2: Run tests — verify they fail**
```bash
cd services/ibl_merge_report_service && go test ./... -run TestHandler -v
```
Expected: all 8 `TestHandler_*` tests FAIL (stub returns 501).
- [ ] **Step 3.3: Implement ServeHTTP**
Replace the stub `ServeHTTP` in `handler.go` with:
```go
func (h *mergeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
if r.Header.Get("X-Internal-Secret") != h.secret {
writeError(w, http.StatusUnauthorized, "unauthorized")
return
}
var req mergeRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if err := validateMergeRequest(&req); err != nil {
writeError(w, http.StatusUnprocessableEntity, err.Error())
return
}
pdfBytes, err := h.merge(req.URLs)
if err != nil {
var fe *fetchError
if errors.As(err, &fe) {
if fe.HTTPStatus == http.StatusNotFound {
writeError(w, http.StatusNotFound, "source PDF not found")
return
}
writeError(w, http.StatusBadGateway, "source PDF unavailable")
return
}
writeError(w, http.StatusInternalServerError, "merge failed")
return
}
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Disposition", `inline; filename="`+req.Name+`"`)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(pdfBytes)
}
```
- [ ] **Step 3.4: Run tests — verify all pass**
```bash
cd services/ibl_merge_report_service && go test ./... -run TestHandler -v
```
Expected: all 8 `TestHandler_*` tests PASS.
- [ ] **Step 3.5: Commit**
```bash
cd /Users/fajrihardhitamurti/REPO_GITEA_IBL/BE_IBL/one-api-lab
git add services/ibl_merge_report_service/
git commit -m "TASKCODE - scaffold ibl_merge_report_service with handler logic and tests"
```
(Replace TASKCODE with the actual task code from user before committing.)
---
### Task 4: fetchPDF TDD
**Files:**
- Modify: `services/ibl_merge_report_service/merger.go` — implement `fetchPDF`
- Create: `services/ibl_merge_report_service/merger_test.go`
- [ ] **Step 4.1: Write failing fetchPDF tests**
`services/ibl_merge_report_service/merger_test.go`:
```go
package main
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
)
func TestFetchPDF_Returns404Error(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer srv.Close()
_, err := fetchPDF(srv.URL)
if err == nil {
t.Fatal("expected error for 404 response")
}
var fe *fetchError
if !errors.As(err, &fe) {
t.Fatalf("expected *fetchError, got %T: %v", err, err)
}
if fe.HTTPStatus != http.StatusNotFound {
t.Fatalf("expected HTTPStatus 404, got %d", fe.HTTPStatus)
}
}
func TestFetchPDF_Returns500Error(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
_, err := fetchPDF(srv.URL)
if err == nil {
t.Fatal("expected error for 500 response")
}
var fe *fetchError
if !errors.As(err, &fe) {
t.Fatalf("expected *fetchError, got %T", err)
}
if fe.HTTPStatus != http.StatusInternalServerError {
t.Fatalf("expected HTTPStatus 500, got %d", fe.HTTPStatus)
}
}
func TestFetchPDF_ReturnsBytes(t *testing.T) {
expected := []byte("%PDF-1.4 test content")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/pdf")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(expected)
}))
defer srv.Close()
got, err := fetchPDF(srv.URL)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(got) != string(expected) {
t.Fatalf("expected %q, got %q", expected, got)
}
}
func TestFetchPDF_NetworkError(t *testing.T) {
// Port that's almost certainly not listening
_, err := fetchPDF("http://127.0.0.1:19999/notexist")
if err == nil {
t.Fatal("expected error for unreachable host")
}
var fe *fetchError
if !errors.As(err, &fe) {
t.Fatalf("expected *fetchError, got %T: %v", err, err)
}
if fe.HTTPStatus != 0 {
t.Fatalf("expected HTTPStatus 0 for network error, got %d", fe.HTTPStatus)
}
}
```
- [ ] **Step 4.2: Run tests — verify they fail**
```bash
cd services/ibl_merge_report_service && go test ./... -run TestFetchPDF -v
```
Expected: all 4 `TestFetchPDF_*` tests FAIL (stub returns generic error).
- [ ] **Step 4.3: Implement fetchPDF**
Replace the `fetchPDF` stub in `merger.go`:
```go
func fetchPDF(url string) ([]byte, error) {
resp, err := httpClient.Get(url)
if err != nil {
return nil, &fetchError{URL: url, HTTPStatus: 0}
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, &fetchError{URL: url, HTTPStatus: resp.StatusCode}
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, &fetchError{URL: url, HTTPStatus: 0}
}
return data, nil
}
```
Add `"io"` to the import block in `merger.go`.
- [ ] **Step 4.4: Run tests — verify all pass**
```bash
cd services/ibl_merge_report_service && go test ./... -run TestFetchPDF -v
```
Expected: all 4 `TestFetchPDF_*` tests PASS.
---
### Task 5: mergePDFs implementation
**Files:**
- Modify: `services/ibl_merge_report_service/merger.go` — implement `mergePDFs`
No unit test is written for `mergePDFs` because testing requires valid PDF binaries which are awkward to embed. Error propagation from `fetchPDF` is already covered by `TestFetchPDF_*`. End-to-end correctness is verified manually in Task 8.
- [ ] **Step 5.1: Implement mergePDFs**
Replace the `mergePDFs` stub in `merger.go`:
```go
func mergePDFs(urls []string) ([]byte, error) {
readers := make([]io.ReadSeeker, 0, len(urls))
for _, url := range urls {
data, err := fetchPDF(url)
if err != nil {
return nil, err
}
readers = append(readers, bytes.NewReader(data))
}
var buf bytes.Buffer
if err := api.MergeRaw(readers, &buf, nil); err != nil {
return nil, fmt.Errorf("PDF merge error: %w", err)
}
return buf.Bytes(), nil
}
```
Add `"bytes"` and `"github.com/pdfcpu/pdfcpu/pkg/api"` to the import block in `merger.go`.
Remove the now-unused `"time"` import only if `httpClient` is still using it — keep `time` because `httpClient` uses `25 * time.Second`.
The full import block for `merger.go` after this step:
```go
import (
"bytes"
"fmt"
"io"
"net/http"
"time"
"github.com/pdfcpu/pdfcpu/pkg/api"
)
```
- [ ] **Step 5.2: Run all tests**
```bash
cd services/ibl_merge_report_service && go test ./... -v
```
Expected: all previously passing tests still pass. No new tests in this task.
- [ ] **Step 5.3: Build to verify no compile errors**
```bash
cd services/ibl_merge_report_service && go build ./...
```
Expected: compiles cleanly.
- [ ] **Step 5.4: Commit**
```bash
cd /Users/fajrihardhitamurti/REPO_GITEA_IBL/BE_IBL/one-api-lab
git add services/ibl_merge_report_service/
git commit -m "TASKCODE - implement fetchPDF and mergePDFs"
```
---
### Task 6: Deployment files
**Files:**
- Create: `scripts/build-merge-service.sh`
- Create: `services/ibl_merge_report_service/ibl-merge-report.service`
- [ ] **Step 6.1: Create build script**
`scripts/build-merge-service.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
SRC_DIR="$REPO_ROOT/services/ibl_merge_report_service"
OUT_DIR="$REPO_ROOT/up"
mkdir -p "$OUT_DIR"
echo "Building ibl_merge_report_service for linux/amd64..."
cd "$SRC_DIR"
GOOS=linux GOARCH=amd64 go build -o "$OUT_DIR/ibl-merge-report-service" .
echo "Binary written to: $OUT_DIR/ibl-merge-report-service"
```
Make executable:
```bash
chmod +x scripts/build-merge-service.sh
```
- [ ] **Step 6.2: Create systemd unit template**
`services/ibl_merge_report_service/ibl-merge-report.service`:
```ini
[Unit]
Description=IBL Merge Report Service
After=network.target
[Service]
Type=simple
User=one
WorkingDirectory=/home/one/project/ibl_merge_report_service
ExecStart=/home/one/project/ibl_merge_report_service/ibl-merge-report-service
Environment=MERGE_LISTEN_ADDR=127.0.0.1:8005
Environment=MERGE_INTERNAL_SECRET=REPLACE_WITH_VALUE_FROM_DB
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
```
`MERGE_INTERNAL_SECRET` must match the value of `S_SystemsMergeReportServiceSecret` in `conf_systems`.
Deployment steps on IBL server:
1. Build: `bash scripts/build-merge-service.sh`
2. Copy binary: `scp -i /Users/fajrihardhitamurti/id_rsa up/ibl-merge-report-service one@10.9.20.31:/home/one/project/ibl_merge_report_service/`
3. Copy unit file: `scp -i /Users/fajrihardhitamurti/id_rsa services/ibl_merge_report_service/ibl-merge-report.service one@10.9.20.31:/tmp/`
4. On server: `sudo mv /tmp/ibl-merge-report.service /etc/systemd/system/`
5. On server: edit `/etc/systemd/system/ibl-merge-report.service` to set the real `MERGE_INTERNAL_SECRET`
6. On server: `sudo systemctl daemon-reload && sudo systemctl enable ibl-merge-report && sudo systemctl start ibl-merge-report`
7. Verify: `sudo systemctl status ibl-merge-report`
- [ ] **Step 6.3: Run build script to verify it compiles for Linux**
```bash
cd /Users/fajrihardhitamurti/REPO_GITEA_IBL/BE_IBL/one-api-lab && bash scripts/build-merge-service.sh
```
Expected: `up/ibl-merge-report-service` is created (Linux ELF binary).
- [ ] **Step 6.4: Commit**
```bash
cd /Users/fajrihardhitamurti/REPO_GITEA_IBL/BE_IBL/one-api-lab
git add scripts/build-merge-service.sh services/ibl_merge_report_service/ibl-merge-report.service up/ibl-merge-report-service
git commit -m "TASKCODE - add build script and systemd unit for merge report service"
```
---
### Task 7: Run all tests and final verification
- [ ] **Step 7.1: Run full test suite**
```bash
cd services/ibl_merge_report_service && go test ./... -v
```
Expected: all tests pass. Final count: 6 `TestValidate*` + 8 `TestHandler*` + 4 `TestFetchPDF*` = 18 tests.
- [ ] **Step 7.2: Run go vet**
```bash
cd services/ibl_merge_report_service && go vet ./...
```
Expected: no output (no issues).
- [ ] **Step 7.3: Smoke test locally (macOS)**
```bash
cd services/ibl_merge_report_service && go run . &
sleep 1
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8005/merge \
-H "X-Internal-Secret: ibl-merge-secret" \
-H "Content-Type: application/json" \
-d '{"name":"test.pdf","urls":["http://127.0.0.1:9999/nope"],"mergeRequestID":1,"T_OrderHeaderID":1}'
```
Expected: HTTP `502` (source unavailable — correct error code for unreachable source).
Kill background process: `kill %1`
- [ ] **Step 7.4: Verify secret validation**
```bash
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8005/merge \
-H "X-Internal-Secret: wrong-secret" \
-H "Content-Type: application/json" \
-d '{}'
```
Expected: `401`.
---
## Error Code Reference
| Go returns | PHP maps to | Meaning |
|------------|------------|---------|
| `401` | `MERGE_SERVICE_UNAUTHORIZED` | Bad secret |
| `404` | `MERGE_SOURCE_NOT_FOUND` | Source PDF returned 404 |
| `422` | `MERGE_SERVICE_REJECTED` | Validation error |
| `500` | `MERGE_SERVICE_FAILED` | pdfcpu merge error |
| `502` | `MERGE_SERVICE_FAILED` | Source fetch network/non-404 error |
---
## Self-Review: Spec Coverage
- [x] Service listens on `127.0.0.1:8005` (configurable via `MERGE_LISTEN_ADDR`)
- [x] `POST /merge` endpoint
- [x] Validates `X-Internal-Secret` header → 401
- [x] Validates JSON body fields (name, urls, mergeRequestID, T_OrderHeaderID) → 422
- [x] Fetches each URL sequentially — if any fails, entire request fails
- [x] Merges with pdfcpu, streams as `application/pdf`
- [x] No cache / no file artifacts (in-memory only)
- [x] Error responses use HTTP status codes that map correctly to PHP gateway error codes
- [x] Build script for Linux amd64
- [x] Systemd unit for IBL server deployment

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_fajrihardhitamurti_repo_gitea_ibl_be_ibl_one_api_lab_application_third_party_fpdf_font_helveticab_php", "label": "helveticab.php", "file_type": "code", "source_file": "/Users/fajrihardhitamurti/REPO_GITEA_IBL/BE_IBL/one-api-lab/application/third_party/fpdf/font/helveticab.php", "source_location": "L1"}], "edges": [], "raw_calls": []}

Some files were not shown because too many files have changed in this diff Show More