Update Refactoring Patient
This commit is contained in:
parent
3f656bfa87
commit
fda7a14a5f
@ -23,12 +23,12 @@ $routes->post('/api/auth/register', 'Auth::register');
|
||||
$routes->get('/api/auth/check', 'Auth::checkAuth');
|
||||
$routes->post('/api/auth/logout', 'Auth::logout');
|
||||
|
||||
$routes->get('/api/patient', 'Patient::index');
|
||||
$routes->post('/api/patient', 'Patient::create');
|
||||
$routes->get('/api/patient/(:num)', 'Patient::show/$1');
|
||||
$routes->delete('/api/patient', 'Patient::delete');
|
||||
$routes->patch('/api/patient', 'Patient::update');
|
||||
$routes->get('/api/patient/check', 'Patient::patientCheck');
|
||||
$routes->get('/api/patient', 'Patient\Patient::index');
|
||||
$routes->post('/api/patient', 'Patient\Patient::create');
|
||||
$routes->get('/api/patient/(:num)', 'Patient\Patient::show/$1');
|
||||
$routes->delete('/api/patient', 'Patient\Patient::delete');
|
||||
$routes->patch('/api/patient', 'Patient\Patient::update');
|
||||
$routes->get('/api/patient/check', 'Patient\Patient::patientCheck');
|
||||
|
||||
//$routes->get('/api/patvisit', 'Patient::index');
|
||||
$routes->post('/api/patvisit', 'PatVisit::create');
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
namespace App\Controllers\Patient;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use CodeIgniter\Controller;
|
||||
use App\Models\PatientModel;
|
||||
|
||||
use App\Models\Patient\PatientModel;
|
||||
|
||||
class Patient extends Controller {
|
||||
use ResponseTrait;
|
||||
@ -63,14 +64,6 @@ class Patient extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private function validationError(string $context, array $errors) {
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'message' => "Validation failed ({$context})",
|
||||
'errors' => $errors
|
||||
], 400);
|
||||
}
|
||||
|
||||
public function update() {
|
||||
$input = $this->request->getJSON(true);
|
||||
if (!$this->validateData($input, $this->rules)) { return $this->validationError('patient', $this->validator->getErrors()); }
|
||||
@ -154,4 +147,12 @@ class Patient extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private function validationError(string $context, array $errors) {
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'message' => "Validation failed ({$context})",
|
||||
'errors' => $errors
|
||||
], 400);
|
||||
}
|
||||
|
||||
}
|
||||
88
app/Models/Patient/PatAttModel.php
Normal file
88
app/Models/Patient/PatAttModel.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace App\Models\Patient;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\BaseUtcModel;
|
||||
|
||||
class PatAttModel extends BaseModel {
|
||||
protected $table = 'patatt';
|
||||
protected $primaryKey = 'PatAttID';
|
||||
protected $allowedFields = ['InternalPID', 'Address', 'UserID', 'CreateDate', 'DelDate'];
|
||||
|
||||
protected $useTimestamps = true;
|
||||
protected $createdField = 'CreateDate';
|
||||
protected $updatedField = '';
|
||||
protected $useSoftDeletes = true;
|
||||
protected $deletedField = 'DelDate';
|
||||
|
||||
protected $beforeInsert = ['normalizeDatesToUTC'];
|
||||
protected $beforeUpdate = ['normalizeDatesToUTC'];
|
||||
protected $afterFind = ['convertDatesToUTCISO'];
|
||||
protected $afterInsert = ['convertDatesToUTCISO'];
|
||||
protected $afterUpdate = ['convertDatesToUTCISO'];
|
||||
|
||||
public function createPatAtt($patatt, string $newInternalPID) {
|
||||
foreach ($patatt as &$row) {
|
||||
$row['InternalPID'] = (string)$newInternalPID;
|
||||
}
|
||||
$this->insertBatch($patatt);
|
||||
}
|
||||
|
||||
public function updatePatAtt(array $patatt, string $InternalPID) {
|
||||
// Ambil daftar address aktif saat ini dari DB
|
||||
$oldActive = $this->getActiveAddresses($InternalPID);
|
||||
|
||||
// Normalisasi input baru (hapus duplikat & pastikan punya key 'Address')
|
||||
$mapNew = [];
|
||||
foreach ($patatt as $row) {
|
||||
if (empty($row['Address'])) continue;
|
||||
$mapNew[$row['Address']] = $row; // overwrite duplikat
|
||||
}
|
||||
|
||||
$newData = array_keys($mapNew);
|
||||
|
||||
// Hitung data yang ditambah & dihapus
|
||||
$added = array_diff($newData, $oldActive); // address baru
|
||||
$removed = array_diff($oldActive, $newData); // address lama dihapus
|
||||
|
||||
// Soft delete yang dihapus
|
||||
if (!empty($removed)) {
|
||||
$this->softDeleteByAddresses($InternalPID, $removed);
|
||||
}
|
||||
|
||||
// Tambahkan / re-activate yang baru
|
||||
foreach ($added as $addr) {
|
||||
$data = $mapNew[$addr];
|
||||
$data['InternalPID'] = $InternalPID;
|
||||
|
||||
// Coba re-activate dulu
|
||||
$this->reactivateAddress($InternalPID, $addr);
|
||||
|
||||
// Cek apakah ada baris yang di-update (re-activate)
|
||||
if ($this->db->affectedRows() === 0) {
|
||||
// Tidak ada baris lama yang dihapus → insert baru
|
||||
$this->insert($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deletePatAtt(string $InternalPID) {
|
||||
$this->where('InternalPID', $InternalPID)->delete();
|
||||
}
|
||||
|
||||
// Dapatkan daftar alamat aktif berdasarkan InternalPID
|
||||
public function getActiveAddresses(string $InternalPID): array {
|
||||
$rows = $this->select('Address')->where('InternalPID', $InternalPID)->where('DelDate', null)->findAll();
|
||||
return array_column($rows, 'Address');
|
||||
}
|
||||
// Soft delete beberapa address sekaligus
|
||||
public function softDeleteByAddresses(string $InternalPID, array $addresses) {
|
||||
if (empty($addresses)) return;
|
||||
$this->where('InternalPID', $InternalPID)->whereIn('Address', $addresses)->delete();
|
||||
}
|
||||
// Reactivate (hapus DelDate) 1 address jika pernah dihapus
|
||||
// Return true jika berhasil re-activate
|
||||
public function reactivateAddress(string $InternalPID, string $Address): bool {
|
||||
return $this->where('InternalPID', $InternalPID)->where('Address', $Address)->where('DelDate IS NOT NULL', null, false)->set('DelDate', null)->orderBy('PatAttID', 'DESC')->limit(1)->update();
|
||||
}
|
||||
}
|
||||
44
app/Models/Patient/PatComModel.php
Normal file
44
app/Models/Patient/PatComModel.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace App\Models\Patient;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\BaseUtcModel;
|
||||
|
||||
class PatComModel extends BaseModel {
|
||||
protected $table = 'patcom';
|
||||
protected $primaryKey = 'PatComID ';
|
||||
protected $allowedFields = ['InternalPID', 'Comment', 'CreateDate', 'DelDate'];
|
||||
|
||||
protected $useTimestamps = true;
|
||||
protected $createdField = 'CreateDate';
|
||||
protected $updatedField = '';
|
||||
protected $useSoftDeletes = true;
|
||||
protected $deletedField = 'EndDate';
|
||||
|
||||
protected $beforeInsert = ['normalizeDatesToUTC'];
|
||||
protected $beforeUpdate = ['normalizeDatesToUTC'];
|
||||
protected $afterFind = ['convertDatesToUTCISO'];
|
||||
protected $afterInsert = ['convertDatesToUTCISO'];
|
||||
protected $afterUpdate = ['convertDatesToUTCISO'];
|
||||
|
||||
public function createPatCom(string $patcom, string $newInternalPID) {
|
||||
$this->insert(["InternalPID" => $newInternalPID, "Comment" => $patcom]);
|
||||
}
|
||||
|
||||
public function updatePatCom(string $patcom, string $InternalPID) {
|
||||
$exists = $this->where('InternalPID', $InternalPID)->first();
|
||||
|
||||
if ($exists) {
|
||||
//Update
|
||||
$this->where('InternalPID', $InternalPID)->set('Comment', $patcom)->update();
|
||||
} else {
|
||||
//Insert
|
||||
$this->insert(['InternalPID' => $InternalPID, 'Comment' => $patcom]);
|
||||
}
|
||||
}
|
||||
|
||||
public function deletePatCom(string $InternalPID) {
|
||||
$this->where('InternalPID', $InternalPID)->delete();
|
||||
}
|
||||
|
||||
}
|
||||
51
app/Models/Patient/PatIdtModel.php
Normal file
51
app/Models/Patient/PatIdtModel.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace App\Models\Patient;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\BaseUtcModel;
|
||||
|
||||
class PatIdtModel extends BaseModel {
|
||||
protected $table = 'patidt';
|
||||
protected $primaryKey = 'PatIdtID';
|
||||
protected $allowedFields = ['InternalPID', 'IdentifierType', 'Identifier', 'EffectiveDate', 'ExpirationDate', 'CreateDate', 'DelDate'];
|
||||
|
||||
protected $useTimestamps = true;
|
||||
protected $createdField = 'CreateDate';
|
||||
protected $updatedField = '';
|
||||
protected $useSoftDeletes = true;
|
||||
protected $deletedField = 'DelDate';
|
||||
|
||||
protected $beforeInsert = ['normalizeDatesToUTC'];
|
||||
protected $beforeUpdate = ['normalizeDatesToUTC'];
|
||||
protected $afterFind = ['convertDatesToUTCISO'];
|
||||
protected $afterInsert = ['convertDatesToUTCISO'];
|
||||
protected $afterUpdate = ['convertDatesToUTCISO'];
|
||||
|
||||
public function createPatIdt($patidt, string $newInternalPID) {
|
||||
$this->insert(["InternalPID" => $newInternalPID, "IdentifierType" => $patidt['IdentifierType'], 'Identifier' => $patidt['Identifier']]);
|
||||
}
|
||||
|
||||
public function updatePatIdt($patidt, string $InternalPID) {
|
||||
$exists = $this->where('InternalPID', $InternalPID)->first();
|
||||
// $exists = $db->table('patidt')->where('InternalPID', $InternalPID)->get()->getRowArray();
|
||||
|
||||
if ($exists) {
|
||||
// Update
|
||||
$this->where('InternalPID', $InternalPID)->set($patidt)->update();
|
||||
// $db->table('patidt')->where('InternalPID', $InternalPID)->update($patidt);
|
||||
|
||||
} else {
|
||||
// Insert
|
||||
$patidt['InternalPID'] = $InternalPID;
|
||||
$this->insert($patidt);
|
||||
|
||||
// $patidt['InternalPID'] = $InternalPID;
|
||||
// $db->table('patidt')->insert($patidt);
|
||||
}
|
||||
}
|
||||
|
||||
public function deletePatIdt(string $InternalPID) {
|
||||
$this->where('InternalPID', $InternalPID)->delete();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,12 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
namespace App\Models\Patient;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\BaseUtcModel;
|
||||
|
||||
use App\Models\Patient\PatAttModel;
|
||||
use App\Models\Patient\PatComModel;
|
||||
use App\Models\Patient\PatIdtModel;
|
||||
|
||||
use CodeIgniter\Database\RawSql;
|
||||
|
||||
@ -16,6 +23,12 @@ class PatientModel extends BaseModel {
|
||||
protected $useSoftDeletes = true;
|
||||
protected $deletedField = 'DelDate';
|
||||
|
||||
protected $beforeInsert = ['normalizeDatesToUTC'];
|
||||
protected $beforeUpdate = ['normalizeDatesToUTC'];
|
||||
protected $afterFind = ['convertDatesToUTCISO'];
|
||||
protected $afterInsert = ['convertDatesToUTCISO'];
|
||||
protected $afterUpdate = ['convertDatesToUTCISO'];
|
||||
|
||||
public function getPatients($filters = []) {
|
||||
$qname = "LOWER(CONCAT_WS(' ', IFNULL(Prefix,''), IFNULL(NameFirst,''), IFNULL(NameMiddle,''), IFNULL(NameLast,''), IFNULL(NameMaiden,''), IFNULL(Suffix,'')))";
|
||||
|
||||
@ -114,45 +127,43 @@ class PatientModel extends BaseModel {
|
||||
|
||||
public function createPatient($input) {
|
||||
$db = \Config\Database::connect();
|
||||
$patidt = $input['PatIdt'] ?? [];
|
||||
$patatt = $input['PatAtt'] ?? [];
|
||||
$patcom['Comment'] = $input['PatCom'] ?? null;
|
||||
$this->modelPatAtt = new PatAttModel();
|
||||
$this->modelPatCom = new PatComModel();
|
||||
$this->modelPatIdt = new PatIdtModel();
|
||||
|
||||
$input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];
|
||||
$input['Birthdate'] = $this->isValidDateTime($input['Birthdate']);
|
||||
$input['DeathDateTime'] = $this->isValidDateTime($input['DeathDateTime']);
|
||||
|
||||
$db->transBegin();
|
||||
|
||||
try {
|
||||
|
||||
// Insert Data ke Tabel Patient, get ID dan cek apa ada error
|
||||
$this->insert($input);
|
||||
$newInternalPID = $this->getInsertID();
|
||||
$this->checkDbError($db, 'Insert patient');
|
||||
|
||||
if (!empty($patidt)) {
|
||||
$patidt['InternalPID'] = $newInternalPID;
|
||||
$db->table('patidt')->insert($patidt);
|
||||
$this->checkDbError($db, 'Insert patidt');
|
||||
// Insert Data ke Tabel PatIdt
|
||||
if (!empty($input['PatIdt'])) {
|
||||
$this->modelPatIdt->createPatIdt($input['PatIdt'], $newInternalPID);
|
||||
$this->checkDbError($db, 'Insert PatIdt');
|
||||
}
|
||||
|
||||
if (!empty($patcom)) {
|
||||
$patcom['InternalPID'] = $newInternalPID;
|
||||
$db->table('patcom')->insert($patcom);
|
||||
$this->checkDbError($db, 'Insert patcom');
|
||||
// Insert Data ke Tabel PatCom
|
||||
if (!empty($input['PatCom'])) {
|
||||
$this->modelPatCom->createPatCom($input['PatCom'], $newInternalPID);
|
||||
$this->checkDbError($db, 'Insert PatCom');
|
||||
}
|
||||
|
||||
if (!empty($patatt)) {
|
||||
foreach ($patatt as &$row) {
|
||||
$row['InternalPID'] = $newInternalPID;
|
||||
}
|
||||
$db->table('patatt')->upsertBatch($patatt);
|
||||
$this->checkDbError($db, 'Insert patatt');
|
||||
// Insert Data ke Tabel PatAtt
|
||||
if (!empty($input['PatAtt'])) {
|
||||
$this->modelPatAtt->createPatAtt($input['PatAtt'], $newInternalPID);
|
||||
$this->checkDbError($db, 'Insert PatAtt');
|
||||
}
|
||||
|
||||
$db->transCommit();
|
||||
// $db->transComplete();
|
||||
// if ($db->transStatus() === false) {
|
||||
// throw new \Exception("Failed to sync patient relations");
|
||||
// }
|
||||
|
||||
return $newInternalPID;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
@ -163,9 +174,10 @@ class PatientModel extends BaseModel {
|
||||
|
||||
public function updatePatient($input) {
|
||||
$db = \Config\Database::connect();
|
||||
$patidt = $input['PatIdt'] ?? [];
|
||||
$patatt = $input['PatAtt'] ?? [];
|
||||
$patcom['Comment'] = $input['PatCom'] ?? null;
|
||||
$this->modelPatIdt = new PatIdtModel();
|
||||
$this->modelPatCom = new PatComModel();
|
||||
$this->modelPatAtt = new PatAttModel();
|
||||
|
||||
$input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];
|
||||
$input['Birthdate'] = $this->isValidDateTime($input['Birthdate']);
|
||||
$input['DeathDateTime'] = $this->isValidDateTime($input['DeathDateTime']);
|
||||
@ -173,109 +185,41 @@ class PatientModel extends BaseModel {
|
||||
$db->transBegin();
|
||||
|
||||
try {
|
||||
|
||||
// Update Patient
|
||||
$InternalPID = $input['InternalPID'];
|
||||
$this->where('InternalPID',$InternalPID)->set($input)->update();
|
||||
$this->checkDbError($db, 'Update patient');
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
if (!empty($patidt)) {
|
||||
$exists = $db->table('patidt')->where('InternalPID', $InternalPID)->get()->getRowArray();
|
||||
if ($exists) {
|
||||
$db->table('patidt')->where('InternalPID', $InternalPID)->update($patidt);
|
||||
// Update Patidt
|
||||
if (!empty($input['PatIdt'])) {
|
||||
$this->modelPatIdt->updatePatIdt($input['PatIdt'], $InternalPID);
|
||||
$this->checkDbError($db, 'Update patIdt');
|
||||
} else {
|
||||
$patidt['InternalPID'] = $InternalPID;
|
||||
$db->table('patidt')->insert($patidt);
|
||||
$this->checkDbError($db, 'Update patidt');
|
||||
}
|
||||
} else {
|
||||
$db->table('patidt')->where('InternalPID', $InternalPID)->delete();
|
||||
$this->modelPatIdt->deletePatIdt($InternalPID);
|
||||
$this->checkDbError($db, 'Update patidt');
|
||||
}
|
||||
|
||||
if (!empty($patcom)) {
|
||||
$exists = $db->table('patcom')->where('InternalPID', $InternalPID)->get()->getRowArray();
|
||||
if ($exists) {
|
||||
$db->table('patcom')->where('InternalPID', $InternalPID)->update($patcom);
|
||||
$this->checkDbError($db, 'Update patcom');
|
||||
// Update Patcom
|
||||
if (!empty($input['PatCom'])) {
|
||||
$this->modelPatCom->updatePatCom($input['PatCom'], $InternalPID);
|
||||
$this->checkDbError($db, 'Update PatCom');
|
||||
} else {
|
||||
$patcom['InternalPID'] = $InternalPID;
|
||||
$db->table('patcom')->insert($patcom);
|
||||
$this->checkDbError($db, 'Update patcom');
|
||||
}
|
||||
} else {
|
||||
$db->table('patcom')->where('InternalPID', $InternalPID)->delete();
|
||||
$this->modelPatCom->deletePatCom($InternalPID);
|
||||
$this->checkDbError($db, 'Update patcom');
|
||||
}
|
||||
|
||||
if (!empty($patatt)) {
|
||||
// Ambil daftar address aktif (DelDate IS NULL) di DB
|
||||
$oldActive = $db->table('patatt')
|
||||
->select('Address')
|
||||
->where('InternalPID', $InternalPID)
|
||||
->where('DelDate', null)
|
||||
->get()->getResultArray();
|
||||
$oldActive = array_column($oldActive, 'Address');
|
||||
|
||||
// Normalisasi & dedup input baru (berdasarkan Address)
|
||||
$mapNew = [];
|
||||
foreach ($patatt as $row) {
|
||||
if (!isset($row['Address'])) continue;
|
||||
$mapNew[$row['Address']] = $row; // overwrite duplikat di input
|
||||
}
|
||||
$newData = array_keys($mapNew);
|
||||
|
||||
// Hitung yang perlu ditambah & dihapus
|
||||
$added = array_diff($newData, $oldActive); // baru (belum aktif)
|
||||
$removed = array_diff($oldActive, $newData); // dulu aktif tapi hilang di input
|
||||
|
||||
|
||||
// 1) Soft delete yang dihapus
|
||||
if (!empty($removed)) {
|
||||
$db->table('patatt')
|
||||
->where('InternalPID', $InternalPID)
|
||||
->whereIn('Address', $removed)
|
||||
->set('DelDate', $now)
|
||||
->update();
|
||||
$this->checkDbError($db, 'Update/Delete patatt');
|
||||
}
|
||||
|
||||
// 2) Tambahkan yang baru
|
||||
foreach ($added as $addr) {
|
||||
$data = $mapNew[$addr];
|
||||
$data['InternalPID'] = $InternalPID;
|
||||
|
||||
// Coba REACTIVATE satu baris yang pernah di-soft delete (kalau ada)
|
||||
$builder = $db->table('patatt');
|
||||
$builder->set('DelDate', null);
|
||||
// Kalau ada kolom lain yang mau di-update saat re-activate, set di sini juga
|
||||
// mis: $builder->set('Note', $data['Note'] ?? null);
|
||||
|
||||
$builder->where('InternalPID', $InternalPID)
|
||||
->where('Address', $addr)
|
||||
->where('DelDate IS NOT NULL', null, false)
|
||||
->orderBy('PatAttID', 'DESC')
|
||||
->limit(1)
|
||||
->update();
|
||||
$this->checkDbError($db, 'Update/Insert patatt');
|
||||
|
||||
if ($db->affectedRows() === 0) {
|
||||
// Tidak ada baris soft-deleted untuk alamat ini → INSERT baru
|
||||
$db->table('patatt')->insert($data);
|
||||
$this->checkDbError($db, 'Update/Insert patatt');
|
||||
}
|
||||
}
|
||||
|
||||
// Update Patatt
|
||||
if (!empty($input['PatAtt'])) {
|
||||
$this->modelPatAtt->updatePatAtt($input['PatAtt'], $InternalPID);
|
||||
$this->checkDbError($db, 'Update PatAtt');
|
||||
} else {
|
||||
// Input kosong → semua yang masih aktif di-soft delete
|
||||
$db->table('patatt')->where('InternalPID', $InternalPID)->where('DelDate', null)->set('DelDate', $now)->update();
|
||||
$this->modelPatAtt->deletePatAtt($InternalPID);
|
||||
$this->checkDbError($db, 'Update/Delete patatt');
|
||||
}
|
||||
|
||||
$db->transCommit();
|
||||
// if ($db->transStatus() === false) {
|
||||
// throw new \Exception('Failed to sync patient relations');
|
||||
// }
|
||||
|
||||
return $InternalPID;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
@ -50,7 +50,7 @@ class PatientCreateTest extends CIUnitTestCase
|
||||
|
||||
$faker = Factory::create('id_ID');
|
||||
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
|
||||
$payload = [
|
||||
"PatientID" => "DUM" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
|
||||
@ -100,9 +100,8 @@ class PatientCreateTest extends CIUnitTestCase
|
||||
}
|
||||
|
||||
$result = $this->withBodyFormat('json')->call('post', 'api/patient', $payload);
|
||||
$result->assertStatus(201);
|
||||
$result->assertJSONFragment(['status' => 'success']);
|
||||
|
||||
$result->assertStatus(201);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user