https://t.me/RX1948
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 :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /var/www/laciasmara.com/public_html/shop/application/controllers/admin/Orders.php
<?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));

	}

}


https://t.me/RX1948 - 2025