836 lines
23 KiB
JavaScript
836 lines
23 KiB
JavaScript
const http = require('http');
|
||
const crypto = require('crypto');
|
||
const express = require('express');
|
||
const cors = require('cors');
|
||
const { Server } = require('socket.io');
|
||
|
||
const {
|
||
db,
|
||
initialize,
|
||
getUserByUsername,
|
||
createInventoryManager,
|
||
listInventoryManagers,
|
||
updateInventoryManager,
|
||
deleteInventoryManager,
|
||
createVehicle,
|
||
listVehicles,
|
||
updateVehicle,
|
||
deleteVehicle,
|
||
createUnit,
|
||
listUnits,
|
||
updateUnit,
|
||
deleteUnit,
|
||
createFuelPersonnel,
|
||
listFuelPersonnel,
|
||
updateFuelPersonnel,
|
||
deleteFuelPersonnel,
|
||
getVehicleById,
|
||
getUnitById,
|
||
getFuelPersonnelById,
|
||
getInventoryManagerById,
|
||
createFuelSlip,
|
||
listFuelSlips,
|
||
listFuelSlipsByInventoryManager,
|
||
getFuelSlipById,
|
||
updateFuelSlipStatus
|
||
} = require('./db');
|
||
|
||
const PDFDocument = require('pdfkit');
|
||
|
||
const app = express();
|
||
const PORT = process.env.PORT || 5005;
|
||
const sessions = new Map();
|
||
|
||
const httpServer = http.createServer(app);
|
||
const io = new Server(httpServer, {
|
||
cors: {
|
||
origin: '*'
|
||
}
|
||
});
|
||
|
||
const FUEL_FORCES = ['MSB', 'K.K.K.', 'Dz.K.K.', 'Hv.K.K.', 'SGK', 'Gnkur. Bşk.', 'Hrt.Gn.K.'];
|
||
|
||
const fuelManagerSockets = new Map();
|
||
const inventoryManagerSockets = new Map();
|
||
|
||
function addSocket(map, key, socket) {
|
||
if (!map.has(key)) {
|
||
map.set(key, new Set());
|
||
}
|
||
map.get(key).add(socket);
|
||
}
|
||
|
||
function removeSocket(map, key, socket) {
|
||
const set = map.get(key);
|
||
if (!set) {
|
||
return;
|
||
}
|
||
set.delete(socket);
|
||
if (set.size === 0) {
|
||
map.delete(key);
|
||
}
|
||
}
|
||
|
||
function emitToInventoryManager(id, event, payload) {
|
||
const set = inventoryManagerSockets.get(id);
|
||
if (!set) {
|
||
return;
|
||
}
|
||
for (const socket of set) {
|
||
socket.emit(event, payload);
|
||
}
|
||
}
|
||
|
||
function emitToFuelManager(id, event, payload) {
|
||
const set = fuelManagerSockets.get(id);
|
||
if (!set) {
|
||
return;
|
||
}
|
||
for (const socket of set) {
|
||
socket.emit(event, payload);
|
||
}
|
||
}
|
||
|
||
io.use((socket, next) => {
|
||
const token = socket.handshake.auth?.token || socket.handshake.query?.token;
|
||
|
||
if (!token || !sessions.has(token)) {
|
||
return next(new Error('Oturum bulunamadı.'));
|
||
}
|
||
|
||
socket.sessionToken = token;
|
||
socket.user = sessions.get(token);
|
||
return next();
|
||
});
|
||
|
||
io.on('connection', (socket) => {
|
||
const { user } = socket;
|
||
|
||
if (user.role === 'fuel_manager') {
|
||
addSocket(fuelManagerSockets, user.id, socket);
|
||
} else if (user.role === 'inventory_manager') {
|
||
addSocket(inventoryManagerSockets, user.id, socket);
|
||
}
|
||
|
||
socket.on('disconnect', () => {
|
||
if (user.role === 'fuel_manager') {
|
||
removeSocket(fuelManagerSockets, user.id, socket);
|
||
} else if (user.role === 'inventory_manager') {
|
||
removeSocket(inventoryManagerSockets, user.id, socket);
|
||
}
|
||
});
|
||
});
|
||
|
||
app.use(cors());
|
||
app.use(express.json());
|
||
|
||
function createSession(user) {
|
||
const token = crypto.randomBytes(24).toString('hex');
|
||
const session = {
|
||
id: user.id,
|
||
username: user.username,
|
||
role: user.role,
|
||
displayName: user.displayName
|
||
};
|
||
sessions.set(token, session);
|
||
return { token, session };
|
||
}
|
||
|
||
function requireSession(req, res, next) {
|
||
const token = req.header('x-session-token');
|
||
|
||
if (!token || !sessions.has(token)) {
|
||
return res.status(401).json({ message: 'Oturum bulunamadi.' });
|
||
}
|
||
|
||
req.session = sessions.get(token);
|
||
req.sessionToken = token;
|
||
return next();
|
||
}
|
||
|
||
function requireAdmin(req, res, next) {
|
||
if (req.session.role !== 'admin') {
|
||
return res.status(403).json({ message: 'Bu islem icin yetki yok.' });
|
||
}
|
||
|
||
return next();
|
||
}
|
||
|
||
function requireFuelManager(req, res, next) {
|
||
if (req.session.role !== 'fuel_manager') {
|
||
return res.status(403).json({ message: 'Bu islem icin yetki yok.' });
|
||
}
|
||
|
||
return next();
|
||
}
|
||
|
||
function requireInventoryManager(req, res, next) {
|
||
if (req.session.role !== 'inventory_manager') {
|
||
return res.status(403).json({ message: 'Bu islem icin yetki yok.' });
|
||
}
|
||
|
||
return next();
|
||
}
|
||
|
||
app.post('/api/auth/login', async (req, res) => {
|
||
const { username, password } = req.body || {};
|
||
|
||
if (!username || !password) {
|
||
return res.status(400).json({ message: 'Kullanici adi ve sifre zorunlu.' });
|
||
}
|
||
|
||
try {
|
||
const user = await getUserByUsername(username);
|
||
|
||
if (!user || user.password !== password) {
|
||
return res.status(401).json({ message: 'Kullanici adi veya sifre hatali.' });
|
||
}
|
||
|
||
const { token, session } = createSession(user);
|
||
|
||
return res.json({
|
||
token,
|
||
user: session
|
||
});
|
||
} catch (err) {
|
||
console.error('Login hatasi:', err);
|
||
return res.status(500).json({ message: 'Beklenmeyen bir sorun olustu.' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/auth/logout', requireSession, (req, res) => {
|
||
sessions.delete(req.sessionToken);
|
||
return res.json({ message: 'Oturum kapatildi.' });
|
||
});
|
||
|
||
app.get('/api/session', requireSession, (req, res) => {
|
||
return res.json({
|
||
user: req.session
|
||
});
|
||
});
|
||
|
||
app.get('/api/inventory-managers', requireSession, requireAdmin, async (req, res) => {
|
||
try {
|
||
const managers = await listInventoryManagers();
|
||
return res.json({ managers });
|
||
} catch (err) {
|
||
console.error('Listeleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Mal sorumlulari okunamadi.' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/inventory-managers', requireSession, requireAdmin, async (req, res) => {
|
||
const { username, password, displayName } = req.body || {};
|
||
|
||
if (!username || !password || !displayName) {
|
||
return res
|
||
.status(400)
|
||
.json({ message: 'Kullanici adi, sifre ve gorunen ad zorunlu alanlardir.' });
|
||
}
|
||
|
||
try {
|
||
const created = await createInventoryManager({ username, password, displayName });
|
||
return res.status(201).json({ manager: created });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu kullanici adi zaten mevcut.' });
|
||
}
|
||
|
||
console.error('Ekleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Mal sorumlusu eklenemedi.' });
|
||
}
|
||
});
|
||
|
||
app.put('/api/inventory-managers/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
const { displayName, password } = req.body || {};
|
||
|
||
if (!displayName && !password) {
|
||
return res.status(400).json({ message: 'Guncelleme icin en az bir alan girilmeli.' });
|
||
}
|
||
|
||
try {
|
||
await updateInventoryManager({ id, displayName, password });
|
||
return res.json({ message: 'Kayit guncellendi.' });
|
||
} catch (err) {
|
||
console.error('Mal sorumlusu guncelleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Guncelleme yapilamadi.' });
|
||
}
|
||
});
|
||
|
||
app.delete('/api/inventory-managers/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
|
||
try {
|
||
await deleteInventoryManager(id);
|
||
return res.status(204).end();
|
||
} catch (err) {
|
||
console.error('Mal sorumlusu silme hatasi:', err);
|
||
return res.status(500).json({ message: 'Silme islemi tamamlanamadi.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/vehicles', requireSession, requireAdmin, async (req, res) => {
|
||
try {
|
||
const vehicles = await listVehicles();
|
||
return res.json({ vehicles });
|
||
} catch (err) {
|
||
console.error('Arac listeleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Araçlar okunamadı.' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/vehicles', requireSession, requireAdmin, async (req, res) => {
|
||
const { brand, model, year, plate } = req.body || {};
|
||
|
||
if (!brand || !model || !year || !plate) {
|
||
return res.status(400).json({ message: 'Marka, model, yıl ve plaka zorunludur.' });
|
||
}
|
||
|
||
const parsedYear = Number(year);
|
||
|
||
if (!Number.isInteger(parsedYear) || parsedYear < 1990 || parsedYear > new Date().getFullYear() + 1) {
|
||
return res.status(400).json({ message: 'Lütfen geçerli bir model yılı girin.' });
|
||
}
|
||
|
||
try {
|
||
const vehicle = await createVehicle({
|
||
brand,
|
||
model,
|
||
year: parsedYear,
|
||
plate: plate.trim().toUpperCase()
|
||
});
|
||
return res.status(201).json({ vehicle });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu plaka ile kayıt zaten mevcut.' });
|
||
}
|
||
|
||
console.error('Arac ekleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Araç kaydedilemedi.' });
|
||
}
|
||
});
|
||
|
||
app.put('/api/vehicles/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
const { brand, model, year, plate } = req.body || {};
|
||
|
||
if (!brand || !model || !year || !plate) {
|
||
return res.status(400).json({ message: 'Tum alanlar zorunlusudur.' });
|
||
}
|
||
|
||
const parsedYear = Number(year);
|
||
if (!Number.isInteger(parsedYear) || parsedYear < 1990 || parsedYear > new Date().getFullYear() + 1) {
|
||
return res.status(400).json({ message: 'Gecerli bir model yılı girin.' });
|
||
}
|
||
|
||
try {
|
||
await updateVehicle({ id, brand, model, year: parsedYear, plate: plate.trim().toUpperCase() });
|
||
return res.json({ message: 'Araç kaydı güncellendi.' });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu plaka ile kayıt mevcut.' });
|
||
}
|
||
|
||
console.error('Arac guncelleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Guncelleme yapilamadi.' });
|
||
}
|
||
});
|
||
|
||
app.delete('/api/vehicles/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
|
||
try {
|
||
await deleteVehicle(id);
|
||
return res.status(204).end();
|
||
} catch (err) {
|
||
console.error('Arac silme hatasi:', err);
|
||
return res.status(500).json({ message: 'Silme islemi tamamlanamadi.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/units', requireSession, requireAdmin, async (req, res) => {
|
||
try {
|
||
const units = await listUnits();
|
||
return res.json({ units });
|
||
} catch (err) {
|
||
console.error('Birlik listeleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Birlikler okunamadı.' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/units', requireSession, requireAdmin, async (req, res) => {
|
||
const {
|
||
name,
|
||
address,
|
||
stk,
|
||
btk,
|
||
contactName,
|
||
contactRank,
|
||
contactRegistry,
|
||
contactIdentity,
|
||
contactPhone
|
||
} = req.body || {};
|
||
|
||
if (
|
||
!name ||
|
||
!address ||
|
||
!stk ||
|
||
!btk ||
|
||
!contactName ||
|
||
!contactRank ||
|
||
!contactRegistry ||
|
||
!contactIdentity ||
|
||
!contactPhone
|
||
) {
|
||
return res.status(400).json({ message: 'Birlik ve sorumlu bilgileri eksiksiz girilmeli.' });
|
||
}
|
||
|
||
try {
|
||
const unit = await createUnit({
|
||
name,
|
||
address,
|
||
stk,
|
||
btk,
|
||
contactName,
|
||
contactRank,
|
||
contactRegistry,
|
||
contactIdentity,
|
||
contactPhone
|
||
});
|
||
return res.status(201).json({ unit });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu birlik adı ile kayıt mevcut.' });
|
||
}
|
||
|
||
console.error('Birlik ekleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Birlik kaydedilemedi.' });
|
||
}
|
||
});
|
||
|
||
app.put('/api/units/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
const {
|
||
name,
|
||
address,
|
||
stk,
|
||
btk,
|
||
contactName,
|
||
contactRank,
|
||
contactRegistry,
|
||
contactIdentity,
|
||
contactPhone
|
||
} = req.body || {};
|
||
|
||
if (
|
||
!name ||
|
||
!address ||
|
||
!stk ||
|
||
!btk ||
|
||
!contactName ||
|
||
!contactRank ||
|
||
!contactRegistry ||
|
||
!contactIdentity ||
|
||
!contactPhone
|
||
) {
|
||
return res.status(400).json({ message: 'Tum alanlar zorunludur.' });
|
||
}
|
||
|
||
try {
|
||
await updateUnit({
|
||
id,
|
||
name,
|
||
address,
|
||
stk,
|
||
btk,
|
||
contactName,
|
||
contactRank,
|
||
contactRegistry,
|
||
contactIdentity,
|
||
contactPhone
|
||
});
|
||
return res.json({ message: 'Birlik kaydı güncellendi.' });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu birlik adı ile kayıt mevcut.' });
|
||
}
|
||
|
||
console.error('Birlik guncelleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Birlik güncellenemedi.' });
|
||
}
|
||
});
|
||
|
||
app.delete('/api/units/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
|
||
try {
|
||
await deleteUnit(id);
|
||
return res.status(204).end();
|
||
} catch (err) {
|
||
console.error('Birlik silme hatasi:', err);
|
||
return res.status(500).json({ message: 'Silme islemi tamamlanamadi.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/fuel-personnel', requireSession, requireAdmin, async (req, res) => {
|
||
try {
|
||
const personnel = await listFuelPersonnel();
|
||
return res.json({ personnel });
|
||
} catch (err) {
|
||
console.error('Personel listeleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Yakıt personeli okunamadı.' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/fuel-personnel', requireSession, requireAdmin, async (req, res) => {
|
||
const { fullName, rank, registryNumber, identityNumber, phone } = req.body || {};
|
||
|
||
if (!fullName || !rank || !registryNumber || !identityNumber || !phone) {
|
||
return res.status(400).json({ message: 'Personel bilgileri eksiksiz girilmeli.' });
|
||
}
|
||
|
||
try {
|
||
const created = await createFuelPersonnel({
|
||
fullName,
|
||
rank,
|
||
registryNumber,
|
||
identityNumber,
|
||
phone
|
||
});
|
||
return res.status(201).json({ personnel: created });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu sicil numarası ile kayıt mevcut.' });
|
||
}
|
||
|
||
console.error('Personel ekleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Personel kaydedilemedi.' });
|
||
}
|
||
});
|
||
|
||
app.put('/api/fuel-personnel/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
const { fullName, rank, registryNumber, identityNumber, phone } = req.body || {};
|
||
|
||
if (!fullName || !rank || !registryNumber || !identityNumber || !phone) {
|
||
return res.status(400).json({ message: 'Tum alanlar zorunludur.' });
|
||
}
|
||
|
||
try {
|
||
await updateFuelPersonnel({
|
||
id,
|
||
fullName,
|
||
rank,
|
||
registryNumber,
|
||
identityNumber,
|
||
phone
|
||
});
|
||
return res.json({ message: 'Personel kaydı güncellendi.' });
|
||
} catch (err) {
|
||
if (err && err.code === 'SQLITE_CONSTRAINT') {
|
||
return res.status(409).json({ message: 'Bu sicil numarası ile kayıt mevcut.' });
|
||
}
|
||
|
||
console.error('Personel guncelleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Guncelleme yapilamadi.' });
|
||
}
|
||
});
|
||
|
||
app.delete('/api/fuel-personnel/:id', requireSession, requireAdmin, async (req, res) => {
|
||
const { id } = req.params;
|
||
|
||
try {
|
||
await deleteFuelPersonnel(id);
|
||
return res.status(204).end();
|
||
} catch (err) {
|
||
console.error('Personel silme hatasi:', err);
|
||
return res.status(500).json({ message: 'Silme islemi tamamlanamadi.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/fuel/resources', requireSession, requireFuelManager, async (req, res) => {
|
||
try {
|
||
const [vehicles, units, personnel, inventoryManagers] = await Promise.all([
|
||
listVehicles(),
|
||
listUnits(),
|
||
listFuelPersonnel(),
|
||
listInventoryManagers()
|
||
]);
|
||
|
||
return res.json({
|
||
forces: FUEL_FORCES,
|
||
vehicles,
|
||
units,
|
||
personnel,
|
||
inventoryManagers
|
||
});
|
||
} catch (err) {
|
||
console.error('Kaynaklar okunamadi:', err);
|
||
return res.status(500).json({ message: 'Referans verileri yüklenemedi.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/fuel-slips', requireSession, requireFuelManager, async (req, res) => {
|
||
try {
|
||
const slips = await listFuelSlips();
|
||
return res.json({ slips });
|
||
} catch (err) {
|
||
console.error('Fisi listeleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Fişler okunamadı.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/fuel-slips/assigned', requireSession, requireInventoryManager, async (req, res) => {
|
||
try {
|
||
const slips = await listFuelSlipsByInventoryManager(req.session.id);
|
||
return res.json({ slips });
|
||
} catch (err) {
|
||
console.error('Atanan fişleri listeleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Fişler okunamadı.' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/fuel-slips', requireSession, requireFuelManager, async (req, res) => {
|
||
const {
|
||
date,
|
||
force,
|
||
unitId,
|
||
vehicleId,
|
||
fuelAmountNumber,
|
||
fuelAmountText,
|
||
fuelType,
|
||
receiverId,
|
||
giverId,
|
||
receiverPhone,
|
||
giverPhone,
|
||
notes,
|
||
inventoryManagerId
|
||
} = req.body || {};
|
||
|
||
if (
|
||
!date ||
|
||
!force ||
|
||
!unitId ||
|
||
!vehicleId ||
|
||
!fuelAmountNumber ||
|
||
!fuelAmountText ||
|
||
!fuelType ||
|
||
!receiverId ||
|
||
!giverId ||
|
||
!inventoryManagerId
|
||
) {
|
||
return res.status(400).json({ message: 'Fiş oluşturmak için zorunlu alanları doldurun.' });
|
||
}
|
||
|
||
try {
|
||
const [unit, vehicle, receiver, giver, inventoryManager] = await Promise.all([
|
||
getUnitById(unitId),
|
||
getVehicleById(vehicleId),
|
||
getFuelPersonnelById(receiverId),
|
||
getFuelPersonnelById(giverId),
|
||
getInventoryManagerById(inventoryManagerId)
|
||
]);
|
||
|
||
if (!unit || !vehicle || !receiver || !giver || !inventoryManager) {
|
||
return res.status(404).json({ message: 'Referans kaydı bulunamadı.' });
|
||
}
|
||
|
||
const payload = {
|
||
date,
|
||
force,
|
||
unit: { id: unit.id, name: unit.name },
|
||
vehicle: {
|
||
id: vehicle.id,
|
||
description: `${vehicle.brand} ${vehicle.model}`.trim(),
|
||
plate: vehicle.plate
|
||
},
|
||
fuelAmountNumber: Number(fuelAmountNumber),
|
||
fuelAmountText,
|
||
fuelType,
|
||
receiver: {
|
||
id: receiver.id,
|
||
fullName: receiver.fullName,
|
||
rank: receiver.rank,
|
||
registryNumber: receiver.registryNumber,
|
||
phone: receiverPhone || receiver.phone
|
||
},
|
||
giver: {
|
||
id: giver.id,
|
||
fullName: giver.fullName,
|
||
rank: giver.rank,
|
||
registryNumber: giver.registryNumber,
|
||
phone: giverPhone || giver.phone
|
||
},
|
||
inventoryManager: {
|
||
id: inventoryManager.id,
|
||
displayName: inventoryManager.displayName
|
||
},
|
||
fuelManagerId: req.session.id,
|
||
notes: notes || null
|
||
};
|
||
|
||
const created = await createFuelSlip(payload);
|
||
const slip = await getFuelSlipById(created.id);
|
||
|
||
emitToInventoryManager(slip.inventoryManagerId, 'fuelSlip:new', slip);
|
||
emitToFuelManager(req.session.id, 'fuelSlip:status', slip);
|
||
|
||
return res.status(201).json({ slip });
|
||
} catch (err) {
|
||
console.error('Fiş olusturma hatasi:', err);
|
||
return res.status(500).json({ message: 'Fiş olusturulamadı.' });
|
||
}
|
||
});
|
||
|
||
app.patch('/api/fuel-slips/:id/status', requireSession, requireInventoryManager, async (req, res) => {
|
||
const { id } = req.params;
|
||
const { status, reason } = req.body || {};
|
||
|
||
if (!['approved', 'rejected'].includes(status)) {
|
||
return res.status(400).json({ message: 'Geçersiz durum değeri.' });
|
||
}
|
||
|
||
if (status === 'rejected' && (!reason || !reason.trim())) {
|
||
return res.status(400).json({ message: 'Red gerekçesi girilmeli.' });
|
||
}
|
||
|
||
try {
|
||
const slip = await getFuelSlipById(id);
|
||
|
||
if (!slip) {
|
||
return res.status(404).json({ message: 'Fiş bulunamadı.' });
|
||
}
|
||
|
||
if (slip.inventoryManagerId !== req.session.id) {
|
||
return res.status(403).json({ message: 'Bu fiş size atanmamış.' });
|
||
}
|
||
|
||
if (slip.status !== 'pending') {
|
||
return res.status(409).json({ message: 'Fiş durumu zaten güncellenmiş.' });
|
||
}
|
||
|
||
const trimmedReason = status === 'rejected' ? reason.trim() : null;
|
||
|
||
await updateFuelSlipStatus({ id, status, rejectionReason: trimmedReason });
|
||
const updated = await getFuelSlipById(id);
|
||
|
||
emitToFuelManager(updated.fuelManagerId, 'fuelSlip:status', updated);
|
||
emitToInventoryManager(updated.inventoryManagerId, 'fuelSlip:status', updated);
|
||
|
||
return res.json({ slip: updated });
|
||
} catch (err) {
|
||
console.error('Fiş durum güncelleme hatasi:', err);
|
||
return res.status(500).json({ message: 'Durum güncellenemedi.' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/fuel-slips/:id/pdf', requireSession, requireFuelManager, async (req, res) => {
|
||
const { id } = req.params;
|
||
|
||
try {
|
||
const slip = await getFuelSlipById(id);
|
||
if (!slip) {
|
||
return res.status(404).json({ message: 'Fiş bulunamadı.' });
|
||
}
|
||
|
||
res.setHeader('Content-Type', 'application/pdf');
|
||
res.setHeader('Content-Disposition', `inline; filename=akaryakit-senedi-${slip.slipNumber}.pdf`);
|
||
|
||
const doc = new PDFDocument({ size: 'A4', margin: 36 });
|
||
doc.pipe(res);
|
||
|
||
const title = 'TSK AKARYAKIT İKMAL SENEDİ';
|
||
doc.fontSize(18).text(title, { align: 'center', underline: true });
|
||
doc.moveDown(0.4);
|
||
doc.fontSize(11).text(`Seri No: ${slip.slipNumber}`, { align: 'center' });
|
||
doc.moveDown(0.6);
|
||
|
||
const startX = doc.page.margins.left;
|
||
let y = doc.y;
|
||
const tableWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
||
const rowHeight = 28;
|
||
|
||
const drawCell = (x, yPos, width, height) => {
|
||
doc.rect(x, yPos, width, height).stroke();
|
||
};
|
||
|
||
const writeCell = (x, yPos, width, label, value) => {
|
||
drawCell(x, yPos, width, rowHeight);
|
||
doc.fontSize(9).font('Helvetica-Bold').text(label, x + 6, yPos + 6, {
|
||
width: width - 12
|
||
});
|
||
if (value) {
|
||
doc.fontSize(10).font('Helvetica').text(value, x + 6, yPos + 14, {
|
||
width: width - 12
|
||
});
|
||
}
|
||
};
|
||
|
||
const halfWidth = tableWidth / 2;
|
||
|
||
writeCell(startX, y, halfWidth, 'Tarih', new Date(slip.slipDate).toLocaleDateString('tr-TR'));
|
||
writeCell(startX + halfWidth, y, halfWidth, 'Fiş No', String(slip.slipNumber).padStart(4, '0'));
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, tableWidth, 'Aracın Ait Olduğu Kuvvet', slip.force);
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, tableWidth, 'Aracın Ait Olduğu Birlik', slip.unitName);
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, tableWidth, 'Aracın Cinsi', slip.vehicleDescription);
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, tableWidth, 'Aracın Plakası', slip.plate);
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, halfWidth, 'Yakıt Miktarı (Rakam)', `${slip.fuelAmountNumber}`);
|
||
writeCell(startX + halfWidth, y, halfWidth, 'Yakıt Miktarı (Yazı)', slip.fuelAmountText);
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, halfWidth, 'Yakıt Cinsi', slip.fuelType);
|
||
writeCell(startX + halfWidth, y, halfWidth, 'Not', slip.notes || '-');
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, halfWidth, 'Teslim Alan', `${slip.receiverName}\n${slip.receiverRank} / ${slip.receiverRegistry}`);
|
||
writeCell(startX + halfWidth, y, halfWidth, 'Teslim Eden', `${slip.giverName}\n${slip.giverRank} / ${slip.giverRegistry}`);
|
||
y += rowHeight;
|
||
|
||
writeCell(startX, y, halfWidth, 'Teslim Alan Telefonu', slip.receiverPhone);
|
||
writeCell(startX + halfWidth, y, halfWidth, 'Teslim Eden Telefonu', slip.giverPhone);
|
||
y += rowHeight;
|
||
|
||
doc.moveDown(2);
|
||
doc.fontSize(9).text('Not: Seri numarası yıl + sıra şeklinde takip edilir. Bu fiş sistem üzerinden oluşturulmuştur.', {
|
||
align: 'center'
|
||
});
|
||
|
||
doc.end();
|
||
} catch (err) {
|
||
console.error('PDF olusturma hatasi:', err);
|
||
return res.status(500).json({ message: 'PDF oluşturulamadı.' });
|
||
}
|
||
});
|
||
|
||
initialize()
|
||
.then(() => {
|
||
httpServer.listen(PORT, () => {
|
||
console.log(`Sunucu ${PORT} portunda hazir.`);
|
||
});
|
||
})
|
||
.catch((err) => {
|
||
console.error('Sunucu baslatilamadi:', err);
|
||
process.exit(1);
|
||
});
|
||
|
||
process.on('SIGINT', () => {
|
||
console.log('Sunucu kapatiliyor...');
|
||
io.close();
|
||
db.close(() => {
|
||
httpServer.close(() => {
|
||
process.exit(0);
|
||
});
|
||
});
|
||
});
|