Files
smanab/sarpras-sma-negeri-1-abiansemal/server/routes/inventory.js

335 lines
12 KiB
JavaScript

// Room Inventory (KIR) Routes
import express from 'express';
import pool, { generateId } from '../db.js';
const router = express.Router();
// POST /api/getRoomInventory
router.post('/getRoomInventory', async (req, res) => {
try {
const { roomName } = req.body;
// Get or create room
let [rooms] = await pool.query(
'SELECT id, room_name, location_code FROM room_inventory WHERE room_name = ?',
[roomName]
);
if (rooms.length === 0) {
// Create new room inventory
const [result] = await pool.query(
'INSERT INTO room_inventory (room_name) VALUES (?)',
[roomName]
);
rooms = [{ id: result.insertId, room_name: roomName, location_code: '' }];
}
const room = rooms[0];
// Get items
const [items] = await pool.query(
'SELECT * FROM inventory_items WHERE room_inventory_id = ? ORDER BY name',
[room.id]
);
const formattedItems = items.map(item => ({
id: item.id,
name: item.name,
brand: item.brand,
year: item.year,
code: item.code,
unit: item.unit,
price: parseFloat(item.price),
conditionGood: item.condition_good,
conditionLess: item.condition_less,
conditionBad: item.condition_bad,
notes: item.notes,
serialNumber: item.serial_number,
size: item.size,
material: item.material
}));
res.json({
status: 'success',
data: {
items: formattedItems,
locationCode: room.location_code || ''
}
});
} catch (error) {
console.error('Get room inventory error:', error);
res.json({ status: 'error', message: error.message });
}
});
// POST /api/saveRoomInventory
router.post('/saveRoomInventory', async (req, res) => {
try {
const { roomName, locationCode, items } = req.body;
// Get or create room
let [rooms] = await pool.query(
'SELECT id FROM room_inventory WHERE room_name = ?',
[roomName]
);
let roomId;
if (rooms.length === 0) {
const [result] = await pool.query(
'INSERT INTO room_inventory (room_name, location_code) VALUES (?, ?)',
[roomName, locationCode || '']
);
roomId = result.insertId;
} else {
roomId = rooms[0].id;
await pool.query(
'UPDATE room_inventory SET location_code = ? WHERE id = ?',
[locationCode || '', roomId]
);
}
// Delete existing items
await pool.query('DELETE FROM inventory_items WHERE room_inventory_id = ?', [roomId]);
// Insert new items
if (items && items.length > 0) {
for (const item of items) {
const itemId = item.id || generateId();
await pool.query(
`INSERT INTO inventory_items
(id, room_inventory_id, name, brand, year, code, unit, price,
condition_good, condition_less, condition_bad, notes, serial_number, size, material)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
itemId, roomId, item.name, item.brand || '', item.year || '',
item.code || '', item.unit || 'Buah', item.price || 0,
item.conditionGood || 0, item.conditionLess || 0, item.conditionBad || 0,
item.notes || '', item.serialNumber || '', item.size || '', item.material || ''
]
);
}
}
res.json({ status: 'success', message: 'Inventaris ruangan berhasil disimpan' });
} catch (error) {
console.error('Save room inventory error:', error);
res.json({ status: 'error', message: error.message });
}
});
// POST /api/lookupItemByQR
// Lookup an inventory item by code + year (from QR data format ITEM-{code}-{year}-{index})
router.post('/lookupItemByQR', async (req, res) => {
try {
const { code, year } = req.body;
if (!code || !year) {
return res.json({ status: 'error', message: 'Parameter code dan year wajib diisi.' });
}
const [items] = await pool.query(
`SELECT ii.*, ri.room_name
FROM inventory_items ii
JOIN room_inventory ri ON ii.room_inventory_id = ri.id
WHERE ii.code = ? AND ii.year = ?
LIMIT 10`,
[code, year]
);
if (items.length === 0) {
return res.json({ status: 'not_found', message: 'Barang tidak ditemukan.' });
}
const formattedItems = items.map(item => ({
id: item.id,
name: item.name,
brand: item.brand,
year: item.year,
code: item.code,
unit: item.unit,
price: parseFloat(item.price),
conditionGood: item.condition_good,
conditionLess: item.condition_less,
conditionBad: item.condition_bad,
notes: item.notes,
serialNumber: item.serial_number,
size: item.size,
material: item.material,
roomName: item.room_name
}));
res.json({ status: 'success', data: formattedItems });
} catch (error) {
console.error('Lookup item by QR error:', error);
res.json({ status: 'error', message: error.message });
}
});
// POST /api/updateItemCondition
// Update condition counts for a single inventory item
router.post('/updateItemCondition', async (req, res) => {
try {
const { itemId, conditionGood, conditionLess, conditionBad } = req.body;
if (!itemId) {
return res.json({ status: 'error', message: 'Parameter itemId wajib diisi.' });
}
// Verify item exists
const [existing] = await pool.query('SELECT id FROM inventory_items WHERE id = ?', [itemId]);
if (existing.length === 0) {
return res.json({ status: 'error', message: 'Barang tidak ditemukan.' });
}
await pool.query(
`UPDATE inventory_items
SET condition_good = ?, condition_less = ?, condition_bad = ?
WHERE id = ?`,
[conditionGood || 0, conditionLess || 0, conditionBad || 0, itemId]
);
res.json({ status: 'success', message: 'Kondisi barang berhasil diperbarui.' });
} catch (error) {
console.error('Update item condition error:', error);
res.json({ status: 'error', message: error.message });
}
});
// POST /api/getDamagedItems
// Get all items with condition_less > 0 or condition_bad > 0 in a specific room
router.post('/getDamagedItems', async (req, res) => {
try {
const { roomName } = req.body;
if (!roomName) {
return res.json({ status: 'error', message: 'Parameter roomName wajib diisi.' });
}
const [rooms] = await pool.query(
'SELECT id FROM room_inventory WHERE room_name = ?',
[roomName]
);
if (rooms.length === 0) {
return res.json({ status: 'success', data: [] });
}
const roomId = rooms[0].id;
const [items] = await pool.query(
`SELECT * FROM inventory_items
WHERE room_inventory_id = ? AND (condition_less > 0 OR condition_bad > 0)
ORDER BY name`,
[roomId]
);
const formattedItems = items.map(item => ({
id: item.id,
name: item.name,
brand: item.brand,
year: item.year,
code: item.code,
unit: item.unit,
price: parseFloat(item.price),
conditionGood: item.condition_good,
conditionLess: item.condition_less,
conditionBad: item.condition_bad,
notes: item.notes,
serialNumber: item.serial_number,
size: item.size,
material: item.material
}));
res.json({ status: 'success', data: formattedItems });
} catch (error) {
console.error('Get damaged items error:', error);
res.json({ status: 'error', message: error.message });
}
});
// POST /api/getInventoryConditionStats
// Get summary of inventory condition across ALL rooms for dashboard
router.post('/getInventoryConditionStats', async (req, res) => {
try {
const [rows] = await pool.query(
`SELECT
COUNT(*) as totalItems,
SUM(condition_good) as totalGood,
SUM(condition_less) as totalLess,
SUM(condition_bad) as totalBad,
SUM(CASE WHEN condition_less > 0 OR condition_bad > 0 THEN 1 ELSE 0 END) as itemsNeedRepair
FROM inventory_items`
);
const data = rows[0] || {};
res.json({
status: 'success',
data: {
totalItems: parseInt(data.totalItems) || 0,
totalGood: parseInt(data.totalGood) || 0,
totalLess: parseInt(data.totalLess) || 0,
totalBad: parseInt(data.totalBad) || 0,
itemsNeedRepair: parseInt(data.itemsNeedRepair) || 0
}
});
} catch (error) {
console.error('Get inventory condition stats error:', error);
res.json({ status: 'error', message: error.message });
}
});
// POST /api/getConditionByRoom
// Get condition breakdown grouped by room for dashboard charts
router.post('/getConditionByRoom', async (req, res) => {
try {
const [rows] = await pool.query(
`SELECT
ri.room_name,
SUM(ii.condition_good) as total_good,
SUM(ii.condition_less) as total_less,
SUM(ii.condition_bad) as total_bad,
COUNT(*) as item_count,
SUM(CASE WHEN ii.condition_less > 0 OR ii.condition_bad > 0 THEN 1 ELSE 0 END) as damaged_items
FROM inventory_items ii
JOIN room_inventory ri ON ii.room_inventory_id = ri.id
GROUP BY ri.room_name
ORDER BY (SUM(ii.condition_less) + SUM(ii.condition_bad)) DESC`
);
// Also get recent damaged items with room info
const [recentDamaged] = await pool.query(
`SELECT ii.name, ii.brand, ii.code, ii.condition_less, ii.condition_bad, ri.room_name
FROM inventory_items ii
JOIN room_inventory ri ON ii.room_inventory_id = ri.id
WHERE ii.condition_less > 0 OR ii.condition_bad > 0
ORDER BY (ii.condition_less + ii.condition_bad) DESC
LIMIT 10`
);
const roomData = rows.map(r => ({
roomName: r.room_name,
totalGood: parseInt(r.total_good) || 0,
totalLess: parseInt(r.total_less) || 0,
totalBad: parseInt(r.total_bad) || 0,
itemCount: parseInt(r.item_count) || 0,
damagedItems: parseInt(r.damaged_items) || 0
}));
const damagedList = recentDamaged.map(d => ({
name: d.name,
brand: d.brand || '',
code: d.code || '',
conditionLess: d.condition_less,
conditionBad: d.condition_bad,
roomName: d.room_name
}));
res.json({ status: 'success', data: { rooms: roomData, recentDamaged: damagedList } });
} catch (error) {
console.error('Get condition by room error:', error);
res.json({ status: 'error', message: error.message });
}
});
export default router;