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 : /proc/self/root/var/www/laciasmara.com/public_html/shop/application/controllers/ |
Upload File : |
<?php if (!defined('BASEPATH')) { exit('No direct script access allowed'); } class Shipping extends Public_Controller { function __construct() { parent::__construct(); $this->load->model('cart_model'); $this->load->model('product_m'); $this->load->model('customer_m'); $this->load->model('Category_m'); $this->load->model('Statistic_m'); $this->load->model('log_m'); $this->load->library('cart'); $this->load->helper('rajaongkir'); $this->load->helper('shipping'); $this->load->helper('biteship'); $this->load->library('form_validation'); $this->load->model('Footer_m'); $this->load->library('GoogleClient'); $this->load->library('VisitorTracking'); $this->load->model('Top_banner_m'); $loginUrl = $this->googleclient->getLoginUrl(); $this->data_footer['googleUrl'] = $loginUrl; if ($this->session->userdata('site_lang') == 'english') { $this->lang->load('mainpage', 'english'); } else { $this->lang->load('mainpage', 'indonesian'); } } public function index() { $this->visitortracking->trackVisitor(); // Redirect to index page if there's no product in cart if (!$this->cart->contents()) { redirect('cart'); } if ((int) $this->session->userdata('customer')['customer_id'] == 0) { redirect('login'); } // Store cart items $cart_items = $this->cart->contents(); $id_customer = (int) $this->session->userdata('customer')['customer_id']; $id_reseller = (int) $this->_get_reseller_id($id_customer); $point_reward = $this->getCustomerPointReward($id_customer); $is_referred_customer = $this->_check_is_referred($id_customer); $customer_voucher = $this->_get_customer_voucher($id_customer); $customer_data = $this->customer_m->get_customer($id_customer); $is_customer_first = $this->_check_is_customer_first($id_customer); $is_reseller = $this->_check_is_reseller($id_customer); $reseller_configuration = $this->_get_reseller_configuration_by_id($id_reseller); $addresses = $this->customer_m->get_customer_addresses($id_customer); $subtotal = $this->cart->total(); $shipment_methods_data = $this->getShipmentMethods($cart_items, $addresses[0], $subtotal); $all_shipping_methods = []; $initial_shipping_method = null; if (!empty($shipment_methods_data['common'])) { $all_shipping_methods = $shipment_methods_data['common']; $initial_shipping_method = $all_shipping_methods[0]; } $data['all_shipping_methods'] = $all_shipping_methods; $data['initial_shipping_method'] = $initial_shipping_method; $data['id_customer'] = $id_customer; $data['id_reseller'] = $id_reseller; $data['is_referred_customer'] = $is_referred_customer; $data['is_customer_first'] = $is_customer_first; $data['is_reseller'] = $is_reseller; $data['reseller_configuration'] = $reseller_configuration; $data['customer_vouchers'] = $customer_voucher; $data['customer_data'] = $customer_data; $data['cart_items'] = $cart_items; $data['shipment_methods'] = $shipment_methods_data['per_item']; $data['common_shipment_methods'] = $shipment_methods_data['common']; $data['addresses'] = $addresses; $data['point_reward_data'] = $point_reward; // Set grand total $grand_total = 0; $grand_total_before_discount = 0; foreach ($cart_items as $item) { $grand_total += $item['subtotal']; $grand_total_before_discount += $item['real_price'] * $item['qty']; } $data['grand_total'] = $grand_total; $data['grand_total_before_discount'] = $grand_total_before_discount; $data['total_product_discount'] = $grand_total_before_discount - $grand_total; $this->session->set_userdata('shipping_cart', $cart_items); $activeBanners = $this->Top_banner_m->get_active_banners(); // Fetch website data for header and meta information $websiteData = $this->db->select('website_icon, browser_title, meta_description') ->from('configuration') ->where('id_configuration', 1) ->get() ->row(); $meta_description = ($this->session->userdata('site_lang') == 'english') ? "Pay easily, pleasure instantly! Choose your method and get ready to explore the fun!" : "Pembayaran tanpa repot, kenikmatan di tangan! Nikmati kemudahan pembayaran dengan berbagai pilihan."; // Prepare header data $this->data_header = [ 'website_icon' => $websiteData->website_icon, 'browser_title' => $websiteData->browser_title . ' - Checkout', 'meta_description' => $meta_description, 'banners' => $activeBanners, 'logo_path' => 'https://storage.googleapis.com/laciasmara-photos/laciaasmara_assets/laciasmara_landing_page/laciasmara_landing_page_logo.webp', 'footer_categories' => $this->Footer_m->get_all_categories(), 'footer_social_media' => $this->Footer_m->get_social_media(), 'footer_payment_methods' => $this->Footer_m->get_payment_methods(), 'footer_asmaradoor' => $this->Footer_m->get_asmaradoor(), 'footer_bottom' => $this->Footer_m->get_footer_bottom() ]; $this->data_footer['popular_categories'] = $this->Category_m->get_footer_popular_categories(); $this->data_footer['trending_searches'] = $this->Statistic_m->get_trending_searches(); $this->load->view("themes/3/header_new", $this->data_header); $this->load->view("shipping_new", $data); $this->load->view("themes/3/footer_new", $this->data_footer); } public function get_shipping_rates() { try { // Validasi method request if ($this->input->server('REQUEST_METHOD') !== 'POST') { $this->output ->set_content_type('application/json') ->set_status_header(405) ->set_output(json_encode([ 'success' => false, 'message' => 'Method not allowed' ])); return; } // Ambil data dari form $cart_items = $this->input->post('cart_items'); // JSON string atau array $shipping_method_id = $this->input->post('shipping_method_id'); // ID dari shipping methods table $destination_lat = $this->input->post('destination_latitude'); $destination_lng = $this->input->post('destination_longitude'); $destination_postal_code = $this->input->post('destination_postal_code'); // Validasi required fields if (!$cart_items) { $this->output ->set_content_type('application/json') ->set_status_header(400) ->set_output(json_encode([ 'success' => false, 'message' => 'Cart items is required' ])); return; } if (!$shipping_method_id) { $this->output ->set_content_type('application/json') ->set_status_header(400) ->set_output(json_encode([ 'success' => false, 'message' => 'Shipping method ID is required' ])); return; } // Get shipping method info dari database $this->db->select('carrier, service_code, name as method_name') ->from('shipment_method') ->where('id', $shipping_method_id); $shipping_method = $this->db->get()->row(); if (!$shipping_method) { $this->output ->set_content_type('application/json') ->set_status_header(400) ->set_output(json_encode([ 'success' => false, 'message' => 'Shipping method not found' ])); return; } $courier_code = $shipping_method->carrier; $service_code = $shipping_method->service_code; // Parse cart items jika dalam format JSON string if (is_string($cart_items)) { $cart_items = json_decode($cart_items, true); } if (!is_array($cart_items) || empty($cart_items)) { $this->output ->set_content_type('application/json') ->set_status_header(400) ->set_output(json_encode([ 'success' => false, 'message' => 'Invalid cart items format' ])); return; } // Build biteship items $biteship_items = []; $total_weight = 0; $total_value = 0; foreach ($cart_items as $item) { $detail_id = isset($item['id']) ? $item['id'] : null; $qty = isset($item['qty']) ? (int)$item['qty'] : 1; if ($detail_id) { // Ambil data produk dari database $this->db->select(' pd.*, p.title as product_name ') ->from('product_details pd') ->join('products p', 'p.id_products = pd.product_id') ->where('pd.id', $detail_id); $product_data = $this->db->get()->row(); if ($product_data) { // Calculate per item $item_weight = max((isset($product_data->weight) ? $product_data->weight : 1000), 100) * $qty; $item_value = max((isset($product_data->price) ? $product_data->price : (isset($item['price']) ? $item['price'] : 10000)), 1000) * $qty; $item_length = isset($product_data->length) ? $product_data->length : 10; $item_width = isset($product_data->width) ? $product_data->width : 10; $item_height = isset($product_data->height) ? $product_data->height : 10; // Track totals $total_weight += $item_weight; $total_value += $item_value; $biteship_items[] = biteship_create_item([ 'name' => isset($product_data->product_name) ? $product_data->product_name : (isset($item['name']) ? $item['name'] : 'Product'), 'sku' => isset($product_data->sku) ? $product_data->sku : (isset($item['sku']) ? $item['sku'] : 'SKU-' . $detail_id), 'length' => $item_length, 'width' => $item_width, 'height' => $item_height, 'weight' => $item_weight, 'value' => $item_value, 'quantity' => $qty ]); } } else { // Fallback untuk item tanpa detail_id $item_weight = isset($item['weight']) ? $item['weight'] * $qty : 1000 * $qty; $item_value = isset($item['value']) ? $item['value'] * $qty : 10000 * $qty; $total_weight += $item_weight; $total_value += $item_value; $biteship_items[] = biteship_create_item([ 'name' => isset($item['name']) ? $item['name'] : 'Product', 'sku' => isset($item['sku']) ? $item['sku'] : 'FALLBACK-SKU', 'length' => isset($item['length']) ? $item['length'] : 10, 'width' => isset($item['width']) ? $item['width'] : 10, 'height' => isset($item['height']) ? $item['height'] : 10, 'weight' => $item_weight, 'value' => $item_value, 'quantity' => $qty ]); } } // Fallback jika tidak ada items valid if (empty($biteship_items)) { $biteship_items = [ biteship_create_item([ 'name' => 'Fallback Item', 'weight' => 1000, 'value' => 100000 ]) ]; $total_weight = 1000; $total_value = 100000; } // Build parameters untuk API call $params = [ 'origin_postal_code' => 12820, 'origin_latitude' => '-6.229434', 'origin_longitude' => '106.853123', 'items' => $biteship_items ]; // Set courier - jika service_code ada, gunakan format khusus if ($service_code) { $params['couriers'] = $courier_code; } // Add destination data if ($destination_postal_code) { $params['destination_postal_code'] = $destination_postal_code; } if ($destination_lat && $destination_lng) { $params['destination_latitude'] = $destination_lat; $params['destination_longitude'] = $destination_lng; } // Call Biteship API $response = biteship_get_rates_mixed($params); if (!$response['success']) { $this->output ->set_content_type('application/json') ->set_status_header(400) ->set_output(json_encode([ 'success' => false, 'message' => 'Failed to get shipping rates', 'params' => $params, 'error' => isset($response['message']) ? $response['message'] : 'Unknown error' ])); return; } // Process response $shipping_rates = []; if (isset($response['data']['pricing']) && !empty($response['data']['pricing'])) { foreach ($response['data']['pricing'] as $rate) { $shipping_rates[] = [ 'courier_code' => $rate['courier_code'], 'service_code' => $rate['courier_service_code'], 'courier_name' => $rate['courier_name'], 'service_name' => $rate['courier_service_name'], 'price' => $rate['price'], 'duration' => $rate['duration'], 'service_type' => isset($rate['service_type']) ? $rate['service_type'] : '', 'description' => isset($rate['description']) ? $rate['description'] : '', 'available_for_cod' => isset($rate['available_for_cash_on_delivery']) ? $rate['available_for_cash_on_delivery'] : false, 'available_for_insurance' => isset($rate['available_for_insurance']) ? $rate['available_for_insurance'] : false ]; } } // Return success response $this->output ->set_content_type('application/json') ->set_status_header(200) ->set_output(json_encode([ 'success' => true, 'message' => 'Success to get shipping rates', 'data' => [ 'origin' => isset($response['data']['origin']) ? $response['data']['origin'] : null, 'destination' => isset($response['data']['destination']) ? $response['data']['destination'] : null, 'shipping_rates' => $shipping_rates, 'total_weight' => $total_weight, 'total_value' => $total_value, 'total_items' => count($biteship_items) ] ])); } catch (Exception $e) { $this->output ->set_content_type('application/json') ->set_status_header(500) ->set_output(json_encode([ 'success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage() ])); } } public function get_available_shipping_methods() { if (!$this->input->is_ajax_request()) { show_404(); return; } $customer_data = $this->session->userdata('customer'); $customer_id = $customer_data['customer_id']; // Validasi input $destination_latitude = $this->input->post('destination_latitude'); $destination_longitude = $this->input->post('destination_longitude'); $destination_postal_code = $this->input->post('destination_postal_code'); $address_id = $this->input->post('address_id'); $required_fields = [ 'customer_id' => [$customer_id, 'Unauthorized access'], 'destination_latitude' => [$destination_latitude, 'Destination latitude is required'], 'destination_longitude' => [$destination_longitude, 'Destination longitude is required'], 'destination_postal_code' => [$destination_postal_code, 'Destination postal code is required'], ]; $validation_result = validate_required_fields($required_fields); if (!$validation_result['status']) { $this->output ->set_status_header(400) ->set_content_type('application/json') ->set_output(json_encode(['error' => $validation_result['error']])); return; } try { $cart_items = $this->cart->contents(); if (empty($cart_items)) { $this->output ->set_content_type('application/json') ->set_output(json_encode(['error' => 'Cart is empty'])); return; } $subtotal = $this->cart->total(); $shipping_data = null; $is_first_order = 0; // Default value // Get address data if address_id is provided if ($address_id) { $address_data = $this->customer_m->get_customer_address_by_id($customer_id, $address_id); if ($address_data) { $shipping_data = $address_data; $is_first_order = isset($address_data->is_first) ? $address_data->is_first : 0; } } // Create or update shipping_data object if (!$shipping_data) { $shipping_data = (object)[ 'latitude' => $destination_latitude, 'longitude' => $destination_longitude, 'postal_code' => $destination_postal_code, 'is_first' => $is_first_order ]; } else { // Update existing shipping_data with new coordinates $shipping_data->latitude = $destination_latitude; $shipping_data->longitude = $destination_longitude; $shipping_data->postal_code = $destination_postal_code; // Keep existing is_first value if available if (!isset($shipping_data->is_first)) { $shipping_data->is_first = $is_first_order; } } // Dapatkan metode pengiriman yang tersedia $shipment_methods_data = $this->getShipmentMethods($cart_items, $shipping_data, $subtotal); $all_shipping_methods = []; if (!empty($shipment_methods_data['common'])) { $all_shipping_methods = $shipment_methods_data['common']; } $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $all_shipping_methods, 'debug_info' => [ 'is_first' => isset($shipping_data->is_first) ? $shipping_data->is_first : 'not_set', 'address_found' => !empty($address_data), 'total_methods' => count($all_shipping_methods), 'subtotal' => $subtotal ] ])); } catch (Exception $e) { // Log error untuk debugging log_message('error', 'Shipping methods error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'error' => 'Internal server error', 'message' => $e->getMessage() // Hanya untuk development, hapus di production ])); } } private function _get_reseller_configuration_by_id($id_reseller) { $reseller_configuration = $this->db->select('*') ->from('resellers') ->where('id_resellers', $id_reseller) ->where('active', 'yes') ->get() ->row(); return $reseller_configuration; } private function _check_is_customer_first($id_customer) { $is_first = false; $customer_first = $this->db->select('is_first') ->from('customers') ->where('id_customers', $id_customer) ->get() ->row(); if ($customer_first && $customer_first->is_first == 0) { $is_first = true; } return $is_first; } private function _check_is_reseller($id_customer) { $is_reseller = false; $reseller_id = $this->db->select('reseller_id') ->from('customers') ->where('id_customers', $id_customer) ->get() ->row(); if ($reseller_id) { $is_reseller = true; } return $is_reseller; } private function _get_reseller_id($id_customer) { $reseller_id = $this->db->select('reseller_id') ->from('customers') ->where('id_customers', $id_customer) ->get() ->row(); return $reseller_id->reseller_id; } private function _check_is_referred($id_customer) { $referal_code = $this->db->select('refferal') ->from('customers') ->where('id_customers', $id_customer) ->get() ->row(); return $referal_code; } public function _get_customer_voucher($id_customer) { // Load the database model if not already loaded $this->load->model('customer_m'); // Fetch customer details $customer = $this->customer_m->get_customer($id_customer); // Initialize voucher data $vouchers = []; // Check if reseller_id is set if ($customer->reseller_id) { // No vouchers for customers with a reseller_id return $vouchers; } // Check if the customer has a referral if ($customer->refferal) { // Check the affiliator register table for the referral $affiliator = $this->get_approved_affiliator_by_referral($customer->refferal); if ($affiliator) { // Check the category and set the discount accordingly if ($affiliator['kategori'] === 'asmaradoor') { $vouchers[] = [ 'name' => 'Voucher: ' . $customer->refferal, 'type' => 'percentage', 'value' => 10, 'code' => $customer->refferal ]; } elseif ($affiliator['kategori'] === 'asmarasana') { $vouchers[] = [ 'name' => 'Voucher: ' . $customer->refferal, 'type' => 'percentage', 'value' => 20, 'code' => $customer->refferal ]; } } } // Check if it's the customer's first purchase if ($customer->is_first == 0) { $vouchers[] = [ 'name' => 'First Purchase Discount', 'type' => 'percentage', 'value' => 5, 'code' => 'FIRSTPURCHASE' ]; } // Return the voucher data as JSON return $vouchers; } private function get_approved_affiliator_by_referral($referral) { // Load the database if not already loaded $this->load->database(); // Query to get the approved affiliator by referral $query = $this->db->select('kategori') ->from('affiliator_register') ->where('referral', $referral) ->where('status', 'approve') ->get(); // Check if any result is returned if ($query->num_rows() > 0) { // Return the first row as an associative array return $query->row_array(); } // Return null if no affiliator is found return null; } /** * Cek apakah diskon shipping berlaku * @param array $cart_items * @param object $shipping_data * @return bool */ // lebih kompleks, jadi diskonnya per metode pengiriman private function checkDiscountEligibility($cart_items, $shipping_methods, $shipping_data) { if (!$shipping_data || intval($shipping_data->is_first) === 0) { return false; } if (empty($cart_items)) { return false; } return true; } private function getCustomerPointReward($id_customer) { $point_reward = $this->db->select('current_pointreward') ->from('customers') ->where('id_customers', $id_customer) ->get() ->row(); return $point_reward; } private function _get_customer_shipping_data($id_customer) { $shipping_data = $this->db->select('shipping_id_province, shipping_id_district, shipping_id_subdistrict, shipping_country, shipping_province, shipping_district, shipping_subdistrict, shipping_address, shipping_name, shipping_postcode, shipping_phone, is_first') ->from('customers') ->where('id_customers', $id_customer) ->get() ->row(); return $shipping_data; } public function update_shipping_address() { $data = $this->input->post(); $id_customer = (int) $this->session->userdata('customer')['customer_id']; $update_data = [ 'id_province' => $data['province'], 'id_district' => $data['district'], 'id_subdistrict' => $data['subdistrict'], 'province' => $data['province_name'], 'district' => $data['district_name'], 'subdistrict' => $data['subdistrict_name'], 'postcode' => $data['postcode'], 'shipping_id_province' => $data['province'], 'shipping_id_district' => $data['district'], 'shipping_id_subdistrict' => $data['subdistrict'], 'shipping_province' => $data['province_name'], 'shipping_district' => $data['district_name'], 'shipping_subdistrict' => $data['subdistrict_name'], 'shipping_name' => $data['receiver_name'], 'shipping_address' => $data['full_address'], 'shipping_phone' => $data['receiver_phone'], 'shipping_postcode' => $data['postcode'] ]; $this->db->where('id_customers', $id_customer)->update('customers', $update_data); if ($this->db->affected_rows() > 0) { echo json_encode(['status' => 'success']); } else { // Cek apakah data yang diupdate sama dengan data yang sudah ada if ($this->db->affected_rows() == 0 && $this->db->get_where('customers', ['id_customers' => $id_customer])->num_rows() > 0) { echo json_encode(['status' => 'success']); // Data sama, anggap sukses } else { echo json_encode(['status' => 'error']); } } } // Shipment public function getShipmentMethods($cart_items, $shipping_data, $subtotal = null) { // STEP 1: Get available methods dari Biteship API $biteship_available_methods = $this->getBiteshipAvailableMethods($cart_items, $shipping_data); if (empty($biteship_available_methods)) { return [ 'per_item' => [], 'common' => [] ]; } // STEP 2: Match Biteship response dengan database shipment_method $matched_shipment_methods = $this->matchBiteshipWithDatabase($biteship_available_methods); if (empty($matched_shipment_methods)) { return [ 'per_item' => [], 'common' => [] ]; } // STEP 3: Filter per produk dan cari intersection $filtered_methods = $this->filterByProductsAndFindCommon($cart_items, $matched_shipment_methods); // STEP 4: Apply discount logic dan format data return $this->applyDiscountAndFormatMethods($filtered_methods, $cart_items, $shipping_data, $subtotal); } private function getBiteshipAvailableMethods($cart_items, $shipping_data) { try { $biteship_items = []; $total_weight = 0; $total_value = 0; $max_dimensions = ['length' => 0, 'width' => 0, 'height' => 0]; foreach ($cart_items as $item) { $detail_id = $item['id']; $qty = $item['qty']; $this->db->select(' pd.*, p.title as product_name, ') ->from('product_details pd') ->join('products p', 'p.id_products = pd.product_id') ->where('pd.id', $detail_id); $product_data = $this->db->get()->row(); if ($product_data) { // Calculate per item $item_weight = max((isset($product_data->weight) ? $product_data->weight : 1000), 100) * $qty; // Min 100g per item $item_value = max((isset($product_data->price) ? $product_data->price : $item['price']), 1000) * $qty; // Min 1000 per item $item_length = isset($product_data->length) ? $product_data->length : 10; $item_width = isset($product_data->width) ? $product_data->width : 10; $item_height = isset($product_data->height) ? $product_data->height : 10; // Track totals $total_weight += $item_weight; $total_value += $item_value; // Track max dimensions $max_dimensions['length'] = max($max_dimensions['length'], $item_length); $max_dimensions['width'] = max($max_dimensions['width'], $item_width); $max_dimensions['height'] = max($max_dimensions['height'], $item_height); $biteship_items[] = biteship_create_item([ 'name' => isset($product_data->product_name) ? $product_data->product_name : $item['name'], 'sku' => isset($product_data->sku) ? $product_data->sku : $item['sku'], 'length' => $item_length, 'width' => $item_width, 'height' => $item_height, 'weight' => $item_weight, 'value' => $item_value, 'quantity' => $qty ]); } } // Fallback jika tidak ada items valid if (empty($biteship_items)) { $biteship_items = [ biteship_create_item([ 'name' => 'Fallback Item', 'weight' => 1000, 'value' => 100000 ]) ]; $total_weight = 1000; $total_value = 100000; } $params = [ 'origin_postal_code' => 12820, 'origin_latitude' => '-6.229434', 'origin_longitude' => '106.853123', 'destination_postal_code' => $shipping_data->postal_code, 'couriers' => 'gojek,grab,jne', 'items' => $biteship_items ]; // Tambahkan koordinat jika tersedia if (isset($shipping_data->latitude) && isset($shipping_data->longitude)) { $params['destination_latitude'] = $shipping_data->latitude; $params['destination_longitude'] = $shipping_data->longitude; } // Call Biteship API $response = biteship_get_rates_mixed($params); if (!$response['success']) { return []; } // Extract available methods dari response $available_methods = []; if ($response['success'] && isset($response['data']['pricing'])) { foreach ($response['data']['pricing'] as $rate) { $available_methods[] = [ 'courier_code' => $rate['courier_code'], 'service_code' => $rate['courier_service_code'], 'courier_name' => $rate['courier_name'], 'service_name' => $rate['courier_service_name'], 'price' => $rate['price'], 'duration' => $rate['duration'] ]; } } return $available_methods; } catch (Exception $e) { return []; } } private function matchBiteshipWithDatabase($biteship_methods) { if (empty($biteship_methods)) { return []; } $matched_methods = []; foreach ($biteship_methods as $biteship_method) { $this->db->select('id, name, carrier, shipper, icon_url, service_code') ->from('shipment_method') ->where('carrier', $biteship_method['courier_code']) ->where('service_code', $biteship_method['service_code']) ->where('is_active', 1); $db_method = $this->db->get()->row(); if ($db_method) { $matched_methods[] = (object)[ 'id' => $db_method->id, 'name' => $db_method->name, 'carrier' => $db_method->carrier, 'shipper' => $db_method->shipper, 'icon_url' => $db_method->icon_url, 'service_code' => $db_method->service_code, 'biteship_price' => $biteship_method['price'], 'biteship_duration' => $biteship_method['duration'], ]; } } return $matched_methods; } private function filterByProductsAndFindCommon($cart_items, $matched_methods) { $shipment_methods_per_item = []; $common_methods = null; // Extract IDs dari matched methods untuk query yang lebih efisien $matched_method_ids = array_column($matched_methods, 'id'); if (empty($matched_method_ids)) { return [ 'per_item' => [], 'common' => [] ]; } foreach ($cart_items as $item) { $detail_id = $item['id']; // Get product_id dari product_details dengan struktur tabel yang benar $this->db->select('product_id'); $product_detail = $this->db->get_where('product_details', ['id' => $detail_id])->row(); if (!$product_detail) continue; $product_id = $product_detail->product_id; // Query shipment_method_product menggunakan id_products yang benar $this->db->select('shipment_method_id') ->from('shipment_method_product') ->where('product_id', $product_id) ->where_in('shipment_method_id', $matched_method_ids); $product_method_ids = $this->db->get()->result(); if (empty($product_method_ids)) continue; $product_method_ids_array = array_column($product_method_ids, 'shipment_method_id'); // Filter matched_methods berdasarkan yang tersedia untuk produk ini $available_for_product = array_filter($matched_methods, function ($method) use ($product_method_ids_array) { return in_array($method->id, $product_method_ids_array); }); if (!empty($available_for_product)) { $shipment_methods_per_item[$item['rowid']] = array_values($available_for_product); $available_ids = array_column($available_for_product, 'id'); // Calculate intersection untuk common methods if ($common_methods === null) { $common_methods = $available_ids; } else { $common_methods = array_intersect($common_methods, $available_ids); } } } // Get detail common methods $common_methods_detail = []; if (!empty($common_methods)) { $common_methods_detail = array_filter($matched_methods, function ($method) use ($common_methods) { return in_array($method->id, $common_methods); }); $common_methods_detail = array_values($common_methods_detail); } return [ 'per_item' => $shipment_methods_per_item, 'common' => $common_methods_detail ]; } private function applyDiscountAndFormatMethods($filtered_methods, $cart_items, $shipping_data, $subtotal = null) { $this->load->helper('shipping'); // Format per_item methods $formatted_per_item = []; foreach ($filtered_methods['per_item'] as $rowid => $methods) { $formatted_per_item[$rowid] = []; foreach ($methods as $method) { $formatted_per_item[$rowid][] = $this->formatMethodWithPerMethodDiscount($method, $cart_items, $shipping_data, $subtotal); } } // Format common methods $formatted_common = []; foreach ($filtered_methods['common'] as $method) { $formatted_common[] = $this->formatMethodWithPerMethodDiscount($method, $cart_items, $shipping_data, $subtotal); } // Get general discount info for reference (optional, for additional data) // $general_discount_info = $this->getGeneralDiscountInfo($cart_items, $shipping_data, $subtotal); return [ 'per_item' => $formatted_per_item, 'common' => $formatted_common ]; } private function formatMethodWithPerMethodDiscount($method, $cart_items, $shipping_data, $subtotal = null) { // Format ETD dari Biteship duration $etd_info = $this->formatBiteshipETD($method->biteship_duration, $method->service_code); $display_name = get_shipping_service_display_name($method); // Calculate original price $original_price = $method->biteship_price; $final_price = $original_price; $discount_amount = 0; $is_free = false; $discount_applicable = false; // Check if this specific method is eligible for discount $method_discount_info = $this->getMethodSpecificDiscountInfo($method, $cart_items, $shipping_data, $subtotal); if ($method_discount_info['is_discount_applicable']) { $discount_applicable = true; $discount_amount = min($method_discount_info['discount_applied'], $original_price); $final_price = $original_price - $discount_amount; if ($final_price <= 0) { $final_price = 0; $is_free = true; } } return [ 'id' => $method->id, 'name' => $method->name, 'display_name' => $display_name, 'carrier' => $method->carrier, 'shipper' => $method->shipper, 'icon_url' => $method->icon_url, 'service_code' => $method->service_code, 'original_price' => $original_price, 'final_price' => $final_price, 'discount_amount' => $discount_amount, 'is_free' => $is_free, 'discount_applicable' => $discount_applicable, 'service_type' => $this->determineServiceType($method->service_code), 'duration' => $method->biteship_duration, 'etd_info' => $etd_info, 'biteship_service_name' => isset($method->biteship_service_name) ? $method->biteship_service_name : $method->name, 'method_discount_info' => $method_discount_info ]; } private function getMethodSpecificDiscountInfo($method, $cart_items, $shipping_data, $subtotal = null) { // Determine service type for this method $service_type = $this->determineServiceType($method->service_code); // Check basic eligibility first $basic_eligibility = $this->checkDiscountEligibility($cart_items, [$method], $shipping_data); // Check if this service type is eligible for discount $service_eligible = $this->isServiceTypeEligibleForDiscount($service_type); $is_discount_applicable = $basic_eligibility && $service_eligible; $discount_percentage = 0; $discount_applied = 0; if ($is_discount_applicable && $subtotal) { // Tentukan persentase diskon berdasarkan subtotal if ($subtotal >= 1500000) { $discount_percentage = 5; } else { $discount_percentage = 3; } // Hitung diskon untuk metode ini $discount_applied = ($subtotal * $discount_percentage) / 100; } return [ 'is_discount_applicable' => $is_discount_applicable, 'service_type' => $service_type, 'service_eligible' => $service_eligible, 'basic_eligibility' => $basic_eligibility, 'discount_percentage' => $discount_percentage, 'discount_applied' => $discount_applied, 'subtotal_threshold_met' => $subtotal >= 1500000, 'is_first_order' => $shipping_data && intval($shipping_data->is_first) === 1 ]; } private function isServiceTypeEligibleForDiscount($service_type) { // Hanya metode reguler dan next_day yang eligible untuk diskon $eligible_service_types = ['regular', 'next_day']; return in_array($service_type, $eligible_service_types); } private function formatMethodWithDiscount($method, $discount_info) { // Format ETD dari Biteship duration $etd_info = $this->formatBiteshipETD($method->biteship_duration, $method->service_code); $display_name = get_shipping_service_display_name($method); // Calculate original and discounted price $original_price = $method->biteship_price; $final_price = $original_price; $discount_amount = 0; $is_free = false; if ($discount_info['is_discount_applicable']) { $discount_amount = min($discount_info['discount_applied'], $original_price); $final_price = $original_price - $discount_amount; if ($final_price <= 0) { $final_price = 0; $is_free = true; } } return [ 'id' => $method->id, 'name' => $method->name, 'display_name' => $display_name, 'carrier' => $method->carrier, 'shipper' => $method->shipper, 'icon_url' => $method->icon_url, 'service_code' => $method->service_code, 'original_price' => $original_price, 'final_price' => $final_price, 'discount_amount' => $discount_amount, 'is_free' => $is_free, 'duration' => $method->biteship_duration, 'etd_info' => $etd_info, 'biteship_service_name' => isset($method->biteship_service_name) ? $method->biteship_service_name : $method->name ]; } private function getDiscountInfo($cart_items, $shipping_data, $shipping_methods = [], $total_shipping_fee = 0, $subtotal = null) { $is_discount_applicable = $this->checkDiscountEligibility($cart_items, $shipping_methods, $shipping_data); $discount_percentage = 0; $discount_applied = 0; $shipping_discount_received = 0; if ($is_discount_applicable && $subtotal) { // Tentukan persentase diskon berdasarkan subtotal if ($subtotal >= 1500000) { $discount_percentage = 5; } else { $discount_percentage = 3; } // Hitung diskon $discount_applied = ($subtotal * $discount_percentage) / 100; $shipping_discount_received = min($discount_applied, $total_shipping_fee); } return [ 'is_discount_applicable' => $is_discount_applicable, 'discount_percentage' => $discount_percentage, 'discount_applied' => $discount_applied, 'shipping_discount_received' => $shipping_discount_received, 'subtotal_threshold_met' => $subtotal >= 1500000, 'is_first_order' => $shipping_data && intval($shipping_data->is_first) === 1 ]; } private function formatBiteshipETD($biteship_duration, $service_code = null) { $service_type = $this->determineServiceType($service_code); $result = format_shipping_etd($biteship_duration, $service_type, date('Y-m-d H:i:s')); return $result; } private function determineServiceType($service_code) { $service_lower = strtolower($service_code); // Instant services - tidak eligible untuk diskon $instant_services = ['instant', 'grab_instant', 'gojek_instant', 'gosend', 'grab_express']; // Same day services - tidak eligible untuk diskon $same_day_services = ['same_day', 'sameday', 'sd', 'grab_sameday', 'gojek_sameday']; // Next day services - eligible untuk diskon $nextday_services = ['next_day', 'nextday', 'oke', 'yes', 'overnight']; // Regular services - eligible untuk diskon $regular_services = ['reg', 'regular', 'standard', 'ctc', 'jtr', 'economy', 'yakin']; if (in_array($service_lower, $instant_services)) { return 'instant'; } elseif (in_array($service_lower, $same_day_services)) { return 'same_day'; } elseif (in_array($service_lower, $nextday_services)) { return 'next_day'; } else { return 'regular'; } } public function set_voucher() { $voucher_code = $this->security->xss_clean($this->input->post('voucher')); $response = [ 'success' => false, 'message' => 'Voucher tidak ditemukan', 'voucher' => null ]; // Validasi voucher if (empty($voucher_code)) { $response['message'] = 'Voucher code is required.'; echo json_encode($response); return; } // Retrieve voucher $voucher = $this->db->select('*') ->from('vouchers') ->where('voucher_code', $voucher_code) ->get() ->row(); // Invalid voucher if (!$voucher) { $response['message'] = 'Voucher not found or not valid.'; echo json_encode($response); return; } // Validasi berbagai jenis voucher $validation_result = ['valid' => false, 'message' => '']; switch ($voucher->voucher_type) { case 'normal promo': $validation_result = $this->_validate_normal_promo($voucher); break; case 'birthday promo': $validation_result = $this->_validate_birth_promo($voucher); break; case 'time promo': $validation_result = $this->_validate_time_promo($voucher); break; case 'product promo': $validation_result = $this->_validate_product_promo($voucher); break; case 'customer promo': $validation_result = $this->_validate_customer_promo($voucher); break; default: $validation_result = ['valid' => false, 'message' => 'Tipe voucher tidak didukung.']; } // Jika tidak valid, kembalikan pesan error spesifik if (!$validation_result['valid']) { $response['message'] = $validation_result['message']; echo json_encode($response); return; } // Jika valid, simpan informasi voucher ke session $this->session->set_userdata('chosen_voucher_code', $voucher->voucher_code); $this->session->set_userdata('chosen_voucher_type', $voucher->discount_type); $this->session->set_userdata('chosen_voucher_discount', $voucher->discount_value); $this->session->set_userdata('chosen_voucher_voucher_type', $voucher->voucher_type); // Jika product promo, simpan product_ids yang mendapat diskon if ($voucher->voucher_type == 'product promo' && !empty($voucher->productpromo)) { $this->session->set_userdata('promo_product_ids', $voucher->productpromo); } // Siapkan response sukses $response = [ 'success' => true, 'message' => 'Voucher applied successfully.', 'voucher' => [ 'code' => $voucher->voucher_code, 'name' => $voucher->voucher_name, 'discount_type' => $voucher->discount_type, 'discount_value' => $voucher->discount_value ] ]; echo json_encode($response); } private function _validate_normal_promo($voucher) { // Default result $result = ['valid' => true, 'message' => 'Voucher valid.']; return $result; } private function _validate_birth_promo($voucher) { $id_customer = (int) $this->session->userdata('customer')['customer_id']; $customer = $this->customer_m->get_customer($id_customer); // Default result $result = ['valid' => false, 'message' => 'Voucher tidak valid.']; // Validasi data pelanggan if (!$customer || empty($customer->birthday)) { $result['message'] = 'Data pelanggan tidak valid atau tanggal lahir tidak tersedia.'; return $result; } // Validasi bulan kelahiran $birth_month = (int) date('m', strtotime($customer->birthday)); $voucher_birth_month = (int) $voucher->birthmonth; // Validasi bulan kelahiran if ($birth_month !== $voucher_birth_month) { $result['message'] = 'Voucher hanya berlaku untuk bulan kelahiran tertentu.'; return $result; } // Validasi min_order $cart_total = $this->cart->total(); // Ambil total harga item di cart if (!is_null($voucher->min_order) && $cart_total < $voucher->min_order) { $result['message'] = "Minimal order: IDR " . number_format($voucher->min_order, 0, ',', '.'); return $result; } // Validasi maxqty_per_person if (!is_null($voucher->maxqty_per_person) && $voucher->maxqty_per_person > 0) { $redeemed_count = $this->db->where('customer_id', $id_customer) ->where('redeemed_voucher_code', $voucher->voucher_code) ->count_all_results('orders'); if ($redeemed_count >= $voucher->maxqty_per_person) { $result['message'] = 'Anda sudah pernah menggunakan voucher ini.'; return $result; } } // Jika semua validasi berhasil $result['valid'] = true; $result['message'] = 'Voucher valid.'; return $result; } private function _validate_time_promo($voucher) { $current_time = date('Y-m-d H:i:s'); $id_customer = (int) $this->session->userdata('customer')['customer_id']; // Default result $result = ['valid' => false, 'message' => 'Voucher tidak valid.']; // Validasi waktu promo if (!($current_time >= $voucher->promostart && $current_time <= $voucher->promoend)) { $result['message'] = 'Waktu promo tidak valid.'; return $result; } // Validasi Min Order $cart_total = $this->cart->total(); // Ambil total harga item di cart if (!is_null($voucher->min_order) && $cart_total < $voucher->min_order) { $result['message'] = "Minimal order: IDR " . number_format($voucher->min_order, 0, ',', '.'); return $result; } // Validasi max redeemed per person if (!is_null($voucher->maxqty_per_person) && $voucher->maxqty_per_person > 0) { $redeemed_count = $this->db->where('customer_id', $id_customer) ->where('redeemed_voucher_code', $voucher->voucher_code) ->count_all_results('orders'); if ($redeemed_count >= $voucher->maxqty_per_person) { $result['message'] = 'Anda sudah pernah menggunakan voucher ini.'; return $result; } } // Jika semua validasi berhasil $result['valid'] = true; $result['message'] = 'Voucher valid.'; return $result; } private function _validate_customer_promo($voucher) { $id_customer = (int) $this->session->userdata('customer')['customer_id']; $cart_total = $this->cart->total(); // Ambil total harga belanjaan pelanggan // Default result $result = ['valid' => false, 'message' => 'Voucher tidak valid.']; // Validasi customerpromo if (!empty($voucher->customerpromo)) { $promo_customer_ids = explode(',', $voucher->customerpromo); // Misalnya "101,102,103" $promo_customer_ids = array_map('trim', $promo_customer_ids); // Bersihkan spasi if (!in_array($id_customer, $promo_customer_ids)) { $result['message'] = 'Voucher hanya berlaku untuk pelanggan tertentu.'; return $result; } } // Validasi maxqty_per_person if (!empty($voucher->maxqty_per_person)) { // Cek apakah voucher sudah digunakan oleh customer $used_voucher_count = $this->db->select('count(*) as qty_used') ->from('orders') ->where('customer_id', $id_customer) ->where('redeemed_voucher_code', $voucher->voucher_code) ->get() ->row() ->qty_used; if ($used_voucher_count >= $voucher->maxqty_per_person) { $result['message'] = 'Voucher ini sudah mencapai batas penggunaan per pelanggan.'; return $result; } } // Validasi min_order if (!empty($voucher->min_order)) { if ($cart_total < $voucher->min_order) { $result['message'] = 'Minimal order untuk voucher ini adalah ' . number_format($voucher->min_order, 0, ',', '.'); return $result; } } // Jika validasi berhasil $result['valid'] = true; $result['message'] = 'Voucher valid.'; return $result; } private function _validate_product_promo($voucher) { $id_customer = (int) $this->session->userdata('customer')['customer_id']; $cart_items = $this->cart->contents(); $cart_total = $this->cart->total(); // Default result $result = ['valid' => false, 'message' => 'Voucher tidak valid.']; // Validasi maxqty_per_person if (!is_null($voucher->maxqty_per_person) && $voucher->maxqty_per_person > 0) { $redeemed_count = $this->db->where('customer_id', $id_customer) ->where('redeemed_voucher_code', $voucher->voucher_code) ->count_all_results('orders'); if ($redeemed_count >= $voucher->maxqty_per_person) { $result['message'] = 'Anda sudah mencapai batas penggunaan voucher ini.'; return $result; } } // Validasi min_order if (!is_null($voucher->min_order) && $cart_total < $voucher->min_order) { $result['message'] = 'Minimal pembelian untuk voucher ini adalah IDR ' . number_format($voucher->min_order, 0, ',', '.'); return $result; } // Validasi product_id di cart dengan voucher->productpromo if (!empty($voucher->productpromo)) { $promo_product_ids = explode(',', $voucher->productpromo); $promo_product_ids = array_map('trim', $promo_product_ids); $is_product_in_cart = false; foreach ($cart_items as $item) { if (in_array($item['product_id'], $promo_product_ids)) { $is_product_in_cart = true; break; } } if (!$is_product_in_cart) { $result['message'] = 'Voucher ini hanya berlaku untuk produk tertentu yang tidak ada di keranjang Anda.'; return $result; } } // Jika semua validasi berhasil $result['valid'] = true; $result['message'] = 'Voucher valid.'; return $result; } public function create_order_detail() { try { // Validate input existence if (!isset($_POST['orderDetailData'])) { throw new Exception('Order detail data is missing'); } // Decode the JSON string $order_details = json_decode($_POST['orderDetailData'], true); // Additional validation if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception('Invalid JSON format: ' . json_last_error_msg()); } if (!is_array($order_details) || empty($order_details)) { throw new Exception('Order details must be a non-empty array'); } // Extract the order ID from the first detail item for potential rollback $order_id = isset($order_details[0]['orderId']) ? $order_details[0]['orderId'] : null; if (!$order_id) { throw new Exception('Unable to determine order ID'); } // Start database transaction $this->db->trans_start(); // First, check stock availability for all items $insufficient_stock_items = []; $initial_stock_data = []; // Store initial stock for each product foreach ($order_details as $detail) { // Validate required fields $required_fields = ['orderId', 'productId', 'detailId', 'quantity', 'itemPrice', 'shippingMethodId', 'productName']; foreach ($required_fields as $field) { if (!isset($detail[$field]) || $detail[$field] === '') { throw new Exception("Missing required field: {$field}"); } } // Check if stock is sufficient $this->db->select('stock'); $this->db->where('id_product', $detail['productId']); $this->db->where('id_product_detail', $detail['detailId']); $this->db->where('warehouse_id', isset($detail['warehouseId']) ? $detail['warehouseId'] : 1); $stock_query = $this->db->get('stock'); if ($stock_query->num_rows() == 0) { $insufficient_stock_items[] = [ 'product_name' => $detail['productName'], 'error' => 'Stock data not found' ]; continue; } $stock_row = $stock_query->row(); // Store initial stock for each product $initial_stock_data[$detail['detailId']] = $stock_row->stock; if ($stock_row->stock < $detail['quantity']) { $insufficient_stock_items[] = [ 'product_name' => $detail['productName'], 'requested' => $detail['quantity'], 'available' => $stock_row->stock, 'error' => 'Insufficient stock' ]; } } // If any item has insufficient stock, abort the entire transaction if (!empty($insufficient_stock_items)) { // Delete the main order since we can't fulfill it $this->db->where('id_orders', $order_id); $this->db->delete('orders'); throw new Exception('Insufficient stock for some items', 422); } $success_count = 0; $error_details = []; // Process each order detail foreach ($order_details as $detail) { // Prepare insert data $insert_data = [ 'orders_id' => $detail['orderId'], 'product_id' => $detail['productId'], 'item_id' => $detail['detailId'], 'quantity' => $detail['quantity'], 'item_price' => $detail['itemPrice'], 'chosen_shipping_id' => $detail['shippingMethodId'], 'warehouse_id' => isset($detail['warehouseId']) ? $detail['warehouseId'] : 1, 'shipping_fee' => isset($detail['shippingFee']) ? $detail['shippingFee'] : 0, 'attribute_detail_ids' => isset($detail['attributeDetailId']) ? $detail['attributeDetailId'] : null, 'attributes' => isset($detail['attribute']) ? $detail['attribute'] : null, 'subtotal' => $detail['subtotal'], 'sku' => $detail['sku'], 'item_name' => $detail['productName'] ]; // Perform insert $insert_result = $this->db->insert('orders_detail', $insert_data); // Update Stock if ($insert_result) { $this->db->set('stock', 'stock - ' . (int)$detail['quantity'], false); $this->db->where('id_product', $detail['productId']); $this->db->where('id_product_detail', $detail['detailId']); $this->db->where('warehouse_id', isset($detail['warehouseId']) ? $detail['warehouseId'] : 1); $update_stock_result = $this->db->update('stock'); // Generate stock movement if ($update_stock_result) { // Get updated stock $this->db->select('id, stock'); $this->db->where('id_product', $detail['productId']); $this->db->where('id_product_detail', $detail['detailId']); $this->db->where('warehouse_id', isset($detail['warehouseId']) ? $detail['warehouseId'] : 1); $updated_stock = $this->db->get('stock')->row(); if ($updated_stock) { $stock_id = $updated_stock->id; $new_total = $updated_stock->stock; // Get initial stock for this specific product $old_stock = $initial_stock_data[$detail['detailId']]; // Insert stock movement $movement_data = [ 'stock_id' => $stock_id, 'type' => '-', 'stock_change' => $detail['quantity'], 'remark' => 'Sales Order No: ' . $detail['orderId'], 'total' => $new_total, 'name' => 'System', 'datetime' => date('Y-m-d H:i:s') ]; $insert_movement_result = $this->db->insert('stock_movement', $movement_data); // Log stock update with correct old and new stock $this->log_m->log_stock_update( $detail['orderId'], $detail['detailId'], $detail['productName'], $old_stock, $new_total, base_url('admin/products/stock-product?tab=all'), null, 'stock', // field 'orders' // record type ); if (!$insert_movement_result) { $db_error = $this->db->error(); $error_details[] = [ 'product_name' => $detail['productName'], 'error' => 'Failed to insert stock movement' ]; } } else { $error_details[] = [ 'product_name' => $detail['productName'], 'error' => 'Stock ID not found' ]; } $success_count++; } else { $db_error = $this->db->error(); $error_details[] = [ 'product_name' => $detail['productName'], 'error' => 'Failed to update stock' ]; } } else { $db_error = $this->db->error(); $error_details[] = [ 'product_name' => $detail['productName'], 'error' => $db_error ]; } } // Complete transaction $this->db->trans_complete(); // Prepare response $response = $this->db->trans_status() === FALSE ? [ 'status' => false, 'message' => 'Gagal menyimpan order detail', 'error_details' => $error_details ] : [ 'status' => true, 'message' => "Berhasil menyimpan {$success_count} order detail", 'total_processed' => count($order_details), 'success_count' => $success_count ]; // Return JSON response return $this->output ->set_content_type('application/json') ->set_output(json_encode($response)); } catch (Exception $e) { // Log the full exception // Rollback transaction if it's active if ($this->db->trans_status() !== FALSE) { $this->db->trans_rollback(); } // Return error response with appropriate status code $http_code = $e->getCode() >= 400 && $e->getCode() < 600 ? $e->getCode() : 500; $response = [ 'status' => false, 'message' => $e->getMessage(), 'error_code' => $e->getCode(), 'insufficient_stock_items' => isset($insufficient_stock_items) ? $insufficient_stock_items : [], ]; return $this->output ->set_content_type('application/json') ->set_status_header($http_code) ->set_output(json_encode($response)); } } /** * Menghapus data order berdasarkan ID * * Digunakan untuk rollback atau pembatalan pesanan ketika terjadi kesalahan * seperti stok tidak mencukupi * * @return JSON */ public function delete_order_data() { try { // Validate required parameters if (!isset($_POST['orderId']) || !is_numeric($_POST['orderId'])) { throw new Exception('Order ID is required and must be numeric'); } $order_id = (int)$_POST['orderId']; // Start database transaction $this->db->trans_start(); // Check if order exists $this->db->where('id_orders', $order_id); $order_exists = $this->db->get('orders')->num_rows() > 0; if (!$order_exists) { throw new Exception('Order not found with ID: ' . $order_id); } // Delete any related order details first (maintain referential integrity) $this->db->where('orders_id', $order_id); $delete_details_result = $this->db->delete('orders_detail'); $affected_details = $this->db->affected_rows(); // Delete the main order $this->db->where('id_orders', $order_id); $delete_order_result = $this->db->delete('orders'); $affected_orders = $this->db->affected_rows(); // Complete transaction $this->db->trans_complete(); // Check if deletion was successful if ($this->db->trans_status() === FALSE) { throw new Exception('Failed to delete order'); } // Return success response $response = [ 'status' => true, 'message' => 'Pesanan berhasil dihapus', 'deleted_order_id' => $order_id, 'affected_details' => $affected_details, 'affected_orders' => $affected_orders ]; return $this->output ->set_content_type('application/json') ->set_output(json_encode($response)); } catch (Exception $e) { // Rollback transaction if it's active if ($this->db->trans_status() !== FALSE) { $this->db->trans_rollback(); } // Return error response $response = [ 'status' => false, 'message' => $e->getMessage() ]; return $this->output ->set_content_type('application/json') ->set_status_header(500) ->set_output(json_encode($response)); } } public function insert_order_data() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['error' => 'Invalid request method.']); http_response_code(405); return; } $input = $this->input->post(); $payment_type = $input['paymentType']; $orderData = [ 'customer_id' => isset($input['customerId']) ? $input['customerId'] : null, 'total_amount' => isset($input['totalAmount']) ? $input['totalAmount'] : 0, 'order_date' => isset($input['orderDate']) ? $input['orderDate'] : date('Y-m-d'), 'recipient_name' => isset($input['recipientName']) ? $input['recipientName'] : null, 'address' => isset($input['address']) ? $input['address'] : null, 'subdistrict' => isset($input['subdistrict']) ? $input['subdistrict'] : null, 'district' => isset($input['district']) ? $input['district'] : null, 'province' => isset($input['province']) ? $input['province'] : null, 'postcode' => isset($input['postcode']) ? $input['postcode'] : null, 'phone' => isset($input['phone']) ? $input['phone'] : null, 'email' => isset($input['email']) ? $input['email'] : null, 'first' => isset($input['first']) ? $input['first'] : 0, 'country' => isset($input['country']) ? $input['country'] : null, 'shipping_fee' => isset($input['shippingFee']) ? $input['shippingFee'] : 0, 'created_by' => isset($input['createdBy']) ? $input['createdBy'] : null, 'customer_note' => isset($input['customerNote']) ? $input['customerNote'] : null, 'gift_receiver_name' => isset($input['giftRecipientName']) ? $input['giftRecipientName'] : null, 'gift_receiver_phone' => isset($input['giftRecipientPhone']) ? $input['giftRecipientPhone'] : null, 'insurance_status' => isset($input['insuranceStatus']) ? $input['insuranceStatus'] : 'No', 'insurance_cost' => isset($input['insuranceFee']) ? $input['insuranceFee'] : 0, 'referral' => isset($input['referral']) ? $input['referral'] : null, 'source' => isset($input['source']) ? $input['source'] : null, 'medium' => isset($input['medium']) ? $input['medium'] : null, 'campaign' => isset($input['campaign']) ? $input['campaign'] : null, 'order_language' => isset($input['orderLanguage']) ? $input['orderLanguage'] : 'indonesian', 'redeemed_voucher_code' => isset($input['redeemedVoucherCode']) ? $input['redeemedVoucherCode'] : null, 'redeemed_voucher_value' => isset($input['voucherRedeemedValue']) ? $input['voucherRedeemedValue'] : 0, 'redeemed_voucher_type' => isset($input['voucherRedeemedType']) ? $input['voucherRedeemedType'] : null, 'redeemed_voucher_amount' => isset($input['voucherRedeemedAmount']) ? $input['voucherRedeemedAmount'] : null, 'plus_reward' => isset($input['plusPointReward']) ? $input['plusPointReward'] : 0, 'minus_reward' => isset($input['minusPointReward']) ? $input['minusPointReward'] : 0, 'minus_reward_amount' => isset($input['pointRedeemedAmount']) ? $input['pointRedeemedAmount'] : 0, 'grand_total_amount' => isset($input['grandTotalAmount']) ? $input['grandTotalAmount'] : 0, 'total_downpayment' => isset($input['grandTotalAmount']) ? $input['grandTotalAmount'] : 0, 'payment_type' => isset($payment_type) ? $payment_type : 'not_yet', ]; $grandTotal = (int) $input['grandTotalAmount']; $this->db->trans_start(); try { $this->db->insert('orders', $orderData); if ($this->db->affected_rows() > 0) { $orderId = $this->db->insert_id(); $customerId = $input['customerId']; $minusReward = isset($input['minusPointReward']) ? (int)$input['minusPointReward'] : 0; // Validasi input if ($minusReward < 0) { throw new Exception('Invalid point reward value.'); } // Validasi customer ID if (empty($customerId) || !is_numeric($customerId)) { throw new Exception('Invalid customer ID.'); } // Update customer points hanya jika ada pengurangan if ($minusReward > 0) { // Mulai transaction untuk atomicity $this->db->trans_start(); try { // Cek customer dan current points dengan FOR UPDATE untuk avoid race condition $this->db->select('id_customers, current_pointreward'); $this->db->where('id_customers', $customerId); $query = $this->db->get('customers'); if ($query->num_rows() === 0) { throw new Exception('Customer not found.'); } $customer = $query->row(); $currentPoints = (int)$customer->current_pointreward; // Validasi apakah poin cukup if ($currentPoints < $minusReward) { throw new Exception('Insufficient points. Current: ' . $currentPoints . ', Required: ' . $minusReward); } // Lakukan update $newPoints = $currentPoints - $minusReward; $this->db->set('current_pointreward', $newPoints); $this->db->where('id_customers', $customerId); $this->db->update('customers'); // Cek apakah update berhasil if ($this->db->affected_rows() === 0) { throw new Exception('Failed to update customer points - no rows affected.'); } // Commit transaction $this->db->trans_complete(); if ($this->db->trans_status() === FALSE) { throw new Exception('Transaction failed while updating customer points.'); } } catch (Exception $e) { // Rollback jika ada error $this->db->trans_rollback(); throw $e; } } // Update customer is_first field $this->db->set('is_first', 'is_first + 1', FALSE); $this->db->where('id_customers', $customerId); $this->db->update('customers'); if ($this->db->affected_rows() === 0) { throw new Exception('Failed to update customer is_first field.'); } $name = 'System'; $recipient = $input['recipientName']; $description = "Pesanan Baru dari {$recipient} telah ditambahkan dengan Order ID {$orderId} senilai {$grandTotal}. Pesanan ini dibuat oleh {$name}."; $reference_url = base_url('admin/orders/manage-order?tab=pending'); $log_id = $this->log_m->log_order_create($orderId, $description, $reference_url); $this->log_m->send_order_notifications('CREATE_ORDER', $log_id, $description); echo json_encode([ 'message' => 'Order created successfully.', 'order_id' => $orderId ]); http_response_code(201); $this->db->trans_complete(); } else { echo json_encode(['error' => 'Failed to create order.']); http_response_code(500); } } catch (Exception $e) { $this->db->trans_rollback(); echo json_encode(['error' => 'Database error occurred:' . $e->getMessage()]); http_response_code(500); } } // Validator JSON public function test_etd() { return testCases(); } }