db_sync = $this->load->database('marketplace_sync', TRUE); } public function products() { $data['products'] = $this->db_sync ->select(' mp.*, pm.kode_barang, pm.qty_multiplier, pm.sync_stock, pm.sync_price, pm.is_active as mapping_active ') ->from('marketplace_products mp') ->join('product_mappings pm', 'pm.marketplace_product_id = mp.id', 'left') ->order_by('mp.id', 'DESC') ->get() ->result(); $this->load->view('marketplace/products', $data); } public function sync_products($token_id = 1) { set_time_limit(300); ini_set('max_execution_time', 300); $url = 'https://dev.pc-master.biz/api/marketplace/' . $token_id . '/sync-products'; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, // CURLOPT_SSL_VERIFYPEER => false, // CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', 'X-API-KEY: pcmaster-marketplace-sync-2026', ], CURLOPT_CONNECTTIMEOUT => 15, CURLOPT_TIMEOUT => 300, ]); $response = curl_exec($ch); $error = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($error) { $this->session->set_flashdata('error', 'Curl error: ' . $error); redirect('marketplace/products'); return; } $result = json_decode($response, true); if ($httpCode >= 200 && $httpCode < 300 && !empty($result['success'])) { $this->session->set_flashdata('success', $result['message'] ?? 'Sync produk berhasil'); } else { $this->session->set_flashdata('error', $result['message'] ?? 'Sync produk gagal'); } redirect('marketplace/products'); } public function mapping($marketplace_product_id) { $product = $this->db_sync ->where('id', $marketplace_product_id) ->get('marketplace_products') ->row(); if (!$product) { show_error('Produk marketplace tidak ditemukan'); return; } $mapping = $this->db_sync ->where('marketplace_product_id', $marketplace_product_id) ->get('product_mappings') ->row(); $barang = $this->db ->select('ID_BARANG, KODE_BARANG, NAMA_BARANG') ->from('BARANG') ->order_by('NAMA_BARANG', 'ASC') ->get() ->result(); $data = [ 'product' => $product, 'mapping' => $mapping, 'barang' => $barang, ]; $this->load->view('marketplace/mapping', $data); } public function save_mapping() { $marketplace_product_id = $this->input->post('marketplace_product_id'); $kode_barang = $this->input->post('kode_barang'); $qty_multiplier = $this->input->post('qty_multiplier') ?: 1; $sync_stock = $this->input->post('sync_stock') ? 1 : 0; $sync_price = $this->input->post('sync_price') ? 1 : 0; $product = $this->db_sync ->where('id', $marketplace_product_id) ->get('marketplace_products') ->row(); if (!$product) { $this->session->set_flashdata('error', 'Produk marketplace tidak ditemukan'); redirect('marketplace/products'); return; } $barang = $this->db ->where('KODE_BARANG', $kode_barang) ->get('BARANG') ->row(); if (!$barang) { $this->session->set_flashdata('error', 'Barang lokal tidak ditemukan'); redirect('marketplace/mapping/' . $marketplace_product_id); return; } $exists = $this->db_sync ->where('marketplace_product_id', $marketplace_product_id) ->get('product_mappings') ->row(); $data = [ 'marketplace_product_id' => $marketplace_product_id, 'kode_barang' => $kode_barang, 'qty_multiplier' => $qty_multiplier, 'sync_stock' => $sync_stock, 'sync_price' => $sync_price, 'is_active' => 1, 'updated_at' => date('Y-m-d H:i:s'), ]; if ($exists) { $this->db_sync ->where('id', $exists->id) ->update('product_mappings', $data); } else { $data['created_at'] = date('Y-m-d H:i:s'); $this->db_sync->insert('product_mappings', $data); } $this->session->set_flashdata('success', 'Mapping produk berhasil disimpan'); redirect('marketplace/products'); } public function stock_queue_form($marketplace_product_id) { $product = $this->db_sync ->select(' mp.*, pm.id as mapping_id, pm.kode_barang, pm.qty_multiplier, pm.sync_stock, pm.is_active as mapping_active ') ->from('marketplace_products mp') ->join('product_mappings pm', 'pm.marketplace_product_id = mp.id', 'inner') ->where('mp.id', $marketplace_product_id) ->get() ->row(); if (!$product) { $this->session->set_flashdata('error', 'Produk belum mapping atau tidak ditemukan'); redirect('marketplace/products'); return; } $barang = $this->db ->select('ID_BARANG, KODE_BARANG, NAMA_BARANG') ->where('KODE_BARANG', $product->kode_barang) ->get('BARANG') ->row(); $local_stock = $this->get_local_stock($product->kode_barang); $qty_multiplier = (float) $product->qty_multiplier; if ($qty_multiplier <= 0) { $qty_multiplier = 1; } $marketplace_stock_target = floor($local_stock / $qty_multiplier); $data = [ 'product' => $product, 'barang' => $barang, 'local_stock' => $local_stock, 'marketplace_stock_target' => $marketplace_stock_target, ]; $this->load->view('marketplace/stock_queue_form', $data); } public function save_stock_queue() { $marketplace_product_id = $this->input->post('marketplace_product_id'); $product = $this->db_sync ->select(' mp.*, pm.id as mapping_id, pm.kode_barang, pm.qty_multiplier, pm.sync_stock, pm.is_active as mapping_active ') ->from('marketplace_products mp') ->join('product_mappings pm', 'pm.marketplace_product_id = mp.id', 'inner') ->where('mp.id', $marketplace_product_id) ->get() ->row(); if (!$product) { $this->session->set_flashdata('error', 'Mapping produk tidak ditemukan'); redirect('marketplace/products'); return; } if ((int) $product->sync_stock !== 1) { $this->session->set_flashdata('error', 'Sync stok untuk mapping ini tidak aktif'); redirect('marketplace/products'); return; } $local_stock = $this->get_local_stock($product->kode_barang); $qty_multiplier = (float) $product->qty_multiplier; if ($qty_multiplier <= 0) { $qty_multiplier = 1; } /* * Rumus: * stok marketplace = stok lokal / qty_multiplier */ $marketplace_stock_target = floor($local_stock / $qty_multiplier); $this->db_sync->insert('stock_sync_queues', [ 'product_mapping_id' => $product->mapping_id, 'kode_barang' => $product->kode_barang, 'marketplace' => $product->marketplace, 'local_stock' => $local_stock, 'marketplace_stock_before' => $product->stock, 'marketplace_stock_target' => $marketplace_stock_target, 'action' => 'push_to_marketplace', 'status' => 'pending', 'retry_count' => 0, 'max_retry' => 3, 'error_message' => null, 'processed_at' => null, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); $this->session->set_flashdata( 'success', 'Queue sinkron stok berhasil dibuat. Stok lokal: ' . $local_stock . ', target marketplace: ' . $marketplace_stock_target ); redirect('marketplace/stock_queues'); } public function stock_queues() { $data['queues'] = $this->db_sync ->select(' q.*, mp.product_name, mp.sku, mp.variant_name ') ->from('stock_sync_queues q') ->join('product_mappings pm', 'pm.id = q.product_mapping_id', 'left') ->join('marketplace_products mp', 'mp.id = pm.marketplace_product_id', 'left') ->order_by('q.id', 'DESC') ->get() ->result(); $this->load->view('marketplace/stock_queues', $data); } public function process_stock_queue() { $url = 'https://dev.pc-master.biz/api/stock/process-queue'; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_TIMEOUT => 60, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'Accept: application/json', 'X-API-KEY: pcmaster-marketplace-sync-2026', ], ]); $response = curl_exec($ch); $error = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($error) { $this->session->set_flashdata('error', 'Curl error: ' . $error); redirect('marketplace/stock_queues'); return; } $result = json_decode($response, true); if ($httpCode >= 200 && $httpCode < 300 && !empty($result['success'])) { $message = $result['message'] ?? 'Queue berhasil diproses'; if (isset($result['processed'])) { $message .= ' | Processed: ' . $result['processed']; } if (isset($result['failed'])) { $message .= ' | Failed: ' . $result['failed']; } $this->session->set_flashdata('success', $message); } else { $this->session->set_flashdata('error', $result['message'] ?? 'Proses queue gagal'); } redirect('marketplace/stock_queues'); } private function get_local_stock($kode_barang) { /* * Sesuaikan query ini dengan struktur stok lokal kamu. * Tabel BARANG yang kamu kirim belum punya kolom stok, * jadi kemungkinan stok ada di tabel lain. */ // CONTOH 1: // Kalau stok ada langsung di tabel BARANG dengan kolom STOK_BARANG /* $row = $this->db ->select('STOK_BARANG as stok') ->where('KODE_BARANG', $kode_barang) ->get('BARANG') ->row(); return $row ? (int) $row->stok : 0; */ // CONTOH 2: // Kalau stok ada di tabel STOK_BARANG /* $row = $this->db ->select('SUM(QTY) as stok') ->where('KODE_BARANG', $kode_barang) ->get('STOK_BARANG') ->row(); return $row ? (int) $row->stok : 0; */ // CONTOH 3: // Kalau stok berdasarkan ID_BARANG $barang = $this->db ->select('ID_BARANG, KODE_BARANG') ->where('KODE_BARANG', $kode_barang) ->get('BARANG') ->row(); if (!$barang) { return 0; } /* * GANTI bagian ini sesuai tabel stok kamu. * Misalnya nama tabel stok adalah STOK dan kolomnya BARANG_ID, QTY_STOK. */ $row = $this->db ->select('SUM(PCM_KUANTITI+ITP_KUANTITI) as stok') ->where('ID_KUANTITI', $barang->ID_BARANG) ->get('kuantiti') ->row(); return $row && $row->stok !== null ? (int) $row->stok : 0; } public function stock_logs() { $data['logs'] = $this->db_sync ->select(' l.*, mp.product_name, mp.sku ') ->from('stock_sync_logs l') ->join('product_mappings pm', 'pm.id = l.product_mapping_id', 'left') ->join('marketplace_products mp', 'mp.id = pm.marketplace_product_id', 'left') ->order_by('l.id', 'DESC') ->limit(100) ->get() ->result(); $this->load->view('marketplace/stock_logs', $data); } public function create_stock_queue($marketplace_product_id) { $product = $this->db_sync ->select(' mp.*, pm.id as mapping_id, pm.kode_barang, pm.qty_multiplier, pm.sync_stock, pm.is_active as mapping_active ') ->from('marketplace_products mp') ->join('product_mappings pm', 'pm.marketplace_product_id = mp.id', 'inner') ->where('mp.id', $marketplace_product_id) ->where('pm.is_active', 1) ->get() ->row(); if (!$product) { $this->session->set_flashdata('error', 'Produk belum mapping atau mapping tidak aktif'); redirect('marketplace/products'); return; } if ((int) $product->sync_stock !== 1) { $this->session->set_flashdata('error', 'Sync stok untuk produk ini tidak aktif'); redirect('marketplace/products'); return; } $local_stock = $this->get_local_stock($product->kode_barang); $qty_multiplier = (float) $product->qty_multiplier; if ($qty_multiplier <= 0) { $qty_multiplier = 1; } $marketplace_stock_target = floor($local_stock / $qty_multiplier); /* * Cek queue terakhir. * Kalau target stok masih sama dan status terakhir pending/processing/success, * maka tidak perlu membuat queue baru. */ $last_queue = $this->db_sync ->where('product_mapping_id', $product->mapping_id) ->order_by('id', 'DESC') ->get('stock_sync_queues') ->row(); if ( $last_queue && (int) $last_queue->marketplace_stock_target === (int) $marketplace_stock_target && in_array($last_queue->status, ['pending', 'processing', 'success']) ) { $this->session->set_flashdata( 'success', 'Queue tidak dibuat karena target stok masih sama. Stok lokal: ' . $local_stock . ', target marketplace: ' . $marketplace_stock_target ); redirect('marketplace/stock_queues'); return; } $this->db_sync->insert('stock_sync_queues', [ 'product_mapping_id' => $product->mapping_id, 'kode_barang' => $product->kode_barang, 'marketplace' => $product->marketplace, 'local_stock' => $local_stock, 'marketplace_stock_before' => $product->stock, 'marketplace_stock_target' => $marketplace_stock_target, 'action' => 'push_to_marketplace', 'status' => 'pending', 'retry_count' => 0, 'max_retry' => 3, 'error_message' => null, 'processed_at' => null, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); $this->session->set_flashdata( 'success', 'Queue stok berhasil dibuat. Stok lokal: ' . $local_stock . ', target marketplace: ' . $marketplace_stock_target ); redirect('marketplace/stock_queues'); } public function retry_failed_queue() { $url = 'https://dev.pc-master.biz/api/stock/retry-failed'; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_TIMEOUT => 60, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'Accept: application/json', 'X-API-KEY: pcmaster-marketplace-sync-2026', ], ]); $response = curl_exec($ch); $error = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($error) { $this->session->set_flashdata('error', 'Curl error: ' . $error); redirect('marketplace/stock_queues'); return; } $result = json_decode($response, true); if ($httpCode >= 200 && $httpCode < 300 && !empty($result['success'])) { $message = $result['message'] ?? 'Retry queue selesai'; if (isset($result['processed'])) { $message .= ' | Processed: ' . $result['processed']; } if (isset($result['failed'])) { $message .= ' | Failed: ' . $result['failed']; } $this->session->set_flashdata('success', $message); } else { $this->session->set_flashdata('error', $result['message'] ?? 'Retry queue gagal'); } redirect('marketplace/stock_queues'); } }