|
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/controllers/admin/ |
Upload File : |
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Orders extends Admin_Controller
{
function __construct()
{
parent::__construct();
$this->load->model('order_m');
$this->load->model('order_detail_m');
$this->load->model('configuration_m');
$this->load->model('customer_m');
$this->load->model('product_m');
$this->load->model('stocks_m');
$this->load->model('voucher_m');
$this->load->model('log_m');
$this->load->helper('rajaongkir');
$this->load->helper('visits');
}
public function index()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Daftar Pesanan | Laciasmara';
$data['customers'] = $this->customer_m->fetch_all_customers();
$data['products'] = $this->product_m->all_products();
$data['shippingMethods'] = $this->product_m->get_all_shipping_methods();
$data['paymentMethods'] = $this->product_m->get_all_payment_methods();
$data['vouchers'] = $this->voucher_m->fetch_all_vouchers();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/manage_order');
$this->load->view('admin_new/layouts/footer');
}
public function add_order()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Tambah Pesanan | Laciasmara';
$data['customers'] = $this->customer_m->fetch_all_customers();
$data['products'] = $this->product_m->all_products();
$data['shippingMethods'] = $this->product_m->get_all_shipping_methods();
$data['paymentMethods'] = $this->product_m->get_all_payment_methods();
$data['vouchers'] = $this->voucher_m->fetch_all_vouchers();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/add_order');
$this->load->view('admin_new/layouts/footer');
}
public function add_retailer_order()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Tambah Pesanan Retailer | Laciasmara';
$data['retailers'] = $this->customer_m->fetch_all_retailers();
$data['products'] = $this->product_m->all_products();
$data['shippingMethods'] = $this->product_m->get_all_shipping_methods();
$data['paymentMethods'] = $this->product_m->get_all_payment_methods();
$data['vouchers'] = $this->voucher_m->fetch_all_vouchers();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/retailer/add_order');
$this->load->view('admin_new/layouts/footer');
}
public function manage()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Daftar Pesanan | Laciasmara';
$data['customers'] = $this->customer_m->fetch_all_customers();
$data['products'] = $this->product_m->all_products();
$data['shippingMethods'] = $this->product_m->get_all_shipping_methods();
$data['paymentMethods'] = $this->product_m->get_all_payment_methods();
$data['vouchers'] = $this->voucher_m->fetch_all_vouchers();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/manage_order');
$this->load->view('admin_new/layouts/footer');
}
public function manage_retailer()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Daftar Pesanan Retailer | Laciasmara';
$data['customers'] = $this->customer_m->fetch_all_retailers();
$data['products'] = $this->product_m->all_products();
$data['shippingMethods'] = $this->product_m->get_all_shipping_methods();
$data['paymentMethods'] = $this->product_m->get_all_payment_methods();
$data['vouchers'] = $this->voucher_m->fetch_all_vouchers();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/retailer/manage_order');
$this->load->view('admin_new/layouts/footer');
}
public function detail($id_order = NULL)
{
if (!$id_order) {
redirect('admin/orders/manage-order');
}
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Detail Pesanan | Laciasmara';
// Fetch data order
$data['order'] = $this->order_m->fetch_order_by_id($id_order);
if (!$data['order']) {
$this->session->set_flashdata('message', 'Order ID tidak ditemukan.');
$this->session->set_flashdata('message_type', 'error');
redirect('admin/orders/manage-order');
}
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/detail_order');
$this->load->view('admin_new/layouts/footer');
}
public function create_order()
{
if ($this->input->server('REQUEST_METHOD') !== 'POST') {
show_error('Method Not Allowed', 405);
}
try {
$postData = $this->input->post();
$pdf_filename = $this->handle_pdf_upload();
// Add PDF filename to postData if uploaded successfully
if ($pdf_filename) {
$postData['marketplace_label_pdf'] = $pdf_filename;
}
// Validate and prepare order data
$orderData = $this->prepare_order_data($postData);
$orderDetails = $this->validate_order_details($postData);
// Start database transaction
$this->db->trans_start();
// Create main order
$orderId = $this->create_main_order($orderData);
// Process order details with stock management
$process_result = $this->process_order_details_with_stock($orderId, $orderDetails, $postData);
// Update customer first order count
$this->update_customer_first_order($postData['customer']);
// Complete transaction
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw new Exception('Transaction failed');
}
// Create order log and notification AFTER successful completion
$this->create_order_completion_log($orderId, $orderData, $process_result);
// Redirect to success page
$this->session->set_flashdata('success_message', 'Order berhasil dibuat dengan ID: ' . $orderId);
redirect('admin/orders/manage-order');
} catch (Exception $e) {
$this->db->trans_rollback();
log_message('error', 'Create Order Failed: ' . $e->getMessage());
$this->session->set_flashdata('error_message', 'Gagal membuat order: ' . $e->getMessage());
redirect('admin/orders/add-order');
}
}
/**
* Handle PDF file upload
*/
private function handle_pdf_upload()
{
// Check if file was uploaded
if (empty($_FILES['pdf_label']['name'])) {
return null;
}
// Configure upload settings
$upload_path = FCPATH . './uploads/marketplace-label/';
// Create directory if it doesn't exist
if (!is_dir($upload_path)) {
mkdir($upload_path, 0755, true);
}
$config = array(
'upload_path' => $upload_path,
'allowed_types' => 'pdf',
'max_size' => 10240, // 10MB in KB
'encrypt_name' => TRUE,
'remove_spaces' => TRUE
);
$this->load->library('upload', $config);
if (!$this->upload->do_upload('pdf_label')) {
$error = $this->upload->display_errors('', '');
throw new Exception('Upload failed: ' . $error);
}
$upload_data = $this->upload->data();
return $upload_data['file_name'];
}
/**
* Remove uploaded file if transaction fails
*/
private function remove_uploaded_file($filename)
{
if ($filename && file_exists('./uploads/marketplace-label/' . $filename)) {
unlink('./uploads/marketplace-label/' . $filename);
}
}
/**
* Download marketplace label PDF
*/
public function download_marketplace_label($order_id)
{
$order = $this->db->get_where('orders', ['id_orders' => $order_id])->row();
if (!$order) {
log_message('error', 'Download PDF Label: Order not found - ID: ' . $order_id);
show_404('Order not found');
}
if (empty($order->marketplace_label_pdf)) {
log_message('warning', 'Download PDF Label: No PDF file for order - ID: ' . $order_id);
$this->session->set_flashdata('error_message', 'Tidak ada file PDF label untuk order ini');
redirect('admin/orders/detail/' . $order_id);
return;
}
$file_path = FCPATH . 'uploads/marketplace-label/' . $order->marketplace_label_pdf;
if (!file_exists($file_path)) {
log_message('error', 'Download PDF Label: File not found - ' . $file_path . ' for order ID: ' . $order_id);
$this->session->set_flashdata('error_message', 'File PDF tidak ditemukan');
redirect('admin/orders/detail/' . $order_id);
return;
}
log_message('info', 'Download PDF Label: File downloaded - Order ID: ' . $order_id . ', File: ' . $order->marketplace_label_pdf);
$this->load->helper('download');
$download_name = 'label_' . $order->tokopedia_invoice . '.pdf';
force_download($download_name, file_get_contents($file_path));
}
private function prepare_order_data($postData)
{
$userdata = $this->session->userdata();
// Get customer data for additional processing
$customer = $this->get_customer_data($postData['customer']);
return [
'customer_id' => (!empty($postData['is_marketplace']) && $postData['is_marketplace'] == 1) ? 2615 : $postData['customer'],
'payment_status' => 1,
'grand_total_amount' => (int) $postData['total_price'],
'total_amount' => (int) $postData['subtotal_price'],
'payment_type' => $this->determine_payment_type($postData['paymentMethod']),
'recipient_name' => $postData['name'],
'address' => $this->format_address($postData),
'district' => $postData['shipping_district'] ?? '',
'subdistrict' => $postData['shipping_subdistrict'] ?? '',
'province' => $postData['shipping_province'] ?? '',
'phone' => $postData['shipping_phone'] ?? '',
'admin_note' => $postData['admin_note'] ?? '',
'customer_invoice_note' => $postData['customer_invoice_note'] ?? '',
'marketplace_label_pdf' => $postData['marketplace_label_pdf'] ?? '',
'email' => $postData['email'],
'country' => 'Indonesia',
'redeemed_voucher_code' => $postData['redeemed_voucher_code'] ?? null,
'redeemed_voucher_type' => $postData['redeemed_voucher_type'] ?? null,
'redeemed_voucher_value' => (int) ($postData['redeemed_voucher_value'] ?? 0),
'redeemed_voucher_amount' => (int) ($postData['redeemed_voucher_amount'] ?? 0),
'shipping_fee' => (int) ($postData['shipping_fee'] ?? 0),
'created_by' => $userdata['name'],
'source' => empty($postData['tokopedia_invoice']) ? 'www.laciasmara.com' : 'Tokopedia',
'tokopedia_invoice' => $postData['tokopedia_invoice'] ?? '',
'first' => $customer ? $customer->is_first + 1 : 1,
'source' => isset($postData['source']) ? $postData['source'] : null,
'distribution_type' => isset($postData['distribution_type']) ? $postData['distribution_type'] : null,
'distribution_department' => isset($postData['distribution_department']) ? $postData['distribution_department'] : null,
'distribution_purpose' => isset($postData['source']) ? $postData['source'] : null,
];
}
/**
* Validate and decode order details JSON
*/
private function validate_order_details($postData)
{
if (empty($postData['selected_products_data'])) {
throw new Exception('Order details data is missing');
}
$orderDetails = json_decode($postData['selected_products_data'], true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Invalid order details JSON format');
}
if (!is_array($orderDetails) || empty($orderDetails)) {
throw new Exception('Order details must be a non-empty array');
}
return $orderDetails;
}
/**
* Get customer data
*/
private function get_customer_data($customer_id)
{
return $this->db->get_where('customers', ['id_customers' => $customer_id])->row();
}
/**
* Determine payment type based on method ID
*/
private function determine_payment_type($payment_method)
{
switch ($payment_method) {
case 1:
return 'bank transfer BCA';
case 2:
return 'bank transfer MANDIRI';
case 3:
return 'Free';
default:
return 'not yet';
}
}
/**
* Format address with notes
*/
private function format_address($postData)
{
if (empty($postData['address'])) {
return null;
}
$address = $postData['address'];
if (!empty($postData['address_notes'])) {
$address .= ' (' . $postData['address_notes'] . ')';
}
return $address;
}
/**
* Determine warehouse ID based on customer
*/
private function determine_warehouse_id($customer, $postData = [])
{
if (!empty($postData['warehouse_id']) && is_numeric($postData['warehouse_id'])) {
return (int) $postData['warehouse_id'];
}
if (!$customer) {
return 1;
}
if ($customer->id_customers == 2615) {
return 13;
}
if ($customer->reseller_id == 8) {
return 12;
}
if ($customer->reseller_id == 7) {
return 14;
}
return 1;
}
/**
* Create main order record
*/
private function create_main_order($orderData)
{
$this->db->insert('orders', $orderData);
if ($this->db->affected_rows() === 0) {
throw new Exception('Failed to create main order');
}
return $this->db->insert_id();
}
/**
* Process order details with stock management
*/
private function process_order_details_with_stock($orderId, $orderDetails, $postData)
{
$customer = $this->get_customer_data($postData['customer']);
$warehouse_id = $this->determine_warehouse_id($customer, $postData);
// First, check stock availability and store initial stock data
$stock_validation = $this->validate_stock_availability($orderDetails, $warehouse_id);
if (!$stock_validation['success']) {
throw new Exception('Insufficient stock for some items: ' . implode(', ', $stock_validation['insufficient_items']));
}
$success_count = 0;
$processed_items = [];
// Process each order detail
foreach ($orderDetails as $item) {
$result = $this->process_single_order_item($orderId, $item, $warehouse_id, $postData, $stock_validation['initial_stock_data']);
if ($result['success']) {
$success_count++;
$processed_items[] = $result['item_data'];
} else {
throw new Exception('Failed to process item: ' . $item['product'] . ' - ' . $result['error']);
}
}
return [
'success_count' => $success_count,
'total_items' => count($orderDetails),
'processed_items' => $processed_items
];
}
/**
* Validate stock availability for all items
*/
private function validate_stock_availability($orderDetails, $warehouse_id)
{
$initial_stock_data = [];
$insufficient_items = [];
foreach ($orderDetails as $item) {
$stock_data = $this->get_current_stock_data($item['id_products'], $item['id_detail'], $warehouse_id);
if (!$stock_data) {
$insufficient_items[] = $item['product'] . ' (Stock data not found)';
continue;
}
$initial_stock_data[$item['id_detail']] = $stock_data;
if ($stock_data['stock'] < $item['qty']) {
$insufficient_items[] = $item['product'] . ' (Available: ' . $stock_data['stock'] . ', Required: ' . $item['qty'] . ')';
}
}
return [
'success' => empty($insufficient_items),
'insufficient_items' => $insufficient_items,
'initial_stock_data' => $initial_stock_data
];
}
/**
* Get current stock data for a product
*/
private function get_current_stock_data($product_id, $detail_id, $warehouse_id)
{
$this->db->select('id, stock');
$this->db->where('id_product', $product_id);
$this->db->where('id_product_detail', $detail_id);
$this->db->where('warehouse_id', $warehouse_id);
$query = $this->db->get('stock');
return $query->num_rows() > 0 ? $query->row_array() : null;
}
/**
* Process single order item with stock update and logging
*/
private function process_single_order_item($orderId, $item, $warehouse_id, $postData, $initial_stock_data)
{
try {
// Prepare order detail data
$orderDetailData = [
'orders_id' => $orderId,
'item_id' => $item['id_detail'],
'product_id' => $item['id_products'],
'item_name' => $item['product'],
'item_price' => (int) $item['price'],
'quantity' => (int) $item['qty'],
'subtotal' => (int) $item['subtotal'],
'sku' => $item['sku'],
'attributes' => $item['atribut'],
'warehouse_id' => $warehouse_id,
'chosen_shipping_id' => $postData['shippingMethod'],
'shipping_fee' => (int) ($postData['shipping_fee'] ?? 0)
];
// Insert order detail
if (!$this->db->insert('orders_detail', $orderDetailData)) {
throw new Exception('Failed to insert order detail');
}
// Update stock and create movement log
$stock_result = $this->update_stock_with_logging($orderId, $item, $warehouse_id, $initial_stock_data);
if (!$stock_result['success']) {
throw new Exception($stock_result['error']);
}
return [
'success' => true,
'item_data' => array_merge($orderDetailData, $stock_result['stock_info'])
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Update stock with proper logging using stocks_m model
*/
private function update_stock_with_logging($orderId, $item, $warehouse_id, $initial_stock_data)
{
$quantity_sold = (int) $item['qty'];
// Get current stock data
$stock_data = $this->get_current_stock_data($item['id_products'], $item['id_detail'], $warehouse_id);
if (!$stock_data) {
return ['success' => false, 'error' => 'Stock data not found'];
}
$stock_id = $stock_data['id'];
$old_stock = $initial_stock_data[$item['id_detail']]['stock'];
// Update stock
$this->db->set('stock', 'stock - ' . $quantity_sold, false);
$this->db->where('id', $stock_id);
if (!$this->db->update('stock')) {
return ['success' => false, 'error' => 'Failed to update stock'];
}
// Get new stock amount
$updated_stock = $this->get_stock_by_id($stock_id);
$new_stock = $updated_stock['stock'];
// Generate remark using stocks_m model
$customer_name = $this->get_customer_name_by_order($orderId);
$remark = $this->stocks_m->generate_remark('SALES_ORDER', $item['sku'], [
'qty' => $quantity_sold,
'order_no' => $orderId,
'customer' => $customer_name,
'remaining' => $new_stock
]);
// Log stock movement using stocks_m model
$movement_success = $this->stocks_m->log_stock_movement(
$stock_id,
'-',
$quantity_sold,
$new_stock,
$remark
);
if (!$movement_success) {
return ['success' => false, 'error' => 'Failed to log stock movement'];
}
// Log stock update (existing log system)
$this->log_m->log_stock_update(
$orderId,
$item['id_detail'],
$item['product'],
$old_stock,
$new_stock,
base_url('admin/products/stock-product?tab=all'),
null,
'stock',
'orders'
);
return [
'success' => true,
'stock_info' => [
'old_stock' => $old_stock,
'new_stock' => $new_stock,
'stock_change' => $quantity_sold
]
];
}
/**
* Get stock data by ID
*/
private function get_stock_by_id($stock_id)
{
$this->db->select('stock');
$this->db->where('id', $stock_id);
return $this->db->get('stock')->row_array();
}
/**
* Get customer name by order ID
*/
private function get_customer_name_by_order($order_id)
{
$this->db->select('recipient_name');
$this->db->where('id_orders', $order_id);
$query = $this->db->get('orders');
if ($query->num_rows() > 0) {
return $query->row()->recipient_name;
}
return 'Unknown Customer';
}
/**
* Update customer first order count
*/
private function update_customer_first_order($customer_id)
{
$this->db->set('is_first', 'is_first + 1', FALSE);
$this->db->where('id_customers', $customer_id);
if (!$this->db->update('customers')) {
throw new Exception('Failed to update customer first order count');
}
}
/**
* Create order completion log and notification
*/
private function create_order_completion_log($orderId, $orderData, $process_result)
{
try {
$userdata = $this->session->userdata();
$admin_name = $userdata['name'] ?? 'System';
$recipient = $orderData['recipient_name'];
$grand_total = number_format($orderData['grand_total_amount'], 0, ',', '.');
$description = "Pesanan Baru dari {$recipient} telah ditambahkan dengan Order ID {$orderId} senilai Rp {$grand_total}. Pesanan ini dibuat oleh {$admin_name} melalui Admin Panel.";
$reference_url = base_url('admin/orders/manage-order?tab=pending');
// Create log and send notification
$log_id = $this->log_m->log_order_create($orderId, $description, $reference_url);
$this->log_m->send_order_notifications('CREATE_ORDER', $log_id, $description);
} catch (Exception $e) {
log_message('error', "Failed to create order completion log for Order ID {$orderId}: " . $e->getMessage());
}
}
// Fetch Orders Data
public function get_orders()
{
// Get filter parameters
$tab = $this->input->get('tab', true);
$sort = $this->input->get('sort', true);
$marketplace = $this->input->get('marketplace', true);
$department = $this->input->get('department', true);
$dateFilter = $this->input->get('date_filter', true);
$startDate = $this->input->get('start_date', true);
$endDate = $this->input->get('end_date', true);
$isBCATransfer = filter_var($this->input->get('isBCATransfer', true), FILTER_VALIDATE_BOOLEAN);
$isMandiriTransfer = filter_var($this->input->get('isMandiriTransfer', true), FILTER_VALIDATE_BOOLEAN);
$isPaypal = filter_var($this->input->get('isPaypal', true), FILTER_VALIDATE_BOOLEAN);
$isDoku = filter_var($this->input->get('isDoku', true), FILTER_VALIDATE_BOOLEAN);
$usingInsurance = filter_var($this->input->get('usingInsurance', true), FILTER_VALIDATE_BOOLEAN);
$notUsingInsurance = filter_var($this->input->get('notUsingInsurance', true), FILTER_VALIDATE_BOOLEAN);
$useShippingFee = filter_var($this->input->get('usingShippingFee', true), FILTER_VALIDATE_BOOLEAN);
$notUseShippingFee = filter_var($this->input->get('notUsingShippingFee', true), FILTER_VALIDATE_BOOLEAN);
$isRegularShipping = filter_var($this->input->get('isRegularShipping', true), FILTER_VALIDATE_BOOLEAN);
$isTwoHourShipping = filter_var($this->input->get('isTwoHourShipping', true), FILTER_VALIDATE_BOOLEAN);
$isOneDayShipping = filter_var($this->input->get('isOneDayShipping', true), FILTER_VALIDATE_BOOLEAN);
$isNextDayShipping = filter_var($this->input->get('isNextDayShipping', true), FILTER_VALIDATE_BOOLEAN);
$page = (int) ($this->input->get('page', true) ?? 1);
$limit = (int) ($this->input->get('limit', true) ?? 10);
$offset = ($page - 1) * $limit;
$searchTerm = $this->input->get('search', true);
// Build main query
$this->db->select('
o.id_orders,
o.customer_id,
o.order_date,
o.payment_status,
o.payment_confirm,
o.grand_total_amount,
o.payment_type,
o.recipient_name,
o.address,
o.district,
o.subdistrict,
o.province,
o.phone,
o.email,
o.redeemed_voucher_code,
o.redeemed_voucher_type,
o.redeemed_voucher_value,
o.redeemed_voucher_amount,
o.shipping_fee,
o.customer_note,
o.admin_note,
o.created_by,
o.admin_note,
o.first,
o.gift_receiver_name,
o.gift_receiver_phone,
o.insurance_status,
o.insurance_cost,
o.source,
o.medium,
o.campaign,
o.tokopedia_invoice,
o.no_resi,
o.referral,
o.distribution_department,
o.distribution_purpose
');
$this->db->from('orders o');
$this->db->join('customers c', 'o.customer_id = c.id_customers', 'left');
$this->db->join('orders_detail od', 'o.id_orders = od.orders_id', 'left');
$this->db->where("(c.reseller_id IS NULL OR c.reseller_id = '')", NULL, FALSE);
// Apply filter based on tab
if ($tab) {
switch ($tab) {
case 'pending':
$this->db->where_in('o.payment_status', [0, 1, 3]);
break;
case 'cancelled':
$this->db->where('o.payment_status', 2);
break;
case 'processing':
$this->db->where('o.payment_status', 4);
$this->db->group_start();
$this->db->where('o.no_resi', NULL);
$this->db->or_where('o.no_resi', '');
$this->db->group_end();
break;
case 'shipping': // Tab baru untuk pesanan sedang dikirim
$this->db->where('o.payment_status', 4);
$this->db->where('o.no_resi !=', NULL);
$this->db->where('o.no_resi !=', '');
break;
case 'done':
$this->db->where_in('o.payment_status', [5, 8]);
break;
}
}
// Array untuk menyimpan kondisi pembayaran yang valid
$paymentTypes = [];
if ($isBCATransfer) {
$paymentTypes[] = 'bank transfer BCA';
}
if ($isMandiriTransfer) {
$paymentTypes[] = 'bank transfer MANDIRI';
}
if ($isPaypal) {
$paymentTypes[] = 'Paypal';
}
if ($isDoku) {
$paymentTypes[] = 'DOKU';
}
// Tambahkan filter shipping method di sini
$shippingMethods = [];
if ($isRegularShipping) {
$shippingMethods[] = 3; // Regular shipping
}
if ($isTwoHourShipping) {
$shippingMethods[] = 2; // Two hour shipping
}
if ($isOneDayShipping) {
$shippingMethods[] = 1; // One day shipping
}
if ($isNextDayShipping) {
$shippingMethods[] = 4; // Next day shipping
}
// Jika ada filter shipping method, tambahkan where clause
if (!empty($shippingMethods)) {
$this->db->join('orders_detail od', 'o.id_orders = od.orders_id', 'inner');
$this->db->where_in('od.chosen_shipping_id', $shippingMethods);
$this->db->group_by('o.id_orders'); // Penting untuk menghindari duplikasi orders
}
// Jika ada tipe pembayaran yang valid, terapkan filter
if (!empty($paymentTypes)) {
$this->db->where_in('o.payment_type', $paymentTypes);
}
// Filter berdasarkan status shipping fee
if ($useShippingFee && $notUseShippingFee) {
$this->db->group_start();
$this->db->or_where('o.shipping_fee >', 0);
$this->db->or_where('o.shipping_fee', 0);
$this->db->group_end();
} elseif ($useShippingFee) {
$this->db->where('o.shipping_fee >', 0);
} elseif ($notUseShippingFee) {
$this->db->where('o.shipping_fee', 0);
}
// Filter berdasarkan status asuransi
if ($usingInsurance && $notUsingInsurance) {
$this->db->group_start();
$this->db->or_where('o.insurance_status', 'Yes');
$this->db->or_where('o.insurance_status', 'No');
$this->db->group_end();
} elseif ($usingInsurance) {
$this->db->where('o.insurance_status', 'Yes');
} elseif ($notUsingInsurance) {
$this->db->where('o.insurance_status', 'No');
}
if ($marketplace === 'tokopedia') {
$this->db->where('o.tokopedia_invoice !=', '');
}
if ($marketplace === 'website') {
$this->db->group_start(); // mulai grup kondisi untuk invoice kosong/null
$this->db->where('o.tokopedia_invoice', '');
$this->db->or_where('o.tokopedia_invoice IS NULL');
$this->db->group_end();
// exclude reseller
$this->db->group_start(); // mulai grup kondisi untuk non-reseller
$this->db->where('c.reseller_id', NULL);
$this->db->or_where('c.reseller_id', '');
$this->db->group_end();
}
if ($department === 'marketing') {
$this->db->where('o.distribution_department', 'marketing');
}
if ($dateFilter) {
switch ($dateFilter) {
case 'today':
$this->db->where('DATE(o.order_date) = CURDATE()');
break;
case 'yesterday':
$this->db->where('DATE(o.order_date) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)');
break;
case 'last7days':
$this->db->where('o.order_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)');
break;
case 'last30days':
$this->db->where('o.order_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)');
break;
case 'thisMonth':
$this->db->where('MONTH(o.order_date) = MONTH(CURDATE()) AND YEAR(o.order_date) = YEAR(CURDATE())');
break;
case 'thisYear':
$this->db->where('YEAR(o.order_date) = YEAR(CURDATE())');
break;
case 'custom':
if ($startDate && $endDate) {
$startDate = date('Y-m-d', strtotime($startDate));
$endDate = date('Y-m-d', strtotime($endDate));
if ($startDate && $endDate) {
$this->db->where('DATE(o.order_date) >=', $startDate);
$this->db->where('DATE(o.order_date) <=', $endDate);
}
}
break;
}
}
if (!empty($searchTerm)) {
$this->db->group_start();
$this->db->like('o.id_orders', $searchTerm);
$this->db->or_like('o.tokopedia_invoice', $searchTerm);
$this->db->or_like('o.recipient_name', $searchTerm);
$this->db->or_like('o.email', $searchTerm);
$this->db->or_like('o.phone', $searchTerm);
$this->db->or_like('o.redeemed_voucher_code', $searchTerm);
$this->db->or_like('o.payment_type', $searchTerm);
$this->db->or_like('o.address', $searchTerm);
$this->db->or_like('o.province', $searchTerm);
$this->db->or_like('o.district', $searchTerm);
$this->db->or_like('o.subdistrict', $searchTerm);
$this->db->or_like('od.item_name', $searchTerm);
$this->db->group_end();
}
// Apply sorting
if ($sort) {
switch ($sort) {
case 'paling_baru':
$this->db->order_by('o.order_date', 'DESC');
break;
case 'paling_lama':
$this->db->order_by('o.order_date', 'ASC');
break;
default:
// Default sort by newest
$this->db->order_by('o.order_date', 'DESC');
}
} else {
// Default sort by newest
$this->db->order_by('o.order_date', 'DESC');
}
$this->db->limit($limit, $offset);
$this->db->group_by('o.id_orders');
// Execute query
$query = $this->db->get();
$all_orders = $query->result();
// Post-processing: Get order details for each order
foreach ($all_orders as $order) {
$order->is_referral = false;
$order->referred_by = '';
// Check if referral exists in orders table
if (!empty($order->referral)) {
// Cek apakah referral cocok dengan affiliator berdasarkan referral_code (case insensitive)
$this->db->select('c.name AS nama');
$this->db->from('affiliators a');
$this->db->join('customers c', 'a.customer_id = c.id_customers', 'inner');
$this->db->where('LOWER(a.referral_code)', strtolower($order->referral));
$referral_query = $this->db->get();
if ($referral_query->num_rows() > 0) {
$affiliator = $referral_query->row();
$order->is_referral = true;
$order->referred_by = $affiliator->nama;
}
}
// Jika tidak ditemukan lewat referral, cek redeemed_voucher_code
if (empty($order->is_referral) && !empty($order->redeemed_voucher_code)) {
$this->db->select('c.name AS nama');
$this->db->from('affiliators a');
$this->db->join('customers c', 'a.customer_id = c.id_customers', 'inner');
$this->db->where('LOWER(a.referral_code)', strtolower($order->redeemed_voucher_code));
$voucher_referral_query = $this->db->get();
if ($voucher_referral_query->num_rows() > 0) {
$affiliator = $voucher_referral_query->row();
$order->is_referral = true;
$order->referred_by = $affiliator->nama;
}
}
// Get order details
$this->db->select("
od.id_orders_detail,
od.item_id,
od.product_id,
od.item_name,
od.item_price,
od.quantity,
od.subtotal,
od.sku,
od.attributes,
od.chosen_shipping_id,
CONCAT(sm.shipper, ' - ', sm.name) as shipping_method_name,
pi.image as product_image
");
$this->db->from('orders_detail od');
$this->db->join('shipment_method sm', 'od.chosen_shipping_id = sm.id', 'left');
$this->db->join('product_images pi', 'od.item_id = pi.product_details_id AND pi.priority = 1 AND pi.status = 1', 'left');
$this->db->where('od.orders_id', $order->id_orders);
$details_query = $this->db->get();
$order->items = $details_query->result();
// Calculate total items
$order->total_items = 0;
foreach ($order->items as $item) {
$order->total_items += $item->quantity;
}
// Format some data if needed
$order->order_date_formatted = date('d M Y H:i', strtotime($order->order_date));
// Set payment status text
switch ($order->payment_status) {
case 0:
$order->payment_status_text = 'Pending';
$order->payment_status_class = 'bg-yellow-100 text-yellow-700 px-2 py-1 rounded';
break;
case 1:
if ($order->payment_confirm == 1) {
$order->payment_status_text = 'Menunggu Verifikasi Pembayaran';
$order->payment_status_class = 'bg-blue-100 text-blue-700 px-2 py-1 rounded';
} else {
$order->payment_status_text = 'Menunggu Pembayaran';
$order->payment_status_class = 'bg-orange-100 text-orange-700 px-2 py-1 rounded';
}
break;
case 2:
$order->payment_status_text = 'Dibatalin';
$order->payment_status_class = 'bg-red-100 text-red-700 px-2 py-1 rounded';
break;
case 3:
$order->payment_status_text = 'Sudah Dibayar';
$order->payment_status_class = 'bg-green-100 text-green-700 px-2 py-1 rounded';
break;
case 4:
if (!empty($order->no_resi)) {
$order->payment_status_text = 'Sedang Dikirim';
$order->payment_status_class = 'bg-blue-100 text-blue-700 px-2 py-1 rounded';
} else {
$order->payment_status_text = 'Diproses';
$order->payment_status_class = 'bg-blue-100 text-blue-700 px-2 py-1 rounded';
}
break;
case 5:
$order->payment_status_text = 'Selesai';
$order->payment_status_class = 'bg-gray-100 text-gray-700 px-2 py-1 rounded';
break;
case 8:
$order->payment_status_text = 'Selesai (Retailer)';
$order->payment_status_class = 'bg-purple-100 text-purple-700 px-2 py-1 rounded';
break;
default:
$order->payment_status_text = 'Unknown';
$order->payment_status_class = 'bg-gray-300 text-gray-800 px-2 py-1 rounded';
}
}
// Return JSON response
echo json_encode($all_orders);
}
public function get_marketing_orders()
{
$tab = $this->input->get('tab', true);
$sort = $this->input->get('sort', true);
$distributionType = $this->input->get('distribution_type', true);
$dateFilter = $this->input->get('date_filter', true);
$startDate = $this->input->get('start_date', true);
$endDate = $this->input->get('end_date', true);
$page = (int) ($this->input->get('page', true) ?? 1);
$limit = (int) ($this->input->get('limit', true) ?? 10);
$offset = ($page - 1) * $limit;
$searchTerm = $this->input->get('search', true);
// Build main query (sebelumnya sudah benar)
$this->db->select('
o.id_orders,
o.customer_id,
o.order_date,
o.distribution_type,
o.distribution_purpose,
o.created_by,
o.updated_by,
o.recipient_name,
o.address,
o.province,
o.district,
o.subdistrict,
o.phone,
o.email,
o.payment_status,
o.no_resi,
c.name as customer_name,
c.email as customer_email,
c.shipping_phone as customer_phone
');
$this->db->from('orders o');
$this->db->join('customers c', 'o.customer_id = c.id_customers', 'left');
$this->db->join('orders_detail od', 'o.id_orders = od.orders_id', 'left');
// Filter for marketing distribution
$this->db->group_start();
$this->db->where('LOWER(o.distribution_department)', 'marketing');
$this->db->or_where('LOWER(o.distribution_department)', 'Marketing');
$this->db->group_end();
// Tab filter
if ($tab) {
switch ($tab) {
case 'processing':
$this->db->where('o.payment_status', 4);
$this->db->group_start();
$this->db->where('o.no_resi', NULL);
$this->db->or_where('o.no_resi', '');
$this->db->group_end();
break;
case 'shipping':
$this->db->where('o.payment_status', 4);
$this->db->where('o.no_resi !=', NULL);
$this->db->where('o.no_resi !=', '');
break;
case 'delivered':
$this->db->where('o.payment_status', 5);
break;
case 'cancelled':
$this->db->where('o.payment_status', 2);
break;
}
}
// Date filter (sama seperti get_orders)
if ($dateFilter) {
switch ($dateFilter) {
case 'today':
$this->db->where('DATE(o.order_date) = CURDATE()');
break;
case 'yesterday':
$this->db->where('DATE(o.order_date) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)');
break;
case 'last7days':
$this->db->where('o.order_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)');
break;
case 'last30days':
$this->db->where('o.order_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)');
break;
case 'thisMonth':
$this->db->where('MONTH(o.order_date) = MONTH(CURDATE()) AND YEAR(o.order_date) = YEAR(CURDATE())');
break;
case 'thisYear':
$this->db->where('YEAR(o.order_date) = YEAR(CURDATE())');
break;
case 'custom':
if ($startDate && $endDate) {
$startDate = date('Y-m-d', strtotime($startDate));
$endDate = date('Y-m-d', strtotime($endDate));
if ($startDate && $endDate) {
$this->db->where('DATE(o.order_date) >=', $startDate);
$this->db->where('DATE(o.order_date) <=', $endDate);
}
}
break;
}
}
// Search functionality
if (!empty($searchTerm)) {
$this->db->group_start();
$this->db->like('o.distribution_purpose', $searchTerm);
$this->db->or_like('o.created_by', $searchTerm);
$this->db->or_like('o.updated_by', $searchTerm);
$this->db->or_like('od.item_name', $searchTerm);
$this->db->group_end();
}
// Apply sorting
if ($sort) {
switch ($sort) {
case 'paling_baru':
$this->db->order_by('o.order_date', 'DESC');
break;
case 'paling_lama':
$this->db->order_by('o.order_date', 'ASC');
break;
default:
$this->db->order_by('o.order_date', 'DESC');
}
} else {
$this->db->order_by('o.order_date', 'DESC');
}
$this->db->limit($limit, $offset);
// Execute query
$query = $this->db->get();
$marketing_orders = $query->result();
// Post-processing for each order
foreach ($marketing_orders as $order) {
// Get order details
$this->db->select('
od.id_orders_detail,
od.item_id,
od.product_id,
od.item_name,
od.item_price,
od.quantity,
od.subtotal,
od.sku,
od.attributes,
od.chosen_shipping_id,
sm.name as shipping_method_name,
pi.image as product_image
');
$this->db->from('orders_detail od');
$this->db->join('shipment_method sm', 'od.chosen_shipping_id = sm.id', 'left');
$this->db->join('product_images pi', 'od.item_id = pi.product_details_id AND pi.priority = 1 AND pi.status = 1', 'left');
$this->db->where('od.orders_id', $order->id_orders);
$details_query = $this->db->get();
$order->items = $details_query->result();
// Calculate total items
$order->total_items = 0;
foreach ($order->items as $item) {
$order->total_items += $item->quantity;
}
// Format order date
$order->order_date_formatted = date('d M Y H:i', strtotime($order->order_date));
}
// Return JSON response
echo json_encode($marketing_orders);
}
// Fetch Retailer Orders Data
public function get_retailer_orders()
{
// Get filter parameters
$tab = $this->input->get('tab', true);
$sort = $this->input->get('sort', true);
$marketplace = $this->input->get('marketplace', true);
$dateFilter = $this->input->get('date_filter', true);
$startDate = $this->input->get('start_date', true);
$endDate = $this->input->get('end_date', true);
$isBCATransfer = filter_var($this->input->get('isBCATransfer', true), FILTER_VALIDATE_BOOLEAN);
$isMandiriTransfer = filter_var($this->input->get('isMandiriTransfer', true), FILTER_VALIDATE_BOOLEAN);
$isPaypal = filter_var($this->input->get('isPaypal', true), FILTER_VALIDATE_BOOLEAN);
$isDoku = filter_var($this->input->get('isDoku', true), FILTER_VALIDATE_BOOLEAN);
$usingInsurance = filter_var($this->input->get('usingInsurance', true), FILTER_VALIDATE_BOOLEAN);
$notUsingInsurance = filter_var($this->input->get('notUsingInsurance', true), FILTER_VALIDATE_BOOLEAN);
$isRegularShipping = filter_var($this->input->get('isRegularShipping', true), FILTER_VALIDATE_BOOLEAN);
$isTwoHourShipping = filter_var($this->input->get('isTwoHourShipping', true), FILTER_VALIDATE_BOOLEAN);
$isOneDayShipping = filter_var($this->input->get('isOneDayShipping', true), FILTER_VALIDATE_BOOLEAN);
$isNextDayShipping = filter_var($this->input->get('isNextDayShipping', true), FILTER_VALIDATE_BOOLEAN);
$page = (int) ($this->input->get('page', true) ?? 1);
$limit = (int) ($this->input->get('limit', true) ?? 10);
$offset = ($page - 1) * $limit;
$searchTerm = $this->input->get('search', true);
$retailerIds = $this->input->get('retailerId', true);
// Build main query
$this->db->select('
o.id_orders,
o.customer_id,
o.order_date,
o.payment_status,
o.payment_confirm,
o.grand_total_amount,
o.payment_type,
o.recipient_name,
o.address,
o.district,
o.subdistrict,
o.province,
o.phone,
o.email,
o.redeemed_voucher_code,
o.redeemed_voucher_type,
o.redeemed_voucher_value,
o.redeemed_voucher_amount,
o.shipping_fee,
o.customer_note,
o.admin_note,
o.created_by,
o.admin_note,
o.first,
o.gift_receiver_name,
o.gift_receiver_phone,
o.insurance_status,
o.insurance_cost,
o.source,
o.medium,
o.campaign,
o.tokopedia_invoice,
o.no_resi,
c.reseller_id
');
$this->db->from('orders o');
$this->db->join('customers c', 'o.customer_id = c.id_customers', 'inner');
$this->db->join('orders_detail od', 'o.id_orders = od.orders_id', 'left');
// Hanya ambil order dengan customer yang memiliki reseller_id tidak null
$this->db->where('c.reseller_id IS NOT NULL');
$this->db->where("c.reseller_id != ''");
// Filter berdasarkan reseller_id spesifik jika diberikan
if ($retailerIds) {
$retailerIdArray = explode(',', $retailerIds);
if (!empty($retailerIdArray)) {
$this->db->where_in('c.id_customers', $retailerIdArray);
}
}
// Apply filter based on tab
if ($tab) {
switch ($tab) {
case 'pending':
$this->db->where_in('o.payment_status', [0, 1, 3]);
break;
case 'cancelled':
$this->db->where('o.payment_status', 2);
break;
case 'processing':
$this->db->where('o.payment_status', 4);
$this->db->group_start();
$this->db->where('o.no_resi', NULL);
$this->db->or_where('o.no_resi', '');
$this->db->group_end();
break;
case 'shipping': // Tab baru untuk pesanan sedang dikirim
$this->db->where('o.payment_status', 4);
$this->db->where('o.no_resi !=', NULL);
$this->db->where('o.no_resi !=', '');
break;
case 'done':
$this->db->where_in('o.payment_status', [5, 8]);
break;
}
}
// Array untuk menyimpan kondisi pembayaran yang valid
$paymentTypes = [];
if ($isBCATransfer) {
$paymentTypes[] = 'bank transfer BCA';
}
if ($isMandiriTransfer) {
$paymentTypes[] = 'bank transfer MANDIRI';
}
if ($isPaypal) {
$paymentTypes[] = 'Paypal';
}
if ($isDoku) {
$paymentTypes[] = 'DOKU';
}
// Tambahkan filter shipping method di sini
$shippingMethods = [];
if ($isRegularShipping) {
$shippingMethods[] = 3; // Regular shipping
}
if ($isTwoHourShipping) {
$shippingMethods[] = 2; // Two hour shipping
}
if ($isOneDayShipping) {
$shippingMethods[] = 1; // One day shipping
}
if ($isNextDayShipping) {
$shippingMethods[] = 4; // Next day shipping
}
// Jika ada filter shipping method, tambahkan where clause
if (!empty($shippingMethods)) {
$this->db->join('orders_detail od', 'o.id_orders = od.orders_id', 'inner');
$this->db->where_in('od.chosen_shipping_id', $shippingMethods);
$this->db->group_by('o.id_orders'); // Penting untuk menghindari duplikasi orders
}
// Jika ada tipe pembayaran yang valid, terapkan filter
if (!empty($paymentTypes)) {
$this->db->where_in('o.payment_type', $paymentTypes);
}
// Filter berdasarkan status asuransi
if ($usingInsurance && $notUsingInsurance) {
$this->db->group_start();
$this->db->or_where('o.insurance_status', 'Yes');
$this->db->or_where('o.insurance_status', 'No');
$this->db->group_end();
} elseif ($usingInsurance) {
$this->db->where('o.insurance_status', 'Yes');
} elseif ($notUsingInsurance) {
$this->db->where('o.insurance_status', 'No');
}
if ($marketplace === 'tokopedia') {
$this->db->where('o.tokopedia_invoice !=', '');
}
if ($dateFilter) {
switch ($dateFilter) {
case 'today':
$this->db->where('DATE(o.order_date) = CURDATE()');
break;
case 'yesterday':
$this->db->where('DATE(o.order_date) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)');
break;
case 'last7days':
$this->db->where('o.order_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)');
break;
case 'last30days':
$this->db->where('o.order_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)');
break;
case 'thisMonth':
$this->db->where('MONTH(o.order_date) = MONTH(CURDATE()) AND YEAR(o.order_date) = YEAR(CURDATE())');
break;
case 'thisYear':
$this->db->where('YEAR(o.order_date) = YEAR(CURDATE())');
break;
case 'custom':
if ($startDate && $endDate) {
$startDate = date('Y-m-d', strtotime($startDate));
$endDate = date('Y-m-d', strtotime($endDate));
if ($startDate && $endDate) {
$this->db->where('DATE(o.order_date) >=', $startDate);
$this->db->where('DATE(o.order_date) <=', $endDate);
}
}
break;
}
}
if (!empty($searchTerm)) {
$this->db->group_start();
$this->db->like('o.id_orders', $searchTerm);
$this->db->or_like('o.tokopedia_invoice', $searchTerm);
$this->db->or_like('o.recipient_name', $searchTerm);
$this->db->or_like('o.email', $searchTerm);
$this->db->or_like('o.redeemed_voucher_code', $searchTerm);
$this->db->or_like('o.payment_type', $searchTerm);
$this->db->or_like('o.address', $searchTerm);
$this->db->or_like('o.province', $searchTerm);
$this->db->or_like('o.district', $searchTerm);
$this->db->or_like('o.subdistrict', $searchTerm);
$this->db->or_like('c.reseller_id', $searchTerm);
$this->db->or_like('od.item_name', $searchTerm);
$this->db->group_end();
}
// Apply sorting
if ($sort) {
switch ($sort) {
case 'paling_baru':
$this->db->order_by('o.order_date', 'DESC');
break;
case 'paling_lama':
$this->db->order_by('o.order_date', 'ASC');
break;
default:
// Default sort by newest
$this->db->order_by('o.order_date', 'DESC');
}
} else {
// Default sort by newest
$this->db->order_by('o.order_date', 'DESC');
}
$this->db->limit($limit, $offset);
$this->db->group_by('o.id_orders');
// Execute query
$query = $this->db->get();
$all_orders = $query->result();
// Post-processing: Get order details for each order
foreach ($all_orders as $order) {
// Get order details
$this->db->select('
od.id_orders_detail,
od.item_id,
od.product_id,
od.item_name,
od.item_price,
od.quantity,
od.subtotal,
od.sku,
od.attributes,
od.chosen_shipping_id,
sm.name as shipping_method_name,
pi.image as product_image
');
$this->db->from('orders_detail od');
$this->db->join('shipment_method sm', 'od.chosen_shipping_id = sm.id', 'left');
$this->db->join('product_images pi', 'od.item_id = pi.product_details_id AND pi.priority = 1 AND pi.status = 1', 'left');
$this->db->where('od.orders_id', $order->id_orders);
$details_query = $this->db->get();
$order->items = $details_query->result();
// Calculate total items
$order->total_items = 0;
foreach ($order->items as $item) {
$order->total_items += $item->quantity;
}
// Format some data if needed
$order->order_date_formatted = date('d M Y H:i', strtotime($order->order_date));
// Set payment status text
switch ($order->payment_status) {
case 0:
$order->payment_status_text = 'Pending';
$order->payment_status_class = 'bg-yellow-100 text-yellow-700 px-2 py-1 rounded';
break;
case 1:
$order->payment_status_text = 'Menunggu Pembayaran';
$order->payment_status_class = 'bg-orange-100 text-orange-700 px-2 py-1 rounded';
break;
case 2:
$order->payment_status_text = 'Dibatalin';
$order->payment_status_class = 'bg-red-100 text-red-700 px-2 py-1 rounded';
break;
case 3:
$order->payment_status_text = 'Sudah Dibayar';
$order->payment_status_class = 'bg-green-100 text-green-700 px-2 py-1 rounded';
break;
case 4:
if (!empty($order->no_resi)) {
$order->payment_status_text = 'Sedang Dikirim';
$order->payment_status_class = 'bg-blue-100 text-blue-700 px-2 py-1 rounded';
} else {
$order->payment_status_text = 'Diproses';
$order->payment_status_class = 'bg-blue-100 text-blue-700 px-2 py-1 rounded';
}
break;
case 5:
$order->payment_status_text = 'Selesai';
$order->payment_status_class = 'bg-gray-100 text-gray-700 px-2 py-1 rounded';
break;
case 8:
$order->payment_status_text = 'Selesai (Retailer)';
$order->payment_status_class = 'bg-purple-100 text-purple-700 px-2 py-1 rounded';
break;
default:
$order->payment_status_text = 'Unknown';
$order->payment_status_class = 'bg-gray-300 text-gray-800 px-2 py-1 rounded';
}
// Dapatkan informasi reseller
if ($order->reseller_id) {
$this->db->select('reseller_name, minimum_order');
$this->db->from('resellers');
$this->db->where('id_resellers', $order->reseller_id);
$reseller_query = $this->db->get();
$order->reseller_info = $reseller_query->row();
}
}
// Return JSON response
echo json_encode($all_orders);
}
public function get_products()
{
$products = $this->product_m->all_products();
echo json_encode($products);
}
public function get_tokopedia_products()
{
$products = $this->product_m->all_tokopedia_products();
echo json_encode($products);
}
public function get_products_by_retailer()
{
$retailer_id = $this->input->get('retailer_id');
if (!$retailer_id) {
echo json_encode(['error' => 'Retailer ID diperlukan']);
return;
}
$products = $this->product_m->get_products_by_retailer($retailer_id);
echo json_encode($products);
}
public function updateNotes()
{
if (!$this->input->is_ajax_request()) {
show_error('No direct script access allowed', 403);
return;
}
$order_id = $this->input->post('order_id', true);
$notes = $this->input->post('notes', true);
$this->db->trans_begin();
try {
// Ambil payment_type dan no_resi sebelum update
$this->db->select('admin_note');
$this->db->where('id_orders', $order_id);
$query = $this->db->get('orders');
if ($query->num_rows() > 0) {
$order_data = $query->row();
$old_notes = $order_data->admin_note;
} else {
throw new Exception('Gagal mengambil data pesanan sebelum update');
}
// Update table orders
$orders_data = [
'admin_note' => $notes,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $this->session->userdata('name')
];
$this->db->where('id_orders', $order_id);
$update_orders = $this->db->update('orders', $orders_data);
if (!$update_orders) {
throw new Exception('Gagal mengupdate notes di tabel orders');
}
$description_notes = "Catatan pada Pesanan {$order_id} telah diubah oleh {$this->session->userdata('name')}.";
$this->log_m->log_order_update(
$order_id,
$description_notes,
'admin_note',
$old_notes,
$notes
);
// Commit transaksi jika semua berhasil
$this->db->trans_commit();
$response = [
'success' => true,
'message' => 'Catatan berhasil diperbarui',
'csrf_hash' => $this->security->get_csrf_hash()
];
} catch (Exception $e) {
// Rollback transaksi jika ada error
$this->db->trans_rollback();
$response = [
'success' => false,
'message' => $e->getMessage(),
'csrf_hash' => $this->security->get_csrf_hash()
];
}
// Kirim response dalam format JSON
echo json_encode($response);
}
public function updateInvoiceNotes()
{
if (!$this->input->is_ajax_request()) {
show_error('No direct script access allowed', 403);
return;
}
$order_id = $this->input->post('order_id', true);
$notes = $this->input->post('notes', true);
$this->db->trans_begin();
try {
// Ambil payment_type dan no_resi sebelum update
$this->db->select('customer_invoice_note');
$this->db->where('id_orders', $order_id);
$query = $this->db->get('orders');
if ($query->num_rows() > 0) {
$order_data = $query->row();
$old_notes = $order_data->customer_invoice_note;
} else {
throw new Exception('Gagal mengambil data pesanan sebelum update');
}
// Update table orders
$orders_data = [
'customer_invoice_note' => $notes,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $this->session->userdata('name')
];
$this->db->where('id_orders', $order_id);
$update_orders = $this->db->update('orders', $orders_data);
if (!$update_orders) {
throw new Exception('Gagal mengupdate notes di tabel orders');
}
$description_notes = "Catatan Invoice pada Pesanan {$order_id} telah diubah oleh {$this->session->userdata('name')}.";
$this->log_m->log_order_update(
$order_id,
$description_notes,
'admin_note',
$old_notes,
$notes
);
// Commit transaksi jika semua berhasil
$this->db->trans_commit();
$response = [
'success' => true,
'message' => 'Catatan Invoice berhasil diperbarui',
'csrf_hash' => $this->security->get_csrf_hash()
];
} catch (Exception $e) {
// Rollback transaksi jika ada error
$this->db->trans_rollback();
$response = [
'success' => false,
'message' => $e->getMessage(),
'csrf_hash' => $this->security->get_csrf_hash()
];
}
// Kirim response dalam format JSON
echo json_encode($response);
}
public function updateResi()
{
if (!$this->input->is_ajax_request()) {
show_error('No direct script access allowed', 403);
return;
}
// Ambil data dari POST
$order_id = $this->input->post('order_id', true);
$no_resi = $this->input->post('no_resi', true);
// Validasi input
if (empty($order_id) || empty($no_resi) || $no_resi === '-') {
$response = [
'success' => false,
'message' => 'Data tidak lengkap',
'csrf_hash' => $this->security->get_csrf_hash()
];
echo json_encode($response);
return;
}
// Mulai transaksi database
$this->db->trans_begin();
try {
// Ambil payment_type dan no_resi sebelum update
$this->db->select('payment_status, no_resi, shipping_fee, customer_id');
$this->db->where('id_orders', $order_id);
$query = $this->db->get('orders');
$order_data = $query->row();
if ($query->num_rows() > 0) {
$old_status = $order_data->payment_status; // Simpan payment_type sebagai old_status
$old_resi = $order_data->no_resi; // Simpan no_resi sebelum update
} else {
throw new Exception('Gagal mengambil data pesanan sebelum update');
}
// Update table orders
$orders_data = [
'no_resi' => $no_resi,
'payment_status' => 4, // Set status menjadi 'Diproses'
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $this->session->userdata('name')
];
$this->db->where('id_orders', $order_id);
$update_orders = $this->db->update('orders', $orders_data);
if (!$update_orders) {
throw new Exception('Gagal mengupdate nomor resi di tabel orders');
}
// Update semua orders_detail yang terkait
$orders_detail_data = [
'no_resi' => $no_resi,
'status' => 2, // Dikirim ,
'shipping_date' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $this->session->userdata('name')
];
$this->db->where('orders_id', $order_id);
$update_details = $this->db->update('orders_detail', $orders_detail_data);
if (!$update_details) {
throw new Exception('Gagal mengupdate nomor resi di tabel orders_detail');
}
// Submit ke shipping table
$this->db->where('order_id', $order_id);
$shipping_exists = $this->db->get('shipping')->num_rows();
if ($shipping_exists > 0) {
// Update data yang sudah ada
$shipping_data = [
'tracking_number' => $no_resi,
'updated_at' => date('Y-m-d H:i:s')
];
$this->db->where('order_id', $order_id);
$update_shipping = $this->db->update('shipping', $shipping_data);
} else {
// Insert data baru
$shipping_data = [
'order_id' => $order_id,
'warehouse_id' => 1,
'shipping_fee' => $order_data->shipping_fee,
'tracking_number' => $no_resi,
'created_at' => date('Y-m-d H:i:s')
];
$update_shipping = $this->db->insert('shipping', $shipping_data);
}
if (!$update_shipping) {
throw new Exception('Gagal mengupdate nomor resi di tabel shipping');
}
$new_status = 4;
$description_payment = "Status Pesanan dengan Order ID {$order_id} diubah menjadi 'Sedang Dikirim' oleh {$this->session->userdata('name')}.";
$this->log_m->log_order_update(
$order_id,
$description_payment,
'payment_status',
$old_status,
$new_status
);
$description_resi = "No. Resi pada Pesanan dengan Order ID {$order_id} telah diupdate dari {$old_resi} menjadi {$no_resi} oleh {$this->session->userdata('name')}.";
$this->log_m->log_order_update(
$order_id,
$description_resi,
'no_resi',
$old_resi,
$no_resi,
base_url('admin/orders/manage-order?tab=shipping')
);
// $email_result = $this->sendShippingNotificationEmail($order_id, $no_resi, $customer_id);
// Commit transaksi jika semua berhasil
$this->db->trans_commit();
$response = [
'success' => true,
'message' => 'Nomor resi berhasil diperbarui.',
'csrf_hash' => $this->security->get_csrf_hash()
];
} catch (Exception $e) {
// Rollback transaksi jika ada error
$this->db->trans_rollback();
$response = [
'success' => false,
'message' => $e->getMessage(),
'csrf_hash' => $this->security->get_csrf_hash()
];
}
// Kirim response dalam format JSON
echo json_encode($response);
}
public function updateOrderStatus()
{
// if (!$this->input->is_ajax_request()) {
// show_error('No direct script access allowed', 403);
// return;
// }
$order_id = $this->input->post('order_id', true);
$status = $this->input->post('status', true);
// Validasi input
if (empty($order_id) || empty($status) || $status === '-') {
$response = [
'success' => false,
'message' => 'Data tidak lengkap',
'csrf_hash' => $this->security->get_csrf_hash()
];
echo json_encode($response);
return;
}
$this->db->trans_begin();
try {
$this->db->select('
payment_status,
customer_id,
total_amount,
referral,
redeemed_voucher_code,
grand_total_amount,
tracking_visit_id,
tracking_link_id,
tracking_affiliator_id
');
$this->db->where('id_orders', $order_id);
$query = $this->db->get('orders');
if ($query->num_rows() === 0) {
throw new Exception('Gagal mengambil data pesanan sebelum update');
}
$order_data = $query->row();
$customer_id = (int) $order_data->customer_id;
$old_status = $order_data->payment_status;
$name = $this->session->userdata('name');
// Persiapan data dasar untuk update
$orders_data = [
'payment_status' => $status,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $this->session->userdata('name')
];
switch ($status) {
case '1': // NOT PAID
$orders_data['payment_date'] = date('Y-m-d');
break;
case '2': // CANCEL
$orders_data['cancel_date'] = date('Y-m-d');
// Handle pengurangan komisi
$this->handleCancelCommission($order_id, $order_data);
// Return stock ke inventory
$this->order_m->returnStockToInventory($order_id, $customer_id);
// Return customer point reward
$this->order_m->returnCustomerPointReward($order_id, $customer_id);
break;
case '3': // PAID
break;
case '4': // PROCESS
$description = "Status pesanan dengan No. {$order_id} diubah menjadi 'Diproses'.";
$reference_url = base_url('admin/orders/manage-order?tab=processing');
$log_id = $this->log_m->log_order_update($order_id, 'payment_status', $old_status, $status, $description, $reference_url);
$this->log_m->send_order_notifications('UPDATE_ORDER', $log_id, $description, 'payment_status', $status);
$this->handleAffiliatorCommission($order_id, $order_data);
// Update customer point rewards
$this->updateCustomerPointRewards($order_id, $customer_id);
break;
case '5': // DELIVERED
break;
}
// Update tabel orders
$this->db->where('id_orders', $order_id);
$update_orders = $this->db->update('orders', $orders_data);
if (!$update_orders) {
throw new Exception('Gagal mengupdate status pesanan');
}
// Kirim email berdasarkan status
$this->sendEmailNotification($order_id, $status);
// Commit transaksi jika semua berhasil
$this->db->trans_commit();
$response = [
'success' => true,
'message' => 'Status pesanan berhasil diperbarui',
'csrf_hash' => $this->security->get_csrf_hash()
];
} catch (Exception $e) {
// Rollback transaksi jika ada error
$this->db->trans_rollback();
$response = [
'success' => false,
'message' => $e->getMessage(),
'csrf_hash' => $this->security->get_csrf_hash()
];
}
// Kirim response dalam format JSON
echo json_encode($response);
}
/**
* Handle affiliate commission when order is processed (status = 4)
* VERSION v4 - With flexible commission rules: brand, category, product, default
*
* @param int $order_id
* @param object $order_data Order data including tracking_* fields
* @return bool Success status
*/
private function handleAffiliatorCommission($order_id, $order_data)
{
// ========================================================================
// STEP 1: READ TRACKING DATA FROM ORDER RECORD
// ========================================================================
$tracking_visit_id = $order_data->tracking_visit_id ?? null;
$tracking_link_id = $order_data->tracking_link_id ?? null;
$tracking_affiliator_id = $order_data->tracking_affiliator_id ?? null;
$affiliator = null;
$visit_data = null;
if ($tracking_affiliator_id) {
log_message('info', "Using tracking data from order {$order_id} - Affiliator ID: {$tracking_affiliator_id}");
$affiliator = $this->_get_affiliator_by_id($tracking_affiliator_id);
$visit_data = [
'id' => $tracking_visit_id,
'link_id' => $tracking_link_id
];
} else {
// ====================================================================
// STEP 2: FALLBACK to old system
// ====================================================================
log_message('info', "No tracking data in order {$order_id}, using fallback to referral code");
$referral_code = !empty($order_data->referral)
? $order_data->referral
: $order_data->redeemed_voucher_code;
if (empty($referral_code)) {
log_message('info', "No referral code found for order {$order_id}");
return false;
}
$affiliator = $this->_get_affiliator_by_referral_code($referral_code);
if (!$affiliator) {
log_message('error', "Affiliator not found for referral code: {$referral_code}");
return false;
}
$visit_data = $this->_get_visit_by_session_and_referral($referral_code);
}
// ========================================================================
// STEP 3: Validate affiliator
// ========================================================================
if (!$affiliator || $affiliator['status'] !== 'active') {
log_message('error', "Affiliator invalid or not active for order {$order_id}");
return false;
}
// ========================================================================
// STEP 4: Check for duplicate commission
// ========================================================================
$existing_commission = $this->db
->select('id')
->from('affiliator_commissions')
->where('order_id', $order_id)
->limit(1)
->get()
->row();
if ($existing_commission) {
log_message('warning', "Commission already exists for order {$order_id}. Skipping creation.");
return false;
}
// ========================================================================
// STEP 5: Calculate commission with RULES SYSTEM
// ========================================================================
$order_items = $this->_get_order_items_with_details($order_id);
if (empty($order_items)) {
log_message('error', "No order items found for order {$order_id}");
return false;
}
$total_commission = 0;
$commission_details = []; // Store details per item for audit trail
foreach ($order_items as $item) {
// Get commission rate dengan priority system
$rate_info = $this->_get_commission_rate_for_item(
$affiliator['type'],
$item->product_id,
$item->brand_id,
$item->category_ids // Array of category IDs
);
$item_subtotal = (float) $item->subtotal;
$item_commission = ($item_subtotal * $rate_info['rate']) / 100;
$total_commission += $item_commission;
// Store detail untuk audit
$commission_details[] = [
'product_id' => $item->product_id,
'product_name' => $item->item_name,
'brand_id' => $item->brand_id,
'brand_name' => $item->brand_name,
'categories' => $item->category_names,
'amount' => $item_subtotal,
'rate' => $rate_info['rate'],
'commission' => round($item_commission, 2),
'rule_applied' => $rate_info['rule_type'],
'rule_target' => $rate_info['target_info']
];
}
$order_amount = (float) $order_data->grand_total_amount;
// Calculate weighted average rate untuk display
$effective_rate = $order_amount > 0
? ($total_commission / $order_amount) * 100
: 0;
// Calculate customer discount
$customer_discount_rate = (float) ($affiliator['customer_discount_rate'] ?? 0);
$customer_discount_amount = ($order_amount * $customer_discount_rate) / 100;
// ========================================================================
// STEP 6: Prepare commission data
// ========================================================================
$commission_data = [
'affiliator_id' => $affiliator['id'],
'order_id' => $order_id,
'tracking_id' => $visit_data ? $visit_data['id'] : null,
'order_amount' => $order_amount,
'commission_rate' => round($effective_rate, 2), // Weighted average for display
'commission_amount' => round($total_commission, 2),
'customer_discount_rate' => $customer_discount_rate,
'customer_discount_amount' => round($customer_discount_amount, 2),
'status' => 'pending',
'link_id' => $visit_data ? $visit_data['link_id'] : null,
'commission_details' => json_encode($commission_details, JSON_UNESCAPED_UNICODE),
'created_at' => date('Y-m-d H:i:s')
];
// ========================================================================
// STEP 7: Insert commission record
// ========================================================================
$commission_inserted = $this->db->insert('affiliator_commissions', $commission_data);
if (!$commission_inserted) {
log_message('error', "Failed to insert commission for order {$order_id}");
return false;
}
$commission_id = $this->db->insert_id();
// ========================================================================
// STEP 8: Update link statistics
// ========================================================================
if ($visit_data && !empty($visit_data['link_id'])) {
$this->_increment_link_conversion_stats($visit_data['link_id'], $order_amount);
log_message('info', "Link {$visit_data['link_id']} statistics updated");
}
log_message('info', sprintf(
"Commission #%d created for order %d: Amount: %.2f (avg %.2f%%), Affiliator: %d (%s), Items: %d",
$commission_id,
$order_id,
$total_commission,
$effective_rate,
$affiliator['id'],
$affiliator['type'],
count($commission_details)
));
return true;
}
/**
* Get commission rate for specific item based on rules
* Priority: product_specific (30) > brand (20) > category (10) > default (0)
*
* @param string $affiliator_type (asmaradoor|asmarasana)
* @param int $product_id
* @param int|null $brand_id
* @param array $category_ids Multiple categories
* @return array ['rate' => float, 'rule_type' => string, 'target_info' => string]
*/
private function _get_commission_rate_for_item($affiliator_type, $product_id, $brand_id, $category_ids = [])
{
// ========================================================================
// PRIORITY 1: Product-specific rule (highest priority = 30)
// ========================================================================
$product_rule = $this->db
->select('commission_rate, id')
->from('affiliator_commission_rules')
->where('affiliator_type', $affiliator_type)
->where('rule_type', 'product')
->where('target_id', $product_id)
->where('status', 'active')
->order_by('priority', 'DESC')
->limit(1)
->get()
->row();
if ($product_rule) {
return [
'rate' => (float) $product_rule->commission_rate,
'rule_type' => 'product',
'target_info' => "Product ID: {$product_id}",
'rule_id' => $product_rule->id
];
}
// ========================================================================
// PRIORITY 2: Brand-specific rule (priority = 20)
// ========================================================================
if ($brand_id) {
$brand_rule = $this->db
->select('commission_rate, id')
->from('affiliator_commission_rules')
->where('affiliator_type', $affiliator_type)
->where('rule_type', 'brand')
->where('target_id', $brand_id)
->where('status', 'active')
->order_by('priority', 'DESC')
->limit(1)
->get()
->row();
if ($brand_rule) {
return [
'rate' => (float) $brand_rule->commission_rate,
'rule_type' => 'brand',
'target_info' => "Brand ID: {$brand_id}",
'rule_id' => $brand_rule->id
];
}
}
// ========================================================================
// PRIORITY 3: Category rule (priority = 10)
// Check all categories, return first match with highest priority
// ========================================================================
if (!empty($category_ids)) {
$category_rule = $this->db
->select('commission_rate, target_id, id')
->from('affiliator_commission_rules')
->where('affiliator_type', $affiliator_type)
->where('rule_type', 'category')
->where_in('target_id', $category_ids)
->where('status', 'active')
->order_by('priority', 'DESC')
->limit(1)
->get()
->row();
if ($category_rule) {
return [
'rate' => (float) $category_rule->commission_rate,
'rule_type' => 'category',
'target_info' => "Category ID: {$category_rule->target_id}",
'rule_id' => $category_rule->id
];
}
}
// ========================================================================
// PRIORITY 4: Default rule (lowest priority = 0)
// ========================================================================
$default_rule = $this->db
->select('commission_rate, id')
->from('affiliator_commission_rules')
->where('affiliator_type', $affiliator_type)
->where('rule_type', 'default')
->where('target_id IS NULL')
->where('status', 'active')
->order_by('priority', 'DESC')
->limit(1)
->get()
->row();
if ($default_rule) {
return [
'rate' => (float) $default_rule->commission_rate,
'rule_type' => 'default',
'target_info' => 'Default rate',
'rule_id' => $default_rule->id
];
}
// ========================================================================
// FALLBACK: No rule found (should not happen if properly configured)
// ========================================================================
log_message('warning', sprintf(
"No commission rule found for %s, product %d, brand %d, categories [%s]",
$affiliator_type,
$product_id,
$brand_id,
implode(',', $category_ids)
));
return [
'rate' => 0,
'rule_type' => 'none',
'target_info' => 'No rule configured',
'rule_id' => null
];
}
/**
* Get order items with complete details (brand, categories)
*
* @param int $order_id
* @return array Order items with brand and category information
*/
private function _get_order_items_with_details($order_id)
{
// Get base order items
$this->db->select('
od.id_orders_detail,
od.product_id,
od.item_name,
od.item_price,
od.quantity,
od.subtotal,
p.brand_id,
b.brand as brand_name
');
$this->db->from('orders_detail od');
$this->db->join('products p', 'p.id_products = od.product_id', 'left');
$this->db->join('brands b', 'b.id_brands = p.brand_id', 'left');
$this->db->where('od.orders_id', $order_id);
$items = $this->db->get()->result();
// Enrich dengan category data untuk setiap item
foreach ($items as $item) {
// Get all categories untuk produk ini
$categories = $this->db
->select('cp.id_category, c.category')
->from('category_product cp')
->join('categories c', 'c.id_categories = cp.id_category', 'left')
->where('cp.id_product', $item->product_id)
->get()
->result();
// Extract category IDs dan names
$item->category_ids = [];
$item->category_names = [];
foreach ($categories as $cat) {
if ($cat->id_category) {
$item->category_ids[] = (int) $cat->id_category;
$item->category_names[] = $cat->category;
}
}
}
return $items;
}
/**
* Get affiliator by ID
* NEW HELPER METHOD for new visit system
*
* @param int $affiliator_id
* @return array|null
*/
private function _get_affiliator_by_id($affiliator_id)
{
$this->db->select('id, referral_code, type, commission_rate, customer_discount_rate, status');
$this->db->from('affiliators');
$this->db->where('id', $affiliator_id);
$result = $this->db->get()->row_array();
return $result ?: null;
}
/**
* Get affiliator by referral code
* EXISTING METHOD - Keep for backward compatibility
*
* @param string $referral_code
* @return array|null
*/
private function _get_affiliator_by_referral_code($referral_code)
{
$this->db->select('id, referral_code, type, commission_rate, customer_discount_rate, status');
$this->db->from('affiliators');
$this->db->where('UPPER(referral_code)', strtoupper($referral_code));
$result = $this->db->get()->row_array();
return $result ?: null;
}
/**
* Get visit data by session and referral code
* EXISTING METHOD - Keep for backward compatibility
*
* @param string $referral_code
* @return array|null
*/
private function _get_visit_by_session_and_referral($referral_code)
{
// Get the most recent visit with this referral code from current session
// within last 24 hours (attribution window)
$this->db->select('id, link_id, session_id, created_at');
$this->db->from('visits');
$this->db->where('UPPER(referral_code)', strtoupper($referral_code));
$this->db->where('converted_to_order', 0);
$this->db->where('created_at >', date('Y-m-d H:i:s', strtotime('-24 hours')));
$this->db->order_by('created_at', 'DESC');
$this->db->limit(1);
$result = $this->db->get()->row_array();
return $result ?: null;
}
/**
* Increment link conversion statistics
* EXISTING METHOD - Updated to use proper column names
*
* @param int $link_id
* @param float $order_amount
* @return bool
*/
private function _increment_link_conversion_stats($link_id, $order_amount)
{
$this->db->set('total_conversions', 'total_conversions + 1', FALSE);
$this->db->set('total_revenue', 'total_revenue + ' . (float)$order_amount, FALSE);
$this->db->where('id', $link_id);
return $this->db->update('affiliator_links');
}
/**
* Handle commission cancellation when order is cancelled
* UPDATED VERSION - Integrated with new system
*
* @param int $order_id
* @param object $order_data
* @return bool
*/
private function handleCancelCommission($order_id, $order_data)
{
// Check if commission exists for this order
$this->db->select('id, commission_amount, link_id, status');
$this->db->from('affiliator_commissions');
$this->db->where('order_id', $order_id);
$commission = $this->db->get()->row();
if (!$commission) {
log_message('info', "No commission found for cancelled order {$order_id}");
return false;
}
// Only cancel if not already paid
if ($commission->status === 'paid') {
log_message('warning', "Cannot cancel commission {$commission->id} - already paid");
return false;
}
// Update commission status to cancelled
$this->db->where('id', $commission->id);
$this->db->update('affiliator_commissions', [
'status' => 'cancelled',
'updated_at' => date('Y-m-d H:i:s')
]);
// Revert link statistics if applicable
if ($commission->link_id) {
$this->db->set('total_conversions', 'GREATEST(total_conversions - 1, 0)', FALSE);
$this->db->set('total_revenue', 'GREATEST(total_revenue - ' . (float)$commission->commission_amount . ', 0)', FALSE);
$this->db->where('id', $commission->link_id);
$this->db->update('affiliator_links');
log_message('info', "Link {$commission->link_id} statistics reverted for cancelled order {$order_id}");
}
log_message('info', "Commission {$commission->id} cancelled for order {$order_id}");
return true;
}
/**
* Update point rewards saat pesanan diproses
*/
private function updateCustomerPointRewards($order_id, $customer_id)
{
// Get customer current point
$this->db->select('current_pointreward')->from('customers')->where('id_customers', $customer_id);
$current_point = (int)$this->db->get()->row()->current_pointreward;
// Get plus point from order
$this->db->select('plus_reward')->from('orders')->where('id_orders', $order_id);
$rewards = $this->db->get()->row();
$plus_point = (int)$rewards->plus_reward;
// Calculate updated point
$updated_point = $current_point + $plus_point;
// Check if plus reward already given
$this->db->select('plus_reward_given')->from('orders')->where('id_orders', $order_id);
$plus_reward_given = $this->db->get()->row();
if ($plus_reward_given->plus_reward_given == 'no' || $plus_reward_given->plus_reward_given == NULL) {
// Update customer points
$data = ['current_pointreward' => $updated_point];
$this->db->where('id_customers', $customer_id);
$this->db->update('customers', $data);
// Mark plus reward as given
$data_order = ['plus_reward_given' => 'yes'];
$this->db->where('id_orders', $order_id);
$this->db->update('orders', $data_order);
}
}
/**
* Kirim email notifikasi berdasarkan status pesanan
*/
private function sendEmailNotification($order_id, $status)
{
// Get order data
$order = $this->order_m->get_order($order_id);
$customer = $this->customer_m->get_customer($order->customer_id);
// Get website configuration
$this->db->select('logo, from_email, website_name')->from('configuration')->where('id_configuration', 1);
$website_data = $this->db->get()->row();
$email_data = [
'order' => $order,
'customer' => $customer,
'logo' => $website_data->logo,
'website_name' => $website_data->website_name,
'emails' => $this->configuration_m->get_emails(),
'email' => $customer->email
];
// Determine email template and subject based on status
switch ($status) {
case '1': // NOT PAID
$payment_type = $order->payment_type;
// Add required data for payment type-specific email
$this->prepareNotPaidEmailData($email_data, $order_id, $payment_type);
// Determine view file based on language and payment type
$view_file = $this->getNotPaidEmailTemplate($order->order_language, $payment_type);
break;
case '2': // CANCEL
$email_data['minus_point'] = $order->minus_reward;
$email_data['title'] = 'Order Cancel';
if ($order->order_language == 'english') {
$email_data['subject'] = 'Canceling Your Order';
$view_file = 'email/english/order_cancel';
} else {
$email_data['subject'] = 'Kok Batal?';
$view_file = 'email/indonesian/order_cancel';
}
break;
case '3': // PAID
$email_data['title'] = 'Payment Confirmation';
$email_data['order_details'] = $this->order_detail_m->get_orders_detail($order_id);
$email_data['plus_point'] = $order->plus_reward;
if ($order->order_language == 'english') {
$email_data['subject'] = 'Payment Received';
$view_file = 'email/english/payment_confirmation';
} else {
$email_data['subject'] = 'Pembayaran Telah Diterima';
$view_file = 'email/indonesian/payment_confirmation';
}
break;
case '4': // PROCESS
case '5': // DELIVERED
// Email tidak perlu dikirim untuk status ini
return;
}
// Log activity
$user_id = $this->session->userdata('user_id');
$status_texts = [
'1' => 'Belum Bayar',
'2' => 'Batal',
'3' => 'Sudah Bayar',
'4' => 'Dalam Proses',
'5' => 'Terkirim'
];
$activity = "User mengubah status order {$order_id} menjadi {$status_texts[$status]}";
log_activity($user_id, $activity);
$old_status = $order->payment_status;
// Catat log perubahan
$description_notes = "Status Pesanan {$order_id} diubah menjadi {$status_texts[$status]} oleh {$this->session->userdata('name')}.";
$this->log_m->log_order_update(
$order_id,
$description_notes,
'payment_status',
$old_status,
$status
);
// Send email
$this->send_email($view_file, $email_data);
}
/**
* Menyiapkan data email untuk status NOT PAID
*/
private function prepareNotPaidEmailData(&$email_data, $order_id, $payment_type)
{
$email_data['order_details'] = $this->order_detail_m->get_orders_detail($order_id);
// Get shipping fee
$this->db->select('shipping_fee')->from('orders')->where('id_orders', $order_id);
$shipping_fee = $this->db->get()->row();
$email_data['total_shipping_fee'] = $shipping_fee;
// Add bank data for bank transfers
if ($payment_type == 'bank transfer BCA') {
$email_data['bank'] = $this->db->select('bank')->from('configuration')->where('id_configuration', 1)->get()->row()->bank;
} elseif ($payment_type == 'bank transfer MANDIRI') {
$email_data['bank'] = $this->db->select('bank1')->from('configuration')->where('id_configuration', 1)->get()->row()->bank1;
}
// Set subject based on language
if ($email_data['order']->order_language == 'english') {
$email_data['subject'] = 'Order Confirmation';
} else {
$email_data['subject'] = 'Konfirmasi Pesanan';
}
// Add voucher data if exists
if ($this->session->userdata('chosen_voucher_code')) {
$email_data['chosen_voucher_code'] = $this->session->userdata('chosen_voucher_code');
$email_data['chosen_voucher_type'] = $this->session->userdata('chosen_voucher_type');
$email_data['chosen_voucher_discount'] = $this->session->userdata('chosen_voucher_discount');
$email_data['redeemed_voucher_amount'] = $this->session->userdata('redeemed_voucher_amount');
}
$email_data['carrier_name'] = $this->session->userdata('carrier_name');
// Add tax data if exists
if ($this->session->userdata('tax')) {
$email_data['tax'] = $this->session->userdata('tax');
}
// Add point data if exists
if ($this->session->userdata('chosen_point')) {
$email_data['chosen_point'] = $this->session->userdata('chosen_point');
$email_data['chosen_point_discount'] = $this->session->userdata('chosen_point_discount');
}
}
/**
* Menentukan template email untuk status NOT PAID berdasarkan payment type
*/
private function getNotPaidEmailTemplate($language, $payment_type)
{
switch ($payment_type) {
case 'bank transfer BCA':
case 'bank transfer MANDIRI':
case 'midtrans':
return ($language == 'english') ? 'email/english/bank_transfer_english' : 'email/indonesian/bank_transfer_indo';
case 'cod':
return ($language == 'english') ? 'email/english/cod' : 'email/indonesian/cod';
default:
return ($language == 'english') ? 'email/english/bank_transfer_english' : 'email/indonesian/bank_transfer_indo';
}
}
/**
* Generate invoice PDF based on order ID
*
* @param int $order_id Order ID
* @return void
*/
public function generate_invoice($order_id)
{
// Validate order existence
if (!$this->is_valid_order($order_id)) {
redirect('admin/orders');
return;
}
// Prepare data for PDF
$pdf_data = $this->prepare_invoice_data($order_id);
// Generate and output PDF
$this->generate_invoice_pdf($pdf_data);
}
public function generate_delivery_receipt($order_id)
{
// Validate order existence
if (!$this->is_valid_order($order_id)) {
redirect('admin/orders');
return;
}
$pdf_data = $this->prepare_delivery_receipt_data($order_id);
$this->generate_delivery_receipt_pdf($pdf_data);
}
/**
* Check if order ID is valid
*
* @param int $order_id Order ID
* @return bool
*/
private function is_valid_order($order_id)
{
if ($order_id === NULL) {
return false;
}
$count_order = $this->db->select('id_orders')
->from('orders')
->where('id_orders', $order_id)
->get()
->num_rows();
return $count_order > 0;
}
/**
* Prepare all data needed for invoice
*
* @param int $order_id Order ID
* @return array
*/
private function prepare_invoice_data($order_id)
{
$data = [];
// Get website info
$data['website_data'] = $this->db->select('logo, website_name')
->from('configuration')
->where('id_configuration', 1)
->get()
->row();
// Set PDF title
$data['title'] = 'Invoice No: ' . $order_id;
// Get order data
$data['order'] = $this->db->select('*')
->from('orders')
->where('id_orders', $order_id)
->get()
->row();
// Get customer details
$data['customer'] = $this->db->select('*')
->from('customers')
->where('id_customers', $data['order']->customer_id)
->get()
->row();
// Get order details
$data['orders_detail'] = $this->db->select('*')
->from('orders_detail')
->where('orders_id', $order_id)
->get()
->result();
return $data;
}
/**
* Prepare all data needed for delivery receipt
*
* @param int $order_id Order ID
* @return array
*/
private function prepare_delivery_receipt_data($order_id)
{
$data = [];
// Get website info
$data['website_data'] = $this->db->select('logo, website_name')
->from('configuration')
->where('id_configuration', 1)
->get()
->row();
// Set PDF title
$data['title'] = 'Delivery Receipt No: ' . $order_id;
// Get order data
$data['order'] = $this->db->select('*')
->from('orders')
->where('id_orders', $order_id)
->get()
->row();
// Get customer details
$data['customer'] = $this->db->select('*')
->from('customers')
->where('id_customers', $data['order']->customer_id)
->get()
->row();
// Get order details
$data['orders_detail'] = $this->db->select('*')
->from('orders_detail')
->where('orders_id', $order_id)
->get()
->result();
return $data;
}
/**
* Generate PDF from prepared data
*
* @param array $pdf_data Data for PDF generation
* @return void
*/
private function generate_invoice_pdf($pdf_data)
{
// Load DOMPDF library
$this->load->library('dompdf_gen');
// Load view template with data
$html = $this->load->view('pdf/invoice', $pdf_data, true);
// Configure and render PDF
$this->dompdf->loadHtml($html);
$this->dompdf->setPaper('A4', 'portrait');
$this->dompdf->render();
// Output to browser
$this->dompdf->stream('invoice.pdf', ['Attachment' => 0]);
// Save to file
$output = $this->dompdf->output();
$file_path = 'uploads/pdf/invoice.pdf';
file_put_contents($file_path, $output);
}
/**
* Generate PDF from prepared data
*
* @param array $pdf_data Data for PDF generation
* @return void
*/
private function generate_delivery_receipt_pdf($pdf_data)
{
// Load DOMPDF library
$this->load->library('dompdf_gen');
// Load view template with data
$html = $this->load->view('pdf/deliveryreceipt', $pdf_data, true);
// Configure and render PDF
$this->dompdf->loadHtml($html);
$this->dompdf->setPaper('A4', 'portrait');
$this->dompdf->render();
// Output to browser
$this->dompdf->stream('deliveryreceipt.pdf', ['Attachment' => 0]);
// Save to file
$output = $this->dompdf->output();
$file_path = 'uploads/pdf/deliveryreceipt.pdf';
file_put_contents($file_path, $output);
}
// Marketing
public function manage_promotion_products()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Daftar Produk Promosi | Laciasmara';
$data['customers'] = $this->customer_m->fetch_all_customers();
$data['products'] = $this->product_m->all_products();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/marketing/manage_product');
$this->load->view('admin_new/layouts/footer');
}
public function send_promotion_products()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Kirim Produk | Laciasmara';
$data['customers'] = $this->customer_m->fetch_all_customers();
$data['products'] = $this->product_m->all_products();
$data['shippingMethods'] = $this->product_m->get_all_shipping_methods();
$this->load->view('admin_new/layouts/header', $data);
$this->load->view('admin_new/orders/marketing//add_product');
$this->load->view('admin_new/layouts/footer');
}
public function send_product()
{
if ($this->input->server('REQUEST_METHOD') !== 'POST') {
show_error('Method Not Allowed', 405);
}
try {
$postData = $this->input->post();
// Validate and prepare order data (reuse existing function with marketing context)
$orderData = $this->prepare_order_data($postData);
$orderDetails = $this->validate_order_details($postData);
// Start database transaction
$this->db->trans_start();
// Create main order (reuse existing function)
$orderId = $this->create_main_order($orderData);
// Process order details with stock management (reuse existing function)
$process_result = $this->process_order_details_with_stock($orderId, $orderDetails, $postData);
// Update customer first order count (reuse existing function)
$this->update_customer_first_order($postData['customer']);
// Complete transaction
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw new Exception('Transaction failed');
}
// Create marketing-specific completion log
$this->create_marketing_completion_log($orderId, $orderData, $process_result);
// Redirect to marketing success page
$this->session->set_flashdata('message', 'Berhasil menambah pesanan baru dari marketing untuk ' . $postData['name']);
$this->session->set_flashdata('message_type', 'success');
redirect('admin/marketing/promotion-product');
} catch (Exception $e) {
$this->db->trans_rollback();
log_message('error', 'Create Marketing Order Failed: ' . $e->getMessage());
$this->session->set_flashdata('message', 'Failed to add order: ' . $e->getMessage());
$this->session->set_flashdata('message_type', 'error');
redirect('admin/marketing/promotion-product/add');
}
}
private function create_marketing_completion_log($orderId, $orderData, $process_result)
{
try {
$nama = $orderData['recipient_name'];
$grandTotal = number_format($orderData['grand_total_amount'], 0, ',', '.');
$description = "Pesanan Baru dari Marketing telah ditambahkan dengan Order ID {$orderId} senilai Rp {$grandTotal}. Pesanan ini dikirim untuk {$nama}.";
$reference_url = base_url('admin/orders/manage-order?tab=pending');
// Create log and send notification (reuse existing method)
$log_id = $this->log_m->log_order_create($orderId, $description, $reference_url);
$this->log_m->send_order_notifications('CREATE_ORDER', $log_id, $description);
} catch (Exception $e) {
log_message('error', "Failed to create marketing order completion log for Order ID {$orderId}: " . $e->getMessage());
}
}
// Print Label
public function print_label()
{
$data['userdata'] = $this->session->userdata();
$data['title'] = 'Print Label | Laciasmara';
$this->load->view('admin_new/orders/print_label', $data);
}
public function get_print_label_data($id_order)
{
try {
if (empty($id_order) || !is_numeric($id_order)) {
$this->send_error_response('Invalid order ID', 400);
return;
}
if (!$this->is_valid_order($id_order)) {
$this->send_error_response('Order not found', 404);
return;
}
$label_data = $this->prepare_label_data($id_order);
$this->send_success_response($label_data);
} catch (Exception $e) {
log_message('error', 'Error in get_print_label_data: ' . $e->getMessage());
$this->send_error_response('Internal server error', 500);
}
}
/**
* Prepare data untuk shipping label sesuai format JavaScript
*/
private function prepare_label_data($order_id)
{
$data = array();
// 1. Ambil data website/configuration
$data['website_data'] = $this->get_website_data();
// 2. Ambil data order
$data['order'] = $this->get_order_data($order_id);
$data['customer'] = $this->get_customer_label_data($data['order']['customer_id']);
// 4. Ambil detail order items
$data['orders_detail'] = $this->get_order_details($order_id);
return $data;
}
/**
* Ambil data website/toko
*/
private function get_website_data()
{
$query = $this->db->select('logo, website_name')
->from('configuration')
->where('id_configuration', 1)
->get();
$website = $query->row();
if (!$website) {
return array(
'logo' => 'Logo',
'website_name' => 'Laciasmara.com'
);
}
return array(
'logo' => $website->logo ?: 'Logo',
'website_name' => $website->website_name ?: 'Laci Asmara',
);
}
/**
* Ambil data order
*/
private function get_order_data($order_id)
{
$query = $this->db->select('id_orders, customer_id, order_date, grand_total_amount, shipping_fee, district, subdistrict, province, phone, email, country')
->from('orders')
->where('id_orders', $order_id)
->get();
$order = $query->row();
if (!$order) {
throw new Exception("Order not found");
}
// Ambil satu chosen_shipping_id dari orders_detail untuk order ini
$shipment_query = $this->db->select('sm.name AS shipment_type, sm.shipper as shipment_name, sm.service_code as shipment_code')
->from('orders_detail od')
->join('shipment_method sm', 'sm.id = od.chosen_shipping_id', 'left')
->where('od.orders_id', $order_id)
->limit(1) // ambil satu saja
->get();
$shipment = $shipment_query->row();
$shipment_name = $shipment ? $shipment->shipment_name : 'Unknown';
$shipment_type = $shipment ? $shipment->shipment_type : 'Unknown';
$shipment_code = $shipment ? $shipment->shipment_code : 'Unknown';
return array(
'id_orders' => $order->id_orders,
'customer_id' => $order->customer_id,
'order_date' => $order->order_date,
'grand_total_amount' => floatval($order->grand_total_amount),
'shipping_fee' => $order->shipping_fee ?: 0,
'district' => $order->district,
'subdistrict' => $order->subdistrict,
'province' => $order->province,
'phone' => $order->phone,
'email' => $order->email,
'shipment_name' => $shipment_name,
'shipment_type' => $shipment_type,
'shipment_code' => $shipment_code,
);
}
/**
* Ambil detail order items
*/
private function get_order_details($order_id)
{
$query = $this->db->select('od.item_name, od.quantity, od.item_price, od.attributes, od.sku, sm.name AS shipment_name')
->from('orders_detail od')
->join('shipment_method sm', 'sm.id = od.chosen_shipping_id', 'left')
->where('od.orders_id', $order_id)
->get();
$details = $query->result();
if (!$details) {
return array();
}
$order_details = array();
foreach ($details as $detail) {
$order_details[] = array(
'product_name' => $detail->item_name,
'attribute' => $detail->attributes,
'quantity' => intval($detail->quantity),
'price' => floatval($detail->item_price),
'shipment_name' => $detail->shipment_name ?: 'Unknown',
'sku' => $detail->sku ?: 'N/A'
);
}
return $order_details;
}
private function get_customer_label_data($customer_id)
{
$query = $this->db->select('shipping_name, email, shipping_phone, shipping_address, shipping_district, shipping_subdistrict, shipping_province')
->from('customers')
->where('id_customers', $customer_id)
->get();
$customer = $query->row();
if (!$customer) {
throw new Exception("Customer not found");
}
return array(
'name' => $customer->shipping_name,
'email' => $customer->email,
'phone' => $customer->shipping_phone ?: '',
'address' => $customer->shipping_address,
'province' => $customer->shipping_province,
'district' => $customer->shipping_district,
'subdistrict' => $customer->shipping_subdistrict,
);
}
/**
* Kirim response sukses
*/
private function send_success_response($data)
{
$response = array(
'success' => true,
'message' => 'Data retrieved successfully',
'data' => $data
);
$this->output
->set_status_header(200)
->set_output(json_encode($response));
}
/**
* Kirim response error
*/
private function send_error_response($message, $status_code = 400)
{
$response = array(
'success' => false,
'message' => $message,
'data' => null
);
$this->output
->set_status_header($status_code)
->set_output(json_encode($response));
}
}