127 lines
22 KiB
PHP
127 lines
22 KiB
PHP
<?php
|
|
defined('BASEPATH') OR exit('No direct script access allowed');
|
|
|
|
class Migrate extends CI_Controller
|
|
{
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
if (!$this->input->is_cli_request()) {
|
|
show_error('Migration hanya dapat dijalankan melalui CLI.', 403, 'Akses ditolak');
|
|
}
|
|
$this->load->library('migration');
|
|
}
|
|
|
|
public function latest()
|
|
{
|
|
$version = $this->migration->latest();
|
|
if ($version === false) {
|
|
fwrite(STDERR, $this->migration->error_string() . PHP_EOL);
|
|
exit(1);
|
|
}
|
|
if ($version === true) {
|
|
$row = $this->db->order_by('version', 'DESC')->limit(1)->get('migrations')->row();
|
|
$version = $row ? $row->version : 0;
|
|
}
|
|
echo 'Migration berhasil. Version: ' . $version . PHP_EOL;
|
|
}
|
|
|
|
public function version($version = 0)
|
|
{
|
|
$result = $this->migration->version((int) $version);
|
|
if ($result === false) {
|
|
fwrite(STDERR, $this->migration->error_string() . PHP_EOL);
|
|
exit(1);
|
|
}
|
|
echo 'Migration berhasil. Version: ' . $result . PHP_EOL;
|
|
}
|
|
|
|
public function status()
|
|
{
|
|
$migration = $this->db->order_by('version', 'DESC')->limit(1)->get('migrations')->row();
|
|
echo 'Version: ' . ($migration ? $migration->version : 0) . PHP_EOL;
|
|
echo 'document_sequences: ' . ($this->db->table_exists('document_sequences') ? 'ready' : 'missing') . PHP_EOL;
|
|
echo 'system_account_mappings: ' . ($this->db->table_exists('system_account_mappings') ? 'ready' : 'missing') . PHP_EOL;
|
|
if ($this->db->table_exists('system_account_mappings')) {
|
|
echo 'active_mappings: ' . $this->db->where('is_active', 1)->count_all_results('system_account_mappings') . PHP_EOL;
|
|
}
|
|
foreach (array('customer_equipment_registry','technician_equipment_documents','technician_custody_allocations','technician_equipment_document_lines','technician_equipment_inspections') as $table) {
|
|
echo $table . ': ' . ($this->db->table_exists($table) ? 'ready (' . $this->db->count_all($table) . ' rows)' : 'missing') . PHP_EOL;
|
|
}
|
|
if ($this->db->table_exists('k_employees')) {
|
|
echo 'k_employees.is_active: ' . ($this->db->field_exists('is_active','k_employees') ? 'yes' : 'no') . PHP_EOL;
|
|
echo 'k_employees.user_id: ' . ($this->db->field_exists('user_id','k_employees') ? 'yes' : 'no') . PHP_EOL;
|
|
}
|
|
if ($this->db->table_exists('users')) echo 'users.employee_id: ' . ($this->db->field_exists('employee_id','users') ? 'yes' : 'no') . PHP_EOL;
|
|
}
|
|
|
|
public function equipment_audit()
|
|
{
|
|
$checks=array(
|
|
'negative_balances'=>"SELECT COUNT(*) total FROM technician_custody_allocations WHERE qty<=0",
|
|
'duplicate_balance_keys'=>"SELECT COUNT(*) total FROM(SELECT balance_key FROM technician_custody_allocations GROUP BY balance_key HAVING COUNT(*)>1)x",
|
|
'invalid_unit_quantities'=>"SELECT COUNT(*) total FROM technician_custody_allocations a JOIN item_barcodes b ON b.id=a.barcode_id JOIN items i ON i.id=b.item_id LEFT JOIN kode_barang k ON k.id=i.kode_id WHERE a.subject_type='company_barcode' AND UPPER(COALESCE(k.tracking_type,'QTY'))='UNIT' AND ABS(a.qty-1)>.0001",
|
|
'overheld_company_qty'=>"SELECT COUNT(*) total FROM(SELECT a.barcode_id,SUM(a.qty) held,MAX(b.qty_sisa) qty_sisa FROM technician_custody_allocations a JOIN item_barcodes b ON b.id=a.barcode_id WHERE a.subject_type='company_barcode' GROUP BY a.barcode_id HAVING held>qty_sisa+.0001)x",
|
|
'orphan_document_lines'=>"SELECT COUNT(*) total FROM technician_equipment_document_lines l LEFT JOIN technician_equipment_documents d ON d.id=l.document_id WHERE d.id IS NULL",
|
|
'external_inventory_links'=>"SELECT COUNT(*) total FROM customer_equipment_registry r JOIN technician_custody_allocations a ON a.customer_equipment_id=r.id WHERE a.item_id IS NOT NULL OR a.barcode_id IS NOT NULL"
|
|
);
|
|
foreach($checks as$name=>$sql){$row=$this->db->query($sql)->row();echo$name.': '.(int)$row->total.PHP_EOL;}
|
|
echo 'Audit bersifat read-only; nilai 0 berarti lulus.' . PHP_EOL;
|
|
}
|
|
|
|
public function equipment_smoke()
|
|
{
|
|
$fixture=$this->db->query("SELECT b.id barcode_id,b.barcode,b.warehouse_id,i.company_id,COALESCE(k.tracking_type,'QTY') tracking_type,(b.qty_sisa-b.reserved_qty-b.return_reserved_qty) available FROM item_barcodes b JOIN items i ON i.id=b.item_id LEFT JOIN kode_barang k ON k.id=i.kode_id WHERE b.qty_sisa-b.reserved_qty-b.return_reserved_qty>=1 AND b.status='available' ORDER BY b.id LIMIT 1")->row();
|
|
if(!$fixture){echo"SKIP: tidak ada barcode tersedia untuk smoke test.\n";return;}$employee=$this->db->where(array('company_id'=>$fixture->company_id,'is_active'=>1))->order_by('id')->get('k_employees')->row();$customer=$this->db->where('company_id',$fixture->company_id)->order_by('id')->get('customers')->row();$user=$this->db->order_by('id')->get('users')->row();if(!$employee||!$customer||!$user){echo"SKIP: fixture teknisi/customer/user tidak lengkap.\n";return;}
|
|
$this->session->set_userdata(array('company_id'=>(int)$fixture->company_id,'user_id'=>(int)$user->id));$this->load->library('TechnicianEquipmentService');$service=$this->technicianequipmentservice;$prefix='SMOKE-'.date('YmdHis').'-'.bin2hex(random_bytes(3));$beforeJournal=$this->db->count_all('journals');$beforeStock=$this->db->count_all('stock_logs');$this->db->trans_begin(true);
|
|
try{$qty=1;$handover=$service->process(array('action'=>'handover','subject_id'=>$fixture->barcode_id,'technician_id'=>$employee->id,'qty'=>$qty,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Serah terima','idempotency_key'=>$prefix.'-H'),$user->id);$duplicate=$service->process(array('action'=>'handover','subject_id'=>$fixture->barcode_id,'technician_id'=>$employee->id,'qty'=>$qty,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Serah terima','idempotency_key'=>$prefix.'-H'),$user->id);if(empty($duplicate['duplicate']))throw new RuntimeException('Idempotency gagal.');$held=$this->db->where(array('company_id'=>$fixture->company_id,'subject_type'=>'company_barcode','subject_id'=>$fixture->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employee->id,'usage_status'=>'carried'))->where('qty >',0)->get('technician_custody_allocations')->row();if(!$held)throw new RuntimeException('Saldo teknisi tidak terbentuk.');$service->process(array('action'=>'install','allocation_id'=>$held->id,'customer_id'=>$customer->id,'qty'=>$qty,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Pemasangan','idempotency_key'=>$prefix.'-I'),$user->id);$installed=$this->db->where(array('company_id'=>$fixture->company_id,'subject_type'=>'company_barcode','subject_id'=>$fixture->barcode_id,'custodian_type'=>'customer','customer_id'=>$customer->id,'usage_status'=>'installed'))->where('qty >',0)->get('technician_custody_allocations')->row();$service->process(array('action'=>'remove','allocation_id'=>$installed->id,'technician_id'=>$employee->id,'qty'=>$qty,'condition_status'=>'inspection_required','date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Pelepasan','idempotency_key'=>$prefix.'-U'),$user->id);$removed=$this->db->where(array('company_id'=>$fixture->company_id,'subject_type'=>'company_barcode','subject_id'=>$fixture->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employee->id,'usage_status'=>'removed'))->where('qty >',0)->get('technician_custody_allocations')->row();$service->process(array('action'=>'return','allocation_id'=>$removed->id,'warehouse_id'=>$fixture->warehouse_id,'qty'=>$qty,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Kembali gudang','idempotency_key'=>$prefix.'-R'),$user->id);$queue=$this->db->where(array('company_id'=>$fixture->company_id,'subject_type'=>'company_barcode','subject_id'=>$fixture->barcode_id,'custodian_type'=>'quarantine','condition_status'=>'inspection_required'))->where('qty >',0)->get('technician_custody_allocations')->row();$service->inspect(array('allocation_id'=>$queue->id,'result_status'=>'good','qty'=>$qty,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Layak','idempotency_key'=>$prefix.'-Q'),$user->id);$external=$service->registerExternal(array('owner_customer_id'=>$customer->id,'equipment_name'=>'TEST BARANG CUSTOMER','external_barcode'=>$prefix.'-EXT','tracking_type'=>'UNIT','qty'=>1),$user->id);if(!empty($external['item_id'])||!empty($external['barcode_id']))throw new RuntimeException('Barang customer terhubung ke inventory.');if($this->db->count_all('journals')!==$beforeJournal||$this->db->count_all('stock_logs')!==$beforeStock)throw new RuntimeException('Modul menulis jurnal/stock_logs.');echo'PASS: '.$fixture->tracking_type.' gudang-teknisi-customer-teknisi-karantina-available, idempotency, dan barang customer non-accounting.'.PHP_EOL;}catch(Throwable$e){echo'FAIL: '.$e->getMessage().PHP_EOL;$this->db->trans_rollback();exit(1);}$this->db->trans_rollback();echo'ROLLBACK: seluruh data smoke test dibatalkan.'.PHP_EOL;
|
|
}
|
|
|
|
public function equipment_qty_smoke()
|
|
{
|
|
$f=$this->db->query("SELECT b.id barcode_id,b.warehouse_id,i.company_id,(b.qty_sisa-b.reserved_qty-b.return_reserved_qty) available FROM item_barcodes b JOIN items i ON i.id=b.item_id JOIN kode_barang k ON k.id=i.kode_id WHERE UPPER(k.tracking_type)='QTY' AND b.status='available' AND b.qty_sisa-b.reserved_qty-b.return_reserved_qty>=2 ORDER BY b.id LIMIT 1")->row();if(!$f){echo"SKIP: fixture barcode QTY dengan saldo >=2 tidak tersedia.\n";return;}$employee=$this->db->where(array('company_id'=>$f->company_id,'is_active'=>1))->order_by('id')->get('k_employees')->row();$customer=$this->db->where('company_id',$f->company_id)->order_by('id')->get('customers')->row();$user=$this->db->order_by('id')->get('users')->row();if(!$employee||!$customer||!$user){echo"SKIP: fixture tidak lengkap.\n";return;}$this->session->set_userdata(array('company_id'=>(int)$f->company_id,'user_id'=>(int)$user->id));$this->load->library('TechnicianEquipmentService');$s=$this->technicianequipmentservice;$key='SMOKE-QTY-'.bin2hex(random_bytes(5));$this->db->trans_begin(true);try{$s->process(array('action'=>'handover','subject_id'=>$f->barcode_id,'technician_id'=>$employee->id,'qty'=>2,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Partial QTY','idempotency_key'=>$key.'-H'),$user->id);$held=$this->db->where(array('company_id'=>$f->company_id,'subject_id'=>$f->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employee->id,'usage_status'=>'carried'))->get('technician_custody_allocations')->row();$s->process(array('action'=>'install','allocation_id'=>$held->id,'customer_id'=>$customer->id,'qty'=>1,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Pasang sebagian','idempotency_key'=>$key.'-I'),$user->id);$carried=(float)$this->db->select_sum('qty')->where(array('company_id'=>$f->company_id,'subject_id'=>$f->barcode_id,'custodian_type'=>'technician','usage_status'=>'carried'))->get('technician_custody_allocations')->row()->qty;$installed=(float)$this->db->select_sum('qty')->where(array('company_id'=>$f->company_id,'subject_id'=>$f->barcode_id,'custodian_type'=>'customer','usage_status'=>'installed'))->get('technician_custody_allocations')->row()->qty;if($carried<1||$installed<1)throw new RuntimeException('Split saldo QTY tidak terbentuk.');echo"PASS: QTY berpindah dan dipasang sebagian tanpa saldo negatif.\n";}catch(Throwable$e){echo'FAIL: '.$e->getMessage().PHP_EOL;$this->db->trans_rollback();exit(1);}$this->db->trans_rollback();echo"ROLLBACK: data smoke QTY dibatalkan.\n";
|
|
}
|
|
|
|
public function equipment_replacement_smoke()
|
|
{
|
|
$rows=$this->db->query("SELECT b.id barcode_id,b.warehouse_id,i.company_id FROM item_barcodes b JOIN items i ON i.id=b.item_id WHERE b.status='available' AND b.qty_sisa-b.reserved_qty-b.return_reserved_qty>=1 ORDER BY i.company_id,b.id LIMIT 2")->result();if(count($rows)<2||$rows[0]->company_id!=$rows[1]->company_id){echo"SKIP: dua fixture barcode pada perusahaan sama tidak tersedia.\n";return;}$company=(int)$rows[0]->company_id;$employee=$this->db->where(array('company_id'=>$company,'is_active'=>1))->order_by('id')->get('k_employees')->row();$customer=$this->db->where('company_id',$company)->order_by('id')->get('customers')->row();$user=$this->db->order_by('id')->get('users')->row();if(!$employee||!$customer||!$user){echo"SKIP: fixture tidak lengkap.\n";return;}$this->session->set_userdata(array('company_id'=>$company,'user_id'=>(int)$user->id));$this->load->library('TechnicianEquipmentService');$s=$this->technicianequipmentservice;$key='SMOKE-REPLACE-'.bin2hex(random_bytes(5));$this->db->trans_begin(true);
|
|
try{foreach($rows as$i=>$row)$s->process(array('action'=>'handover','subject_id'=>$row->barcode_id,'technician_id'=>$employee->id,'qty'=>1,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Barang penggantian','idempotency_key'=>$key.'-H'.$i),$user->id);$old=$this->db->where(array('company_id'=>$company,'subject_id'=>$rows[0]->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employee->id,'usage_status'=>'carried'))->get('technician_custody_allocations')->row();$s->process(array('action'=>'install','allocation_id'=>$old->id,'customer_id'=>$customer->id,'qty'=>1,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Instalasi lama','idempotency_key'=>$key.'-OLD'),$user->id);$oldInstalled=$this->db->where(array('company_id'=>$company,'subject_id'=>$rows[0]->barcode_id,'custodian_type'=>'customer','customer_id'=>$customer->id,'usage_status'=>'installed'))->get('technician_custody_allocations')->row();$new=$this->db->where(array('company_id'=>$company,'subject_id'=>$rows[1]->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employee->id,'usage_status'=>'carried'))->get('technician_custody_allocations')->row();$doc=$s->replacement(array('customer_id'=>$customer->id,'technician_id'=>$employee->id,'outgoing_allocation_id'=>$new->id,'outgoing_qty'=>1,'incoming_allocation_id'=>$oldInstalled->id,'incoming_qty'=>1,'old_condition'=>'inspection_required','date'=>date('Y-m-d'),'reason'=>'[TEST ROLLBACK] Penggantian','notes'=>'[TEST ROLLBACK] Atomic replacement','idempotency_key'=>$key.'-DOC'),$user->id);$lineCount=$this->db->where('document_id',$doc['document_id'])->count_all_results('technician_equipment_document_lines');if($lineCount!==2)throw new RuntimeException('Dokumen penggantian tidak memiliki dua sisi barang.');$reversal=$s->reverse(array('document_id'=>$doc['document_id'],'date'=>date('Y-m-d'),'reason'=>'[TEST ROLLBACK] Koreksi penggantian','idempotency_key'=>$key.'-REV'),$user->id);$source=$this->db->get_where('technician_equipment_documents',array('id'=>$doc['document_id']))->row();if(!$source||$source->status!=='reversed'||$this->db->where('document_id',$reversal['document_id'])->count_all_results('technician_equipment_document_lines')!==2)throw new RuntimeException('Reversal tidak membentuk histori kebalikan.');echo"PASS: penggantian atomik dan koreksi membuat reversal tanpa menghapus histori.\n";}catch(Throwable$e){echo'FAIL: '.$e->getMessage().PHP_EOL;$this->db->trans_rollback();exit(1);}$this->db->trans_rollback();echo"ROLLBACK: data smoke penggantian dibatalkan.\n";
|
|
}
|
|
|
|
public function equipment_query_smoke()
|
|
{
|
|
$access=$this->db->order_by('is_default','DESC')->order_by('user_id')->get('user_companies')->row();
|
|
if(!$access){echo"SKIP: akses user ke perusahaan belum tersedia.\n";return;}
|
|
$company=(int)$access->company_id;
|
|
$document=$this->db->where('company_id',$company)->order_by('id')->get('technician_equipment_documents')->row();
|
|
$this->session->set_userdata(array('company_id'=>$company,'user_id'=>(int)$access->user_id));
|
|
$this->load->library('TechnicianEquipmentService');
|
|
try{
|
|
$dashboard=$this->technicianequipmentservice->dashboard();
|
|
$allocations=$this->technicianequipmentservice->allocations();
|
|
$documents=$this->technicianequipmentservice->documents();
|
|
$history=$this->technicianequipmentservice->history();
|
|
$queue=$this->technicianequipmentservice->inspectionQueue();
|
|
$external=$this->technicianequipmentservice->externalList();
|
|
$customerSummaries=$this->technicianequipmentservice->customerEquipmentSummary();
|
|
if($customerSummaries){foreach(array('all','installed','stored','technician','inspection','good','damaged')as$status)$this->technicianequipmentservice->customerEquipmentRows((int)$customerSummaries[0]['customer_id'],$status);$this->technicianequipmentservice->customerEquipmentNotes((int)$customerSummaries[0]['customer_id']);}
|
|
$employee=$this->db->where(array('company_id'=>$company,'is_active'=>1))->order_by('id')->get('k_employees')->row();
|
|
if($employee){$this->technicianequipmentservice->allocations(null,null,$employee->id);$this->technicianequipmentservice->technicianHoldings($employee->id);$this->technicianequipmentservice->documents(array('technician_id'=>$employee->id));$this->technicianequipmentservice->history(array('technician_id'=>$employee->id));$this->technicianequipmentservice->inspectionQueue(array('technician_id'=>$employee->id));$this->technicianequipmentservice->externalList($employee->id);$this->technicianequipmentservice->search('a','all',$employee->id);$this->technicianequipmentservice->search('a','handover',$employee->id);$this->technicianequipmentservice->search('a','transfer',$employee->id);$this->technicianequipmentservice->search('a','return',$employee->id);$this->technicianequipmentservice->search('a','customer',$employee->id);}
|
|
if($document)$this->technicianequipmentservice->document($document->id);
|
|
$barcode=$this->db->select('b.barcode')->from('item_barcodes b')->join('items i','i.id=b.item_id')->where('i.company_id',$company)->order_by('b.id')->get()->row();
|
|
if($barcode)$this->technicianequipmentservice->scan($barcode->barcode);
|
|
echo 'PASS: query dashboard, scan, saldo, dokumen, histori, pemeriksaan, dan register customer berhasil.' . PHP_EOL;
|
|
echo 'ROWS: allocations=' . count($allocations) . ', documents=' . count($documents) . ', history=' . count($history) . ', inspection=' . count($queue) . ', external=' . count($external) . ', customer_groups=' . count($customerSummaries) . PHP_EOL;
|
|
}catch(Throwable $e){fwrite(STDERR,'FAIL: '.$e->getMessage().PHP_EOL);exit(1);}
|
|
}
|
|
|
|
public function equipment_batch_smoke()
|
|
{
|
|
$fixtures=$this->db->query("SELECT b.id barcode_id,b.warehouse_id,i.company_id FROM item_barcodes b JOIN items i ON i.id=b.item_id WHERE b.status='available' AND b.qty_sisa-b.reserved_qty-b.return_reserved_qty>=1 ORDER BY i.company_id,b.id LIMIT 2")->result();
|
|
if(count($fixtures)<2||$fixtures[0]->company_id!=$fixtures[1]->company_id){echo"SKIP: dua barcode tersedia pada perusahaan yang sama tidak ditemukan.\n";return;}
|
|
$company=(int)$fixtures[0]->company_id;$employees=$this->db->where(array('company_id'=>$company,'is_active'=>1))->order_by('id')->limit(2)->get('k_employees')->result();$access=$this->db->where('company_id',$company)->order_by('is_default','DESC')->get('user_companies')->row();if(count($employees)<2||!$access){echo"SKIP: dua teknisi atau akses perusahaan belum tersedia.\n";return;}
|
|
$this->session->set_userdata(array('company_id'=>$company,'user_id'=>(int)$access->user_id));$this->load->library('TechnicianEquipmentService');$service=$this->technicianequipmentservice;$prefix='SMOKE-BATCH-'.bin2hex(random_bytes(5));$items=array_map(function($row){return array('subject_id'=>(int)$row->barcode_id,'qty'=>1);},$fixtures);$this->db->trans_begin(true);
|
|
try{$handover=$service->processBatch(array('action'=>'handover','technician_id'=>$employees[0]->id,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Serah terima batch','items'=>$items,'idempotency_key'=>$prefix.'-H'),$access->user_id);$duplicate=$service->processBatch(array('action'=>'handover','technician_id'=>$employees[0]->id,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Serah terima batch','items'=>$items,'idempotency_key'=>$prefix.'-H'),$access->user_id);if(empty($duplicate['duplicate'])||(int)$handover['line_count']!==2)throw new RuntimeException('Dokumen batch atau idempotency tidak valid.');$allocations=array();foreach($fixtures as$fixture){$allocation=$this->db->where(array('company_id'=>$company,'subject_type'=>'company_barcode','subject_id'=>$fixture->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employees[0]->id))->where('qty >',0)->get('technician_custody_allocations')->row();if(!$allocation)throw new RuntimeException('Saldo batch teknisi tidak terbentuk.');$allocations[]=array('allocation_id'=>(int)$allocation->id,'qty'=>1);}$transfer=$service->processBatch(array('action'=>'transfer','technician_id'=>$employees[0]->id,'target_technician_id'=>$employees[1]->id,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Transfer batch','items'=>$allocations,'idempotency_key'=>$prefix.'-T'),$access->user_id);if((int)$transfer['line_count']!==2)throw new RuntimeException('Transfer batch tidak lengkap.');$returns=array();foreach($fixtures as$fixture){$allocation=$this->db->where(array('company_id'=>$company,'subject_type'=>'company_barcode','subject_id'=>$fixture->barcode_id,'custodian_type'=>'technician','custodian_id'=>$employees[1]->id))->where('qty >',0)->get('technician_custody_allocations')->row();if(!$allocation)throw new RuntimeException('Saldo hasil transfer tidak ditemukan.');$returns[]=array('allocation_id'=>(int)$allocation->id,'qty'=>1);}$returned=$service->processBatch(array('action'=>'return','technician_id'=>$employees[1]->id,'warehouse_id'=>$fixtures[0]->warehouse_id,'date'=>date('Y-m-d'),'notes'=>'[TEST ROLLBACK] Kembali batch','items'=>$returns,'idempotency_key'=>$prefix.'-R'),$access->user_id);if((int)$returned['line_count']!==2)throw new RuntimeException('Pengembalian batch tidak lengkap.');echo"PASS: dua barang diserahkan, ditransfer, dan dikembalikan secara batch; double submit tetap idempotent.\n";}catch(Throwable$e){echo'FAIL: '.$e->getMessage().PHP_EOL;$this->db->trans_rollback();exit(1);}$this->db->trans_rollback();echo"ROLLBACK: data smoke batch dibatalkan.\n";
|
|
}
|
|
}
|