|
Server : Apache/2.4.18 (Ubuntu) System : Linux canvaswebdesign 3.13.0-71-generic #114-Ubuntu SMP Tue Dec 1 02:34:22 UTC 2015 x86_64 User : oppastar ( 1041) PHP Version : 7.0.33-0ubuntu0.16.04.15 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, Directory : /var/www/laciasmara.com/public_html/shop/application/models/ |
Upload File : |
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Stocks_m extends CI_Model
{
private $warehouse_id = 1;
public function __construct()
{
parent::__construct();
$this->load->database();
}
/**
* Bulk update stock berdasarkan array SKU dan stock
*
* @param array $stocks_data Array berisi data SKU dan stock baru
* @return array Result dengan status dan detail update
*/
public function bulk_update_stock($stocks_data)
{
$result = [
'success' => true,
'total_processed' => count($stocks_data),
'successful_updates' => 0,
'failed_updates' => 0,
'details' => []
];
// Start transaction
$this->db->trans_start();
foreach ($stocks_data as $stock_item) {
$sku = trim($stock_item['sku']);
$new_stock = intval($stock_item['stock']);
try {
// 1. Ambil id dan product_id dari product_details berdasarkan SKU
$product_detail = $this->get_product_detail_by_sku($sku);
if (!$product_detail) {
$result['failed_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'failed',
'message' => 'SKU not found in product_details'
];
continue;
}
$id_product_detail = $product_detail['id'];
$id_product = $product_detail['product_id'];
// 2. Ambil data stock saat ini
$current_stock_data = $this->get_current_stock($id_product, $id_product_detail);
if (!$current_stock_data) {
// Jika belum ada record stock, buat baru
$stock_id = $this->create_stock_record($id_product, $id_product_detail, $new_stock);
if ($stock_id) {
// Log stock movement untuk stock baru
$remark = $this->generate_remark('BULK_INIT', $sku, ['new_stock' => $new_stock]);
$this->log_stock_movement($stock_id, '+', $new_stock, $new_stock, $remark);
$result['successful_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'success',
'message' => 'New stock record created',
'old_stock' => 0,
'new_stock' => $new_stock,
'change' => $new_stock
];
} else {
$result['failed_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'failed',
'message' => 'Failed to create stock record'
];
}
continue;
}
$stock_id = $current_stock_data['id'];
$current_stock = intval($current_stock_data['stock']);
// Skip jika stock sama
if ($current_stock == $new_stock) {
$result['successful_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'success',
'message' => 'Stock unchanged',
'old_stock' => $current_stock,
'new_stock' => $new_stock,
'change' => 0
];
continue;
}
// 3. Update stock
$update_success = $this->update_stock($stock_id, $new_stock);
if (!$update_success) {
$result['failed_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'failed',
'message' => 'Failed to update stock'
];
continue;
}
// 4. Hitung perubahan dan tentukan type
$stock_change = abs($new_stock - $current_stock);
$movement_type = ($new_stock > $current_stock) ? '+' : '-';
// 5. Log stock movement
$remark = $this->generate_remark('BULK_UPDATE', $sku, [
'old_stock' => $current_stock,
'new_stock' => $new_stock,
'type' => $movement_type,
'change' => $stock_change
]);
$movement_success = $this->log_stock_movement(
$stock_id,
$movement_type,
$stock_change,
$new_stock,
$remark
);
if ($movement_success) {
$result['successful_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'success',
'message' => 'Stock updated successfully',
'old_stock' => $current_stock,
'new_stock' => $new_stock,
'change' => ($movement_type === '+' ? $stock_change : -$stock_change)
];
} else {
$result['failed_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'failed',
'message' => 'Stock updated but failed to log movement'
];
}
} catch (Exception $e) {
$result['failed_updates']++;
$result['details'][] = [
'sku' => $sku,
'status' => 'failed',
'message' => 'Exception: ' . $e->getMessage()
];
log_message('error', "Stock update error for SKU {$sku}: " . $e->getMessage());
}
}
// Complete transaction
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
$result['success'] = false;
log_message('error', 'Database transaction failed during bulk stock update');
}
// Set overall success based on whether any updates failed
if ($result['failed_updates'] > 0) {
$result['success'] = false;
}
return $result;
}
/**
* Ambil detail produk berdasarkan SKU
*/
private function get_product_detail_by_sku($sku)
{
$this->db->select('id, product_id, sku');
$this->db->where('sku', $sku);
$query = $this->db->get('product_details');
return $query->row_array();
}
/**
* Ambil data stock saat ini
*/
private function get_current_stock($id_product, $id_product_detail)
{
$this->db->select('id, stock');
$this->db->where('id_product', $id_product);
$this->db->where('id_product_detail', $id_product_detail);
$this->db->where('warehouse_id', $this->warehouse_id);
$query = $this->db->get('stock');
return $query->row_array();
}
/**
* Buat record stock baru
*/
private function create_stock_record($id_product, $id_product_detail, $stock)
{
$data = [
'id_product' => $id_product,
'id_product_detail' => $id_product_detail,
'warehouse_id' => $this->warehouse_id,
'stock' => $stock,
'stock_keep' => 0,
'stock_reject' => 0,
'stock_sample' => 0
];
if ($this->db->insert('stock', $data)) {
return $this->db->insert_id();
}
return false;
}
/**
* Update stock
*/
private function update_stock($stock_id, $new_stock)
{
$this->db->where('id', $stock_id);
return $this->db->update('stock', ['stock' => $new_stock]);
}
/**
* Log stock movement
*/
public function log_stock_movement($stock_id, $type, $stock_change, $total_stock, $remark, $user_name = 'System')
{
$data = [
'stock_id' => $stock_id,
'type' => $type,
'stock_change' => $stock_change,
'stock_type' => 'stock',
'remark' => $remark,
'total' => $total_stock,
'name' => $user_name,
'datetime' => date('Y-m-d H:i:s')
];
return $this->db->insert('stock_movement', $data);
}
/**
* Get stock data dengan SKU untuk manual adjustment
*/
public function get_stock_with_sku($product_detail_id, $warehouse_id)
{
$this->db->select('s.id as stock_id, s.id_product, s.stock, s.stock_keep, s.stock_reject, s.stock_sample, pd.sku');
$this->db->from('stock s');
$this->db->join('product_details pd', 'pd.id = s.id_product_detail');
$this->db->where('s.id_product_detail', $product_detail_id);
$this->db->where('s.warehouse_id', $warehouse_id);
$query = $this->db->get();
return $query->row_array();
}
/**
* Update stock berdasarkan tipe stock tertentu
*/
public function update_stock_by_type($stock_id, $stock_type, $new_value)
{
// Validasi stock_type yang diizinkan
$allowed_types = ['stock', 'stock_keep', 'stock_reject', 'stock_sample'];
if (!in_array($stock_type, $allowed_types)) {
return false;
}
$update_data = [$stock_type => $new_value];
$this->db->where('id', $stock_id);
return $this->db->update('stock', $update_data);
}
/**
* Log stock movement dengan dukungan berbagai tipe stock dan tabel movement
*/
public function log_stock_movement_by_type($stock_id, $type, $stock_change, $total_stock, $remark, $stock_type = 'stock', $user_name = 'System')
{
// Tentukan tabel movement berdasarkan stock_type
$movement_tables = [
'stock' => 'stock_movement',
'stock_keep' => 'stock_movement_keep',
'stock_reject' => 'stock_movement_reject',
'stock_sample' => 'stock_movement_sample'
];
$movement_table = isset($movement_tables[$stock_type]) ? $movement_tables[$stock_type] : 'stock_movement';
$data = [
'stock_id' => $stock_id,
'type' => $type,
'stock_change' => $stock_change,
'stock_type' => $stock_type,
'remark' => $remark,
'total' => $total_stock,
'name' => $user_name,
'datetime' => date('Y-m-d H:i:s')
];
return $this->db->insert($movement_table, $data);
}
/**
* Update generate_remark method untuk mendukung lebih banyak scenario
*/
public function generate_remark($action, $sku, $details = [])
{
$timestamp = date('Y-m-d H:i:s');
switch ($action) {
case 'BULK_INIT':
return "STOCK INITIALIZATION - SKU: {$sku} | Initial stock set to {$details['new_stock']} units | Created by Admin IT via Bulk Stock Update API | {$timestamp}";
case 'BULK_UPDATE':
return "STOCK ADJUSTMENT - SKU: {$sku} | Previous: {$details['old_stock']} units | New: {$details['new_stock']} units | Change: {$details['type']}{$details['change']} units | Reason: Bulk stock update via API | Updated by Admin IT | {$timestamp}";
case 'SALES_ORDER':
return "STOCK REDUCTION - SKU: {$sku} | Sold: {$details['qty']} units | Order: {$details['order_no']} | Customer: {$details['customer']} | Remaining: {$details['remaining']} units | {$timestamp}";
case 'PURCHASE':
return "STOCK ADDITION - SKU: {$sku} | Received: {$details['qty']} units | PO: {$details['po_no']} | Supplier: {$details['supplier']} | Total: {$details['total']} units | {$timestamp}";
case 'MANUAL_ADJUSTMENT':
$stock_type_label = $this->get_stock_type_description($details['stock_type']);
return "MANUAL ADJUSTMENT - SKU: {$sku} | Stock Type: {$stock_type_label} | Previous: {$details['old_value']} units | New: {$details['total']} units | Change: {$details['type']}{$details['change']} units | Adjusted by: {$details['user']} | Reason: {$details['reason']} | {$timestamp}";
case 'TRANSFER_OUT':
return "STOCK TRANSFER OUT - SKU: {$sku} | Transferred: {$details['qty']} units | To Warehouse: {$details['to_warehouse']} | Transfer ID: {$details['transfer_id']} | Remaining: {$details['remaining']} units | {$timestamp}";
case 'TRANSFER_IN':
return "STOCK TRANSFER IN - SKU: {$sku} | Received: {$details['qty']} units | From Warehouse: {$details['from_warehouse']} | Transfer ID: {$details['transfer_id']} | Total: {$details['total']} units | {$timestamp}";
case 'ORDER_CANCELLED':
return "ORDER CANCELLATION - SKU: {$sku} | Returned: {$details['qty']} units | Order: {$details['order_no']} | Customer: {$details['customer']} | Stock restored to: {$details['total']} units | {$timestamp}";
default:
return "STOCK MOVEMENT - SKU: {$sku} | Action: {$action} | Details: " . json_encode($details) . " | {$timestamp}";
}
}
/**
* Helper method untuk deskripsi stock type
*/
private function get_stock_type_description($stock_type)
{
$descriptions = [
'stock' => 'Regular Stock',
'stock_keep' => 'Keep Stock',
'stock_reject' => 'Reject Stock',
'stock_sample' => 'Sample Stock'
];
return isset($descriptions[$stock_type]) ? $descriptions[$stock_type] : 'Unknown Stock Type';
}
/**
* Get stock movement history dengan pagination dan filter
*/
public function get_stock_movement_history($filters = [], $limit = 50, $offset = 0)
{
$this->db->select('sm.*, pd.sku, p.product_name');
$this->db->from('stock_movement sm');
$this->db->join('stock s', 's.id = sm.stock_id');
$this->db->join('product_details pd', 'pd.id = s.id_product_detail');
$this->db->join('products p', 'p.id = s.id_product');
// Apply filters
if (!empty($filters['sku'])) {
$this->db->where('pd.sku', $filters['sku']);
}
if (!empty($filters['stock_type'])) {
$this->db->where('sm.stock_type', $filters['stock_type']);
}
if (!empty($filters['warehouse_id'])) {
$this->db->where('s.warehouse_id', $filters['warehouse_id']);
}
if (!empty($filters['date_from'])) {
$this->db->where('DATE(sm.datetime) >=', $filters['date_from']);
}
if (!empty($filters['date_to'])) {
$this->db->where('DATE(sm.datetime) <=', $filters['date_to']);
}
$this->db->order_by('sm.datetime', 'DESC');
$this->db->limit($limit, $offset);
$query = $this->db->get();
return $query->result_array();
}
}