Initial commit: IT Nexus Web-App
This commit is contained in:
182
backend/src/controllers/warehouse.controller.js
Normal file
182
backend/src/controllers/warehouse.controller.js
Normal file
@@ -0,0 +1,182 @@
|
||||
const WarehouseLocation = require('../models/WarehouseLocation');
|
||||
const AssetMovement = require('../models/AssetMovement');
|
||||
const StockThreshold = require('../models/StockThreshold');
|
||||
const PurchaseOrder = require('../models/PurchaseOrder');
|
||||
const Asset = require('../models/Asset');
|
||||
const { getDatabase } = require('../config/database');
|
||||
|
||||
// ─── Locations ────────────────────────────────────────────────────────────────
|
||||
|
||||
exports.getLocations = (req, res) => {
|
||||
res.json(WarehouseLocation.getAll());
|
||||
};
|
||||
|
||||
exports.createLocation = (req, res) => {
|
||||
const { name, description } = req.body;
|
||||
if (!name) return res.status(400).json({ error: 'Name ist erforderlich' });
|
||||
try {
|
||||
const loc = WarehouseLocation.create({ name, description });
|
||||
res.status(201).json(loc);
|
||||
} catch (e) {
|
||||
if (e.message?.includes('UNIQUE')) return res.status(409).json({ error: 'Lagerort existiert bereits' });
|
||||
res.status(500).json({ error: e.message });
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateLocation = (req, res) => {
|
||||
const loc = WarehouseLocation.update(parseInt(req.params.id), req.body);
|
||||
if (!loc) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
res.json(loc);
|
||||
};
|
||||
|
||||
exports.deleteLocation = (req, res) => {
|
||||
const ok = WarehouseLocation.delete(parseInt(req.params.id));
|
||||
if (!ok) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
res.json({ success: true });
|
||||
};
|
||||
|
||||
// ─── Movements ────────────────────────────────────────────────────────────────
|
||||
|
||||
exports.getMovements = (req, res) => {
|
||||
const asset_id = req.query.asset_id ? parseInt(req.query.asset_id) : undefined;
|
||||
res.json(AssetMovement.getAll({ asset_id, limit: 500 }));
|
||||
};
|
||||
|
||||
exports.createMovement = (req, res) => {
|
||||
const { asset_id, type, to_location_id, assigned_user_id, ticket_id, reason, notes, new_status } = req.body;
|
||||
if (!asset_id || !type) return res.status(400).json({ error: 'asset_id und type sind erforderlich' });
|
||||
|
||||
const asset = Asset.getById(parseInt(asset_id));
|
||||
if (!asset) return res.status(404).json({ error: 'Asset nicht gefunden' });
|
||||
|
||||
const db = getDatabase();
|
||||
const updateAsset = db.transaction(() => {
|
||||
// Create movement record
|
||||
const movement = AssetMovement.create({
|
||||
asset_id,
|
||||
type,
|
||||
from_location_id: asset.location_id,
|
||||
to_location_id: to_location_id || null,
|
||||
assigned_user_id: assigned_user_id || null,
|
||||
ticket_id: ticket_id || null,
|
||||
reason: reason || '',
|
||||
notes: notes || '',
|
||||
performed_by: req.user.id,
|
||||
});
|
||||
|
||||
// Update asset location + status if needed
|
||||
const updates = {};
|
||||
if (to_location_id) updates.location_id = to_location_id;
|
||||
if (new_status) updates.status = new_status;
|
||||
if (assigned_user_id) {
|
||||
updates.assigned_to_user_id = assigned_user_id;
|
||||
updates.status = 'zugewiesen';
|
||||
}
|
||||
if (type === 'in') {
|
||||
updates.status = 'verfuegbar';
|
||||
updates.assigned_to_user_id = null;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
Asset.update(asset_id, updates, req.user.id);
|
||||
}
|
||||
|
||||
return movement;
|
||||
});
|
||||
|
||||
try {
|
||||
const movement = updateAsset();
|
||||
res.status(201).json(movement);
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: e.message });
|
||||
}
|
||||
};
|
||||
|
||||
// ─── Stock Thresholds ─────────────────────────────────────────────────────────
|
||||
|
||||
exports.getThresholds = (req, res) => {
|
||||
res.json(StockThreshold.getAll());
|
||||
};
|
||||
|
||||
exports.upsertThreshold = (req, res) => {
|
||||
const { category, min_stock, notify_email } = req.body;
|
||||
if (!category) return res.status(400).json({ error: 'Kategorie ist erforderlich' });
|
||||
const threshold = StockThreshold.upsert({ category, min_stock, notify_email });
|
||||
res.json(threshold);
|
||||
};
|
||||
|
||||
exports.deleteThreshold = (req, res) => {
|
||||
const ok = StockThreshold.delete(req.params.category);
|
||||
if (!ok) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
res.json({ success: true });
|
||||
};
|
||||
|
||||
exports.getStockViolations = (req, res) => {
|
||||
res.json(StockThreshold.getViolations());
|
||||
};
|
||||
|
||||
// ─── Purchase Orders ──────────────────────────────────────────────────────────
|
||||
|
||||
exports.getPurchaseOrders = (req, res) => {
|
||||
const { status } = req.query;
|
||||
res.json(PurchaseOrder.getAll({ status }));
|
||||
};
|
||||
|
||||
exports.createPurchaseOrder = (req, res) => {
|
||||
const { category, item_name, quantity, notes } = req.body;
|
||||
if (!category || !item_name) return res.status(400).json({ error: 'Kategorie und Artikelname sind erforderlich' });
|
||||
const order = PurchaseOrder.create({ category, item_name, quantity, notes, created_by: req.user.id });
|
||||
res.status(201).json(order);
|
||||
};
|
||||
|
||||
exports.updatePurchaseOrder = (req, res) => {
|
||||
const order = PurchaseOrder.update(parseInt(req.params.id), req.body);
|
||||
if (!order) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
res.json(order);
|
||||
};
|
||||
|
||||
exports.deletePurchaseOrder = (req, res) => {
|
||||
const ok = PurchaseOrder.delete(parseInt(req.params.id));
|
||||
if (!ok) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
res.json({ success: true });
|
||||
};
|
||||
|
||||
// ─── Dashboard Summary ────────────────────────────────────────────────────────
|
||||
|
||||
exports.getSummary = (req, res) => {
|
||||
const db = getDatabase();
|
||||
|
||||
const stockByLocation = db.prepare(`
|
||||
SELECT wl.name AS location, COUNT(a.id) AS count
|
||||
FROM warehouse_locations wl
|
||||
LEFT JOIN assets a ON a.location_id = wl.id AND a.status = 'verfuegbar'
|
||||
GROUP BY wl.id ORDER BY wl.name
|
||||
`).all();
|
||||
|
||||
const stockByCategory = db.prepare(`
|
||||
SELECT a.type AS category, a.status, COUNT(*) AS count
|
||||
FROM assets a
|
||||
GROUP BY a.type, a.status
|
||||
ORDER BY a.type, a.status
|
||||
`).all();
|
||||
|
||||
const violations = StockThreshold.getViolations();
|
||||
const openOrders = PurchaseOrder.getOpenCount();
|
||||
const recentMovements = AssetMovement.getAll({ limit: 10 });
|
||||
|
||||
res.json({ stockByLocation, stockByCategory, violations, openOrders, recentMovements });
|
||||
};
|
||||
|
||||
// ─── QR Code ──────────────────────────────────────────────────────────────────
|
||||
|
||||
exports.getQrCode = async (req, res) => {
|
||||
const asset = Asset.getById(parseInt(req.params.id));
|
||||
if (!asset) return res.status(404).json({ error: 'Asset nicht gefunden' });
|
||||
|
||||
const QRCode = require('qrcode');
|
||||
const url = `${process.env.FRONTEND_URL || 'https://it-nexus.cereda-systems.de'}/assets?scan=${asset.id}`;
|
||||
const svg = await QRCode.toString(url, { type: 'svg', width: 200, margin: 1 });
|
||||
|
||||
res.setHeader('Content-Type', 'image/svg+xml');
|
||||
res.send(svg);
|
||||
};
|
||||
Reference in New Issue
Block a user