OPEN-ISP v0.5 (pickle-lime) — rilis open-source perdana
Aplikasi billing & manajemen ISP (fiber optic) — PWA pelanggan, portal teknisi & kolektor, admin panel lengkap, multi-vendor OLT, RADIUS, WhatsApp/Telegram, pembayaran QRIS. Kredit & lisensi (ISC): - Base kode & repo: alijayanet/billing-rtrw oleh Alijaya Net — terima kasih banyak atas base kode dan repo yang bermanfaat. - Add-on: GoWA, LibreSpeed/OpenSpeedTest, Uptime Kuma, notification-forwarder. - Dikembangkan menjadi OPEN-ISP oleh oxpoe — https://github.com/oxpoe/open-isp
This commit is contained in:
commit
eaf05f2811
280 files changed
+123975
No files matched your search
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Service: Manajemen Inventaris / Gudang (Warehouse)
|
||||
*/
|
||||
const db = require('../config/database');
|
||||
const { logger } = require('../config/logger');
|
||||
|
||||
// ─── CATEGORIES ───────────────────────────────────────────────────────────
|
||||
function getAllCategories() {
|
||||
return db.prepare('SELECT * FROM inventory_categories ORDER BY name ASC').all();
|
||||
}
|
||||
|
||||
function createCategory(data) {
|
||||
const { name, description } = data;
|
||||
return db.prepare('INSERT INTO inventory_categories (name, description) VALUES (?, ?)').run(name, description);
|
||||
}
|
||||
|
||||
function updateCategory(id, data) {
|
||||
const { name, description } = data;
|
||||
return db.prepare('UPDATE inventory_categories SET name = ?, description = ? WHERE id = ?').run(name, description, id);
|
||||
}
|
||||
|
||||
function deleteCategory(id) {
|
||||
return db.prepare('DELETE FROM inventory_categories WHERE id = ?').run(id);
|
||||
}
|
||||
|
||||
// ─── ITEMS ───────────────────────────────────────────────────────────────
|
||||
function getAllItems(search = '') {
|
||||
let query = `
|
||||
SELECT i.*, c.name as category_name,
|
||||
(SELECT SUM(quantity) FROM inventory_stock s WHERE s.item_id = i.id AND s.status = 'available') as stock_available,
|
||||
(SELECT SUM(quantity) FROM inventory_stock s WHERE s.item_id = i.id AND s.status = 'assigned') as stock_assigned
|
||||
FROM inventory_items i
|
||||
LEFT JOIN inventory_categories c ON i.category_id = c.id
|
||||
`;
|
||||
const params = [];
|
||||
if (search) {
|
||||
query += ' WHERE i.name LIKE ? OR i.brand LIKE ? OR i.model LIKE ?';
|
||||
const s = `%${search}%`;
|
||||
params.push(s, s, s);
|
||||
}
|
||||
query += ' ORDER BY i.name ASC';
|
||||
return db.prepare(query).all(...params);
|
||||
}
|
||||
|
||||
function getItemById(id) {
|
||||
return db.prepare(`
|
||||
SELECT i.*, c.name as category_name
|
||||
FROM inventory_items i
|
||||
LEFT JOIN inventory_categories c ON i.category_id = c.id
|
||||
WHERE i.id = ?
|
||||
`).get(id);
|
||||
}
|
||||
|
||||
function createItem(data) {
|
||||
const { category_id, name, brand, model, unit, min_stock, description } = data;
|
||||
return db.prepare(`
|
||||
INSERT INTO inventory_items (category_id, name, brand, model, unit, min_stock, description)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(category_id, name, brand, model, unit, min_stock, description);
|
||||
}
|
||||
|
||||
function updateItem(id, data) {
|
||||
const { category_id, name, brand, model, unit, min_stock, description } = data;
|
||||
return db.prepare(`
|
||||
UPDATE inventory_items
|
||||
SET category_id = ?, name = ?, brand = ?, model = ?, unit = ?, min_stock = ?, description = ?
|
||||
WHERE id = ?
|
||||
`).run(category_id, name, brand, model, unit, min_stock, description, id);
|
||||
}
|
||||
|
||||
function deleteItem(id) {
|
||||
return db.prepare('DELETE FROM inventory_items WHERE id = ?').run(id);
|
||||
}
|
||||
|
||||
// ─── STOCK ───────────────────────────────────────────────────────────────
|
||||
function getStockByItem(itemId) {
|
||||
return db.prepare(`
|
||||
SELECT s.*, cust.name as customer_name
|
||||
FROM inventory_stock s
|
||||
LEFT JOIN customers cust ON s.assigned_to_customer_id = cust.id
|
||||
WHERE s.item_id = ?
|
||||
ORDER BY s.created_at DESC
|
||||
`).all(itemId);
|
||||
}
|
||||
|
||||
function addStock(data, actor = 'Admin') {
|
||||
const { item_id, serial_number, quantity, condition, location, note } = data;
|
||||
|
||||
const run = db.transaction(() => {
|
||||
const result = db.prepare(`
|
||||
INSERT INTO inventory_stock (item_id, serial_number, quantity, condition, location, note)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(item_id, serial_number || null, quantity, condition || 'new', location || 'Gudang Utama', note || '');
|
||||
|
||||
db.prepare(`
|
||||
INSERT INTO inventory_logs (item_id, stock_id, type, quantity, actor, note)
|
||||
VALUES (?, ?, 'in', ?, ?, ?)
|
||||
`).run(item_id, result.lastInsertRowid, quantity, actor, note || 'Stock Masuk');
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
return run();
|
||||
}
|
||||
|
||||
function assignStockToCustomer(stockId, customerId, actor = 'Admin', note = '') {
|
||||
const stock = db.prepare('SELECT * FROM inventory_stock WHERE id = ?').get(stockId);
|
||||
if (!stock) throw new Error('Stock tidak ditemukan');
|
||||
if (stock.status === 'assigned') throw new Error('Stock sudah terpasang di pelanggan lain');
|
||||
|
||||
const run = db.transaction(() => {
|
||||
db.prepare(`
|
||||
UPDATE inventory_stock
|
||||
SET status = 'assigned', assigned_to_customer_id = ?, updated_at = (NOW_LOCAL())
|
||||
WHERE id = ?
|
||||
`).run(customerId, stockId);
|
||||
|
||||
db.prepare(`
|
||||
INSERT INTO inventory_logs (item_id, stock_id, type, quantity, actor, note)
|
||||
VALUES (?, ?, 'out', ?, ?, ?)
|
||||
`).run(stock.item_id, stockId, stock.quantity, actor, note || `Terpasang ke pelanggan ID: ${customerId}`);
|
||||
});
|
||||
|
||||
return run();
|
||||
}
|
||||
|
||||
function adjustStock(stockId, newQuantity, note, actor = 'Admin') {
|
||||
const stock = db.prepare('SELECT * FROM inventory_stock WHERE id = ?').get(stockId);
|
||||
if (!stock) throw new Error('Stock tidak ditemukan');
|
||||
|
||||
const run = db.transaction(() => {
|
||||
const diff = newQuantity - stock.quantity;
|
||||
db.prepare('UPDATE inventory_stock SET quantity = ?, updated_at = (NOW_LOCAL()) WHERE id = ?').run(newQuantity, stockId);
|
||||
|
||||
db.prepare(`
|
||||
INSERT INTO inventory_logs (item_id, stock_id, type, quantity, actor, note)
|
||||
VALUES (?, ?, 'adjust', ?, ?, ?)
|
||||
`).run(stock.item_id, stockId, diff, actor, note || 'Penyesuaian Stock');
|
||||
});
|
||||
|
||||
return run();
|
||||
}
|
||||
|
||||
function getInventoryLogs(limit = 100) {
|
||||
return db.prepare(`
|
||||
SELECT l.*, i.name as item_name, s.serial_number
|
||||
FROM inventory_logs l
|
||||
LEFT JOIN inventory_items i ON l.item_id = i.id
|
||||
LEFT JOIN inventory_stock s ON l.stock_id = s.id
|
||||
ORDER BY l.created_at DESC
|
||||
LIMIT ?
|
||||
`).all(limit);
|
||||
}
|
||||
|
||||
function getLowStockItems() {
|
||||
return db.prepare(`
|
||||
SELECT i.*, c.name as category_name, SUM(s.quantity) as current_stock
|
||||
FROM inventory_items i
|
||||
LEFT JOIN inventory_categories c ON i.category_id = c.id
|
||||
LEFT JOIN inventory_stock s ON s.item_id = i.id AND s.status = 'available'
|
||||
GROUP BY i.id
|
||||
HAVING current_stock <= i.min_stock OR current_stock IS NULL
|
||||
`).all();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllCategories, createCategory, updateCategory, deleteCategory,
|
||||
getAllItems, getItemById, createItem, updateItem, deleteItem,
|
||||
getStockByItem, addStock, assignStockToCustomer, adjustStock,
|
||||
getInventoryLogs, getLowStockItems
|
||||
};
|
||||
Reference in new issue
Block a user