575 lines
18 KiB
PHP
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');
|
|
}
|
|
}
|