Update besar apk finance

This commit is contained in:
Wian Drs
2026-09-11 16:03:00 +07:00
parent d42483b350
commit 4ef15c22ae
897 changed files with 98056 additions and 15979 deletions
+242 -99
View File
@@ -1,7 +1,9 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Items extends CI_Controller {
class Items extends MY_Controller {
private function companyId(){ return (int)$this->session->userdata('company_id'); }
private function companyItem($id){ return $this->db->get_where('items',array('id'=>(int)$id,'company_id'=>$this->companyId()))->row(); }
public function __construct()
{
@@ -30,6 +32,7 @@ class Items extends CI_Controller {
public function item_barcodes($item_id)
{
if(!$this->companyItem($item_id)) show_404();
$data = [
"active_menu" => "draft_items",
"item_id" => $item_id
@@ -76,8 +79,9 @@ class Items extends CI_Controller {
")
->from('item_barcodes')
->join('items','items.id=item_barcodes.item_id')
->where('item_barcodes.barcode',$barcode)
->or_where('item_barcodes.serial_number',$barcode)
->where('items.company_id',$this->companyId())
->group_start()->where('item_barcodes.barcode',$barcode)
->or_where('item_barcodes.serial_number',$barcode)->group_end()
->get()
->row();
@@ -134,10 +138,7 @@ class Items extends CI_Controller {
public function load_data_barcode($item_id)
{
$item = $this->db
->where('id', $item_id)
->get('items')
->row();
$item = $this->companyItem($item_id);
if (!$item) {
echo json_encode([
@@ -149,48 +150,61 @@ class Items extends CI_Controller {
$next = $this->db
->where('item_id', $item_id)
->where('status', 'pending')
->where('serial_number IS NULL', NULL, false)
->order_by('id', 'ASC')
->get('item_barcodes')
->row();
$start = max(0, (int)$this->input->get('start'));
$length = min(100, max(10, (int)($this->input->get('length') ?: 50)));
$search = trim((string)$this->input->get('search', true));
$total = (int)$this->db->where('item_id', $item_id)->count_all_results('item_barcodes');
$done = (int)$this->db->where('item_id', $item_id)->where('serial_number IS NOT NULL', NULL, false)->where('serial_number !=', '')->count_all_results('item_barcodes');
$this->db->where('item_id', $item_id);
if ($search !== '') {
$this->db->group_start()->like('barcode', $search)->or_like('serial_number', $search)->group_end();
}
$filtered = (int)$this->db->count_all_results('item_barcodes');
$this->db->where('item_id', $item_id);
if ($search !== '') {
$this->db->group_start()->like('barcode', $search)->or_like('serial_number', $search)->group_end();
}
$list = $this->db
->where('item_id', $item_id)
->order_by('id', 'ASC')
->limit($length, $start)
->get('item_barcodes')
->result();
$total = count($list);
$done = 0;
foreach ($list as $row) {
if (!empty($row->serial_number)) {
$done++;
}
}
echo json_encode([
'status' => true,
'item_status' => $item->status,
'item_sale_price' => (float)$item->harga_jual,
'item_purchase_price' => (float)$item->harga_beli,
'next' => $next,
'list' => $list,
'total' => $total,
'done' => $done,
'filtered' => $filtered,
'start' => $start,
'length' => $length,
'percent' => $total > 0 ? round(($done / $total) * 100) : 0
]);
}
public function print_barcode($item_id)
{
if(!$this->companyItem($item_id)) show_404();
require_once APPPATH.'third_party/fpdf/fpdf.php';
require_once APPPATH.'libraries/phpqrcode/qrlib.php';
$barcodes = $this->db
->where('item_id',$item_id)
->where('status !=', 'sold_out')
->order_by('id','ASC')
->get('item_barcodes')
->result();
$printStatus=$this->input->get('status',true)?:'available';
if(!in_array($printStatus,['available','sold_out','all'],true))$printStatus='available';
$this->db->where('item_id',$item_id);
if($printStatus==='all')$this->db->where_in('status',['available','sold_out']);else$this->db->where('status',$printStatus);
$barcodes=$this->db->order_by('id','ASC')->get('item_barcodes')->result();
if(empty($barcodes)){
show_error('Barcode tidak ditemukan.');
@@ -256,7 +270,8 @@ class Items extends CI_Controller {
$x=$col*$labelWidth;
$y=$line*$labelHeight;
$qrFile=$tempDir.$row->barcode.'.png';
$safeBarcode=preg_replace('/[^A-Za-z0-9_-]/','_',basename((string)$row->barcode));
$qrFile=$tempDir.$safeBarcode.'.png';
if(!file_exists($qrFile)){
QRcode::png(
@@ -315,6 +330,7 @@ class Items extends CI_Controller {
public function save_serial_number()
{
if (strtoupper($this->input->method()) !== 'POST') show_404();
$barcode_id = $this->input->post('barcode_id');
$serial = trim($this->input->post('serial_number'));
@@ -337,13 +353,12 @@ class Items extends CI_Controller {
return;
}
$barcode = $this->db
->where('id',$barcode_id)
->get('item_barcodes')
->row();
$barcode = $this->db->select('item_barcodes.*')->from('item_barcodes')->join('items','items.id=item_barcodes.item_id')->where(array('item_barcodes.id'=>$barcode_id,'items.company_id'=>$this->companyId(),'item_barcodes.status'=>'pending'))->get()->row();
if(!$barcode){echo json_encode(array('status'=>false,'message'=>'Barcode pending tidak ditemukan pada perusahaan aktif.'));return;}
$this->db->where('id',$barcode_id);
$this->db->update('item_barcodes',array(
'serial_number'=>$serial
'serial_number'=>$serial,
'status'=>'pending'
));
// langsung kirim data terbaru
$_GET['item_id']=$barcode->item_id;
@@ -352,6 +367,7 @@ class Items extends CI_Controller {
public function delete_serial_number()
{
if (strtoupper($this->input->method()) !== 'POST') show_404();
$barcode_id = (int)$this->input->post('barcode_id');
if ($barcode_id <= 0) {
@@ -363,10 +379,7 @@ class Items extends CI_Controller {
}
// Ambil data barcode
$barcode = $this->db
->where('id', $barcode_id)
->get('item_barcodes')
->row();
$barcode = $this->db->select('item_barcodes.*')->from('item_barcodes')->join('items','items.id=item_barcodes.item_id')->where(array('item_barcodes.id'=>$barcode_id,'items.company_id'=>$this->companyId(),'item_barcodes.status'=>'pending'))->get()->row();
if (!$barcode) {
echo json_encode([
@@ -376,17 +389,60 @@ class Items extends CI_Controller {
return;
}
if ($barcode->status === 'available' || !empty($barcode->activated_at)) {
echo json_encode(['status'=>false,'message'=>'Serial unit yang sudah diposting tidak dapat dihapus.']);
return;
}
// Hapus Serial Number
$this->db
->where('id', $barcode_id)
->update('item_barcodes', [
'serial_number' => NULL
'serial_number' => NULL,
'status' => 'pending'
]);
$this->db->where('id',$barcode->item_id)->update('items',['status'=>'draft']);
// Kirim ulang data terbaru
$this->load_data_barcode($barcode->item_id);
}
public function post_barcode()
{
return $this->barcodeError('Posting hanya dapat dilakukan dari tabel Barang Pending.');
$barcodeId = (int)$this->input->post('barcode_id');
$salePrice = (float)$this->input->post('sale_price');
$barcode = $this->db->get_where('item_barcodes', ['id'=>$barcodeId])->row();
if (!$barcode) return $this->barcodeError('Barcode tidak ditemukan.');
if ($barcode->status === 'available') return $this->barcodeError('Unit barcode sudah diposting.');
if (trim((string)$barcode->serial_number) === '') return $this->barcodeError('Serial number wajib diisi sebelum unit diposting.');
if ($salePrice <= 0) return $this->barcodeError('Harga jual satuan wajib lebih dari nol.');
$this->db->trans_start();
$this->db->where('id',$barcodeId)->update('item_barcodes',[
'sale_price'=>$salePrice,
'status'=>'available',
'activated_at'=>date('Y-m-d H:i:s'),
'activated_by'=>(int)$this->session->userdata('user_id')
]);
$this->db->where('id',$barcode->item_id)->update('items',[
'harga_jual'=>$salePrice,
'status'=>'active'
]);
$this->db->trans_complete();
if ($this->db->trans_status() === false) return $this->barcodeError('Unit barcode gagal diposting.');
log_activity('items','posting','Posting unit barcode ID '.$barcodeId.' dengan harga jual '.$salePrice,'success');
$this->load_data_barcode($barcode->item_id);
}
private function barcodeError($message)
{
echo json_encode(['status'=>false,'message'=>$message]);
}
public function get_barcode_item()
{
$barcode = trim($this->input->post('barcode'));
@@ -491,6 +547,10 @@ class Items extends CI_Controller {
public function get_data()
{
$status = $this->input->get('s') ?? 'all';
$server = (int)$this->input->get('server') === 1;
$draw = (int)$this->input->get('draw');
$start = max(0,(int)$this->input->get('start'));
$length = min(100,max(10,(int)$this->input->get('length')));
$allowed = ['draft','active','all'];
if (!in_array($status, $allowed)) {
@@ -509,6 +569,13 @@ class Items extends CI_Controller {
items.status,
items.kode_id,
kode.need_serial_number,
COALESCE((SELECT SUM(CASE WHEN ibs.status IN("available","reserved") THEN GREATEST(ibs.qty_sisa-COALESCE(ibs.reserved_qty,0)-COALESCE(ibs.return_reserved_qty,0),0) ELSE 0 END) FROM item_barcodes ibs WHERE ibs.item_id=items.id),items.stok) operational_available,
(SELECT COUNT(*) FROM item_barcodes iba WHERE iba.item_id=items.id AND iba.status IN("available","reserved") AND GREATEST(iba.qty_sisa-COALESCE(iba.reserved_qty,0)-COALESCE(iba.return_reserved_qty,0),0)>0) operational_barcode_count,
COALESCE((SELECT SUM(GREATEST(ibp.qty_sisa-COALESCE(ibp.reserved_qty,0)-COALESCE(ibp.return_reserved_qty,0),0)) FROM item_barcodes ibp WHERE ibp.item_id=items.id AND ibp.status="pending"),items.stok) pending_available,
(SELECT COUNT(*) FROM item_barcodes ibp WHERE ibp.item_id=items.id AND ibp.status="pending" AND GREATEST(ibp.qty_sisa-COALESCE(ibp.reserved_qty,0)-COALESCE(ibp.return_reserved_qty,0),0)>0) pending_barcode_count,
(SELECT COUNT(*) FROM item_barcodes ibc WHERE ibc.item_id=items.id) total_barcodes,
(SELECT COUNT(*) FROM item_barcodes ibc WHERE ibc.item_id=items.id AND NULLIF(ibc.serial_number,"") IS NOT NULL) completed_serials,
(SELECT COUNT(*) FROM item_barcodes ibc WHERE ibc.item_id=items.id AND ibc.status="available") posted_barcodes,
(
SELECT GROUP_CONCAT(DISTINCT w.nama SEPARATOR ", ")
FROM stock_logs sl
@@ -518,21 +585,35 @@ class Items extends CI_Controller {
');
$this->db->join('kode_barang kode', 'items.kode_id = kode.id', 'left');
if ($this->db->field_exists('company_id','items')) {
$this->db->where('items.company_id', (int)$this->session->userdata('company_id'));
}
if ($status !== 'all') {
if ($status === 'draft') {
$this->db->where('EXISTS(SELECT 1 FROM goods_receipt_lines grlp WHERE grlp.item_id=items.id)',null,false)->group_start()->where('items.status','draft')->or_where('EXISTS(SELECT 1 FROM item_barcodes ibp WHERE ibp.item_id=items.id AND ibp.status="pending")',null,false)->group_end();
} elseif ($status !== 'all') {
$this->db->where('items.status', $status);
}
if($server){
$search=$this->input->get('search');
$search=is_array($search)?trim((string)($search['value']??'')):'';
if($search!=='')$this->db->group_start()->like('items.kode_detail',$search)->or_like('items.nama_barang',$search)->group_end();
}
$this->db->from('items');
$filtered=$server?$this->db->count_all_results('',false):0;
// urutan custom
$this->db->order_by('(items.stok = 0)', 'ASC', false);
$this->db->order_by('items.tanggal_pembelian', 'ASC');
$this->db->from('items');
if($server)$this->db->limit($length,$start);
$data = $this->db->get()->result();
$result = [];
$no = 1;
$no = $server?$start+1:1;
foreach($data as $row){
@@ -541,39 +622,11 @@ class Items extends CI_Controller {
$btnDelete = '<button class="btn btn-danger btn-sm btn-delete m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Delete</button>';
$btnInsertSn = '<a href="'.base_url('items/item_barcodes/'.$row->id).'" class="btn btn-secondary btn-sm m-1">Barcode</a>';
$btnPrintBarcode = '<button class="btn btn-secondary btn-sm btn-print m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'"><i class="bi bi-printer"></i> Print Barcode</button>';
$btnPost = '<button class="btn btn-success btn-sm btn-updatePosting m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Posting</button>';
$btnPost = '<button class="btn btn-success btn-sm btn-updatePosting m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'" data-buy="'.htmlspecialchars($row->harga_beli,ENT_QUOTES,'UTF-8').'" data-sale="'.htmlspecialchars($row->harga_jual,ENT_QUOTES,'UTF-8').'">Posting</button>';
if ($row->need_serial_number == 'true') {
$list = $this->db
->where('item_id', $row->id)
->order_by('id', 'ASC')
->get('item_barcodes')
->result();
$total = count($list);
$done = 0;
foreach ($list as $barcode) {
if (!empty($barcode->serial_number)) {
$done++;
}
}
$total_sn = $total;
$done_sn = $done;
if ($row->status == 'draft') {
if ($total_sn == $done_sn) {
$btnSN = $btnInsertSn . $btnPrintBarcode;
$btnPostview = $btnPost;
} else {
$btnSN = $btnInsertSn ;
$btnPostview = '';
}
} else {
$btnSN = $btnPrintBarcode;
}
$btnSN = $btnInsertSn . ((int)$row->posted_barcodes > 0 ? $btnPrintBarcode : '');
$btnPostview = (int)$row->completed_serials > (int)$row->posted_barcodes ? $btnPost : '';
} else {
@@ -581,35 +634,43 @@ class Items extends CI_Controller {
$btnPostview = $btnPost;
}
if($row->status == 'draft'){
$action = $btnDetail . $btnPostview . $btnDelete . $btnSN;
$isAdmin=$this->session->userdata('role')==='Admin';
$canUpdate=$isAdmin||check_permission('items','can_update');
$canPost=$isAdmin||check_permission('items','can_post');
$canExport=$isAdmin||check_permission('items','can_export');
if($status==='active'){
$action = $btnDetail . ($canExport?$btnPrintBarcode:'');
} elseif($status==='draft'){
$action = $btnDetail . ($canPost?$btnPostview:'') . ($canUpdate?$btnSN:'');
} else {
if($this->session->userdata('role') == 'Admin') {
$btnAdjust = '<button class="btn btn-warning btn-sm btn-adjust m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Adjust</button>';
} else {
$btnAdjust = '';
}
$action = $btnDetail . $btnEdit . $btnAdjust . $btnSN;
$action = $btnDetail . ($canExport?$btnPrintBarcode:'');
}
$barcodeCount = $status === 'active' ? (int)$row->operational_barcode_count : (int)$row->pending_barcode_count;
$stockAvailable = $status === 'active' ? (float)$row->operational_available : (float)$row->pending_available;
$barcodeStock = '<span title="Jumlah barcode / jumlah stok"><strong>'.number_format($barcodeCount, 0, ',', '.').'</strong> / '.number_format($stockAvailable, 0, ',', '.').'</span>';
$result[] = [
$no++,
$row->tanggal_pembelian,
$row->kode_detail,
$row->nama_barang,
$row->gudang ?? '-',
$row->stok,
number_format($row->harga_beli ?? 0, 2),
number_format($row->harga_jual ?? 0, 2),
$barcodeStock,
number_format($row->harga_beli ?? 0, 2, ',', '.'),
$status==='draft'?'<input type="text" class="form-control form-control-sm format-rupiah pending-sale-price" data-id="'.(int)$row->id.'" data-buy="'.htmlspecialchars($row->harga_beli,ENT_QUOTES,'UTF-8').'" value="'.($row->harga_jual>0?number_format($row->harga_jual,2,',','.'):'').'" placeholder="Minimal '.number_format($row->harga_beli,2,',','.').'" inputmode="decimal">':number_format($row->harga_jual??0,2,',','.'),
$row->created_at,
$action
];
}
echo json_encode(["data"=>$result]);
$payload=array("data"=>$result);
if($server){$payload['draw']=$draw;$payload['recordsTotal']=$filtered;$payload['recordsFiltered']=$filtered;}
echo json_encode($payload);
}
// =========================
@@ -627,7 +688,10 @@ class Items extends CI_Controller {
// =========================
public function detail($id)
{
$item = $this->db->get_where('items',['id'=>$id])->row();
$item = $this->companyItem($id);
if (!$item) {
return json_response(false, 'Barang tidak ditemukan.', array(), 404);
}
$logs = $this->db
->select('stock_logs.*, warehouses.nama as gudang')
@@ -637,16 +701,26 @@ class Items extends CI_Controller {
->get('stock_logs')
->result();
echo json_encode([
'item'=>$item,
'logs'=>$logs
]);
return json_response(true, 'Detail barang berhasil dimuat.', array(
'item' => $item,
'logs' => $logs
));
}
public function stock_log_pdf($id)
{
$item=$this->companyItem($id);
if(!$item)show_404();
$logs=$this->db->select('sl.*,w.nama gudang')->from('stock_logs sl')->join('warehouses w','w.id=sl.warehouse_id','left')->where('sl.item_id',(int)$id)->order_by('sl.id','ASC')->get()->result();
require_once APPPATH.'third_party/fpdf/fpdf.php';
$pdf=new FPDF('L','mm','A4');$pdf->SetMargins(12,12,12);$pdf->AddPage();$pdf->SetFont('Arial','B',15);$pdf->Cell(0,8,'STOCK LOG BARANG',0,1,'C');$pdf->SetFont('Arial','',9);$pdf->Cell(0,5,$item->kode_detail.' - '.$item->nama_barang,0,1,'C');$pdf->Ln(5);
$widths=[12,34,42,20,20,105,35];foreach(['No','Tanggal','Gudang','Qty','Tipe','Keterangan','Referensi']as$i=>$label){$pdf->SetFont('Arial','B',8);$pdf->Cell($widths[$i],7,$label,1,0,'C');}$pdf->Ln();$balance=0;$pdf->SetFont('Arial','',8);foreach($logs as$i=>$log){$signed=$log->tipe==='masuk'?(float)$log->qty:-(float)$log->qty;$balance+=$signed;$pdf->Cell($widths[0],7,$i+1,1,0,'C');$pdf->Cell($widths[1],7,substr($log->created_at,0,19),1);$pdf->Cell($widths[2],7,substr($log->gudang?:'-',0,24),1);$pdf->Cell($widths[3],7,number_format(abs($log->qty),0,',','.'),1,0,'R');$pdf->Cell($widths[4],7,strtoupper($log->tipe),1,0,'C');$pdf->Cell($widths[5],7,substr($log->keterangan?:'-',0,70),1);$pdf->Cell($widths[6],7,substr(($log->ref_type?:'-').' #'.($log->ref_id?:'-'),0,22),1,1);}$pdf->SetFont('Arial','B',9);$pdf->Cell(108,8,'SALDO STOK',1,0,'R');$pdf->Cell(20,8,number_format($balance,0,',','.'),1,0,'R');$pdf->Cell(140,8,'',1,1);$pdf->Output('I','Stock-Log-'.$item->kode_detail.'.pdf');
}
public function get_accounts()
{
$data = $this->db
->where_in('kode_akun', [101,102,201,122,125,105])
->where_in('kode_akun', ['1010','1020','2010','1220','1250','1050'])
->order_by('kode_akun','ASC')
->get('accounts')
->result();
@@ -670,6 +744,7 @@ class Items extends CI_Controller {
// =========================
public function save()
{
return $this->legacyMutationDisabled('Penambahan stok langsung dinonaktifkan. Gunakan Saldo Awal atau Barang Masuk pada Operasional Stok.');
$nama_barang = $this->input->post('nama_barang');
$qty = (int)$this->input->post('qty');
$harga_beli = (float)$this->input->post('harga_beli');
@@ -682,7 +757,7 @@ class Items extends CI_Controller {
$status = $this->input->post('status');
// ================= VALIDASI =================
if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || !$account_kas){
if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || ($status !== 'draft' && !$account_kas)){
echo json_encode([
'status'=>false,
'message'=>'Data tidak lengkap / tidak valid',
@@ -734,6 +809,16 @@ class Items extends CI_Controller {
$total = $qty * $harga_beli;
// Barang pending hanya master/rencana pembelian. Belum membentuk stok,
// barcode, pergerakan barang, maupun jurnal sampai diterima melalui PO.
if ($status === 'draft') {
$this->db->insert('items',array('stok'=>0,'planned_qty'=>$qty,'planned_warehouse_id'=>$warehouse_id,'planned_tracking_qty'=>$tracking_qty,'kode_detail'=>$kode_detail,'kode_id'=>$kode_id,'nama_barang'=>$nama_barang,'harga_beli'=>$harga_beli,'harga_jual'=>$harga_jual,'tanggal_pembelian'=>$tanggal_beli,'status'=>'draft'));
$itemId=(int)$this->db->insert_id();
log_activity('items','create','Membuat master barang pending: '.$nama_barang,'success');
echo json_encode(array('status'=>true,'message'=>'Barang pending dibuat. Stok baru aktif setelah penerimaan PO.','item_id'=>$itemId));
return;
}
$this->db->trans_start();
// ================= INSERT ITEM =================
@@ -854,6 +939,7 @@ class Items extends CI_Controller {
// =========================
public function update()
{
return $this->legacyMutationDisabled('Barang aktif tidak dapat diubah melalui form lama. Gunakan master Jenis/Kode Barang atau dokumen persediaan yang sesuai.');
$id = $this->input->post('id');
$nama_barang = $this->input->post('nama_barang');
$harga_baru = (float)$this->input->post('harga_beli');
@@ -946,6 +1032,8 @@ class Items extends CI_Controller {
public function delete($id)
{
echo json_encode(array('status'=>false,'message'=>'Barang tidak dapat dihapus. Gunakan status nonaktif pada master bila sudah tidak digunakan.'));
return;
// cek item ada
$item = $this->db->get_where('items',['id'=>$id])->row();
@@ -954,10 +1042,26 @@ class Items extends CI_Controller {
return;
}
// Master/item yang telah menjadi bagian dokumen pembelian tidak boleh
// dihapus. Menghapusnya akan meninggalkan item_id yatim pada baris PO
// dan membuat penerimaan gagal saat menulis stock_logs.
if ($this->db->table_exists('purchase_order_lines')) {
$cekPembelian = $this->db->where('item_id', (int) $id)
->count_all_results('purchase_order_lines');
if ($cekPembelian > 0) {
echo json_encode([
'status'=>false,
'message'=>'Barang tidak dapat dihapus karena sudah digunakan pada Purchase Order. Nonaktifkan barang bila tidak akan digunakan lagi.'
]);
return;
}
}
// cek ada transaksi stock
$cekStock = $this->db->where('item_id',$id)->count_all_results('stock_logs');
if($cekStock > 1){
if($cekStock > 0){
echo json_encode([
'status'=>false,
'message'=>'Tidak bisa dihapus, sudah ada transaksi stok'
@@ -1002,6 +1106,7 @@ class Items extends CI_Controller {
public function posting()
{
if (strtoupper($this->input->method()) !== 'POST') show_404();
$id = $this->input->post('id');
if(!$id){
@@ -1013,7 +1118,7 @@ class Items extends CI_Controller {
}
// cek item ada
$item = $this->db->get_where('items', ['id' => $id])->row();
$item = $this->db->get_where('items', ['id' => $id, 'company_id'=>(int)$this->session->userdata('company_id')])->row();
if(!$item){
echo json_encode([
@@ -1023,8 +1128,21 @@ class Items extends CI_Controller {
return;
}
$catalog = $this->db->get_where('kode_barang',['id'=>$item->kode_id])->row();
$serialRequired=$catalog&&strtolower((string)$catalog->need_serial_number)==='true';
$submittedSalePrice=(float)$this->input->post('harga_jual');
if($submittedSalePrice>0)$item->harga_jual=$submittedSalePrice;
if ((float)$item->harga_jual <= 0) {
echo json_encode(['status'=>false,'message'=>'Harga jual wajib diisi sebelum barang diposting.']);
return;
}
if ((float)$item->harga_jual < (float)$item->harga_beli) {
echo json_encode(['status'=>false,'message'=>'Harga jual tidak boleh lebih rendah dari harga beli Rp '.number_format($item->harga_beli,2,',','.')]);
return;
}
// cegah double posting
if($item->status == 'active'){
if(!$serialRequired&&$item->status == 'active'){
echo json_encode([
'status' => false,
'message' => 'Barang sudah diterima sebelumnya'
@@ -1034,10 +1152,24 @@ class Items extends CI_Controller {
$this->db->trans_start();
// update status saja
if($serialRequired){
$ready=$this->db->where('item_id',$id)->where('status','pending')->where('serial_number IS NOT NULL',null,false)->where('serial_number !=','')->count_all_results('item_barcodes');
if($ready<=0){$this->db->trans_complete();echo json_encode(['status'=>false,'message'=>'Belum ada serial number yang siap diposting.']);return;}
$this->db->where('item_id',$id)->where('status','pending')->where('serial_number IS NOT NULL',null,false)->where('serial_number !=','')->update('item_barcodes',[
'sale_price'=>$item->harga_jual,'status'=>'available','activated_at'=>date('Y-m-d H:i:s'),'activated_by'=>(int)$this->session->userdata('user_id')
]);
$posted=$this->db->affected_rows();
}else{
$this->db->where('item_id',$id)->where('status','pending')->update('item_barcodes',[
'sale_price'=>$item->harga_jual,'status'=>'available','activated_at'=>date('Y-m-d H:i:s'),'activated_by'=>(int)$this->session->userdata('user_id')
]);
$posted=$this->db->affected_rows();
}
$notReady=$this->db->where('item_id',$id)->where('status','pending')->count_all_results('item_barcodes');
$this->db->where('id', $id);
$this->db->update('items', [
'status' => 'active'
'status' => 'active',
'harga_jual' => $item->harga_jual
]);
$this->db->trans_complete();
@@ -1061,7 +1193,9 @@ class Items extends CI_Controller {
echo json_encode([
'status' => true,
'message' => 'Barang sudah diterima dan stok siap digunakan'
'message' => $serialRequired?($posted.' unit berhasil diposting dan '.$notReady.' unit masih belum siap.'):('Barang berhasil diposting. '.$posted.' barcode siap digunakan.'),
'posted_count'=>$posted,
'pending_count'=>$notReady
]);
}
}
@@ -1071,6 +1205,7 @@ class Items extends CI_Controller {
// =========================
public function keluarkan()
{
return $this->legacyMutationDisabled('Pengeluaran langsung dinonaktifkan. Buat dokumen Barang Keluar pada Operasional Stok agar ledger, barcode, dan jurnal tetap konsisten.');
$account_biaya = (int)$this->input->post('account_biaya');
$tanggal_keluar = $this->input->post('tanggal_keluar');
$keterangan_keluar = $this->input->post('keterangan_keluar');
@@ -1307,6 +1442,7 @@ class Items extends CI_Controller {
public function adjust()
{
return $this->legacyMutationDisabled('Adjustment langsung dinonaktifkan. Gunakan dokumen Penyesuaian dengan workflow approval pada Operasional Stok.');
$item_id = $this->input->post('item_id');
$qty = (int)$this->input->post('qty');
$warehouse_id = $this->input->post('warehouse_id');
@@ -1412,15 +1548,16 @@ class Items extends CI_Controller {
items.harga_jual,
items.kode_detail,
COALESCE(SUM(
stock_logs.qty * IF(stock_logs.tipe="masuk",1,-1)
inventory_ledger.qty * IF(inventory_ledger.direction="in",1,-1)
),0) as stok
');
$this->db->where('status', 'active');
$this->db->where('items.status', 'active');
$this->db->where('items.company_id', $this->companyId());
$this->db->from('items');
$this->db->join('stock_logs',
'stock_logs.item_id = items.id
AND stock_logs.warehouse_id = '.$this->db->escape($warehouse_id),
$this->db->join('inventory_ledger',
'inventory_ledger.item_id = items.id
AND inventory_ledger.warehouse_id = '.$this->db->escape($warehouse_id),
'left');
$this->db->group_by('items.id');
@@ -1436,6 +1573,7 @@ class Items extends CI_Controller {
public function update_barcode_item()
{
return $this->legacyMutationDisabled('Pembuatan barcode massal tanpa dokumen sumber dinonaktifkan. Barcode dibuat dari penerimaan atau dokumen persediaan.');
$items = $this->db
->select("
i.id,
@@ -1574,4 +1712,9 @@ class Items extends CI_Controller {
'message' => 'Barcode berhasil diperbarui.'
]);
}
}
private function legacyMutationDisabled($message)
{
return $this->output->set_status_header(422)->set_content_type('application/json')->set_output(json_encode(array('status'=>false,'message'=>$message)));
}
}