Files
2026-06-13 16:02:07 +10:00

575 lines
18 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* @property CI_Loader $load
* @property CI_DB_query_builder $db
* @property CI_Session $session
* @property CI_Input $input
*/
class Marketplace extends CI_Controller
{
protected $db_sync;
public function __construct()
{
parent::__construct();
$this->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');
}
}