Numex yayın: services/reportService.js
This commit is contained in:
parent
c03b1a771d
commit
546bfdb792
146
services/reportService.js
Normal file
146
services/reportService.js
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reportService.js
|
||||||
|
* Rapor hesaplama motoru — kategori bazlı raporlar, stok değerleri ve hareket özetleri.
|
||||||
|
* Saf fonksiyonlar; test edilebilir, route'tan bağımsız.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const productModel = require('../models/productModel');
|
||||||
|
const stockModel = require('../models/stockModel');
|
||||||
|
const categoryModel = require('../models/categoryModel');
|
||||||
|
|
||||||
|
/** ISO tarihini Date nesnesine güvenle çevirir (geçersizse boş aralık dönmez). */
|
||||||
|
function toDate(value) {
|
||||||
|
const d = value ? new Date(value) : null;
|
||||||
|
return d && !isNaN(d.getTime()) ? d : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Belirli bir kategoriye ait ürünlerin toplam stok değerini hesaplar.
|
||||||
|
* @param {number} categoryId kategori ID
|
||||||
|
* @returns {number} toplam değer (stock * unitPrice)
|
||||||
|
*/
|
||||||
|
function valueByCategory(categoryId) {
|
||||||
|
const categories = categoryModel.readAll();
|
||||||
|
const catId = Number(categoryId);
|
||||||
|
|
||||||
|
// id:0 / 'all' => tüm kategoriler
|
||||||
|
if (catId === 0 || String(categoryId).toLowerCase() === 'all') {
|
||||||
|
const products = productModel.readAll();
|
||||||
|
return products.reduce((sum, p) => sum + Number(p.stock) * Number(p.unitPrice), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cat = categories.find((c) => c.id === catId);
|
||||||
|
if (!cat) return 0;
|
||||||
|
|
||||||
|
const products = productModel.findByCategory(catId);
|
||||||
|
return products.reduce((sum, p) => sum + Number(p.stock) * Number(p.unitPrice), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tarih aralığında kategori ürünlerine ait giriş/çıkış özeti.
|
||||||
|
* @param {number|null} categoryId kategori ID (0/null = hepsi)
|
||||||
|
* @param {string} fromDate başlangıç tarihi (ISO)
|
||||||
|
* @param {string} toDate bitiş tarihi (ISO)
|
||||||
|
* @returns {Object} {in, out, net}
|
||||||
|
*/
|
||||||
|
function movementSummary(categoryId = null, fromDate = '', toDate = '') {
|
||||||
|
const from = toDate(fromDate);
|
||||||
|
const to = toDate(toDate);
|
||||||
|
|
||||||
|
// Kategoriye ait ürün ID'lerini bul
|
||||||
|
let productIds = new Set();
|
||||||
|
const catId = Number(categoryId);
|
||||||
|
if (catId && !isNaN(catId) && catId !== 0) {
|
||||||
|
productModel.findByCategory(catId).forEach((p) => productIds.add(Number(p.id)));
|
||||||
|
} else {
|
||||||
|
productModel.readAll().forEach((p) => productIds.add(Number(p.id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const movements = stockModel.readAll().filter((m) => {
|
||||||
|
if (!productIds.has(Number(m.productId))) return false;
|
||||||
|
const d = new Date(m.date);
|
||||||
|
if (from && d < from) return false;
|
||||||
|
if (to) {
|
||||||
|
// to bitiş gününü kapsar
|
||||||
|
const toEnd = new Date(to);
|
||||||
|
toEnd.setHours(23, 59, 59, 999);
|
||||||
|
if (d > toEnd) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
let inQty = 0;
|
||||||
|
let outQty = 0;
|
||||||
|
for (const m of movements) {
|
||||||
|
if (m.type === 'in') inQty += Number(m.quantity) || 0;
|
||||||
|
else if (m.type === 'out') outQty += Number(m.quantity) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { in: inQty, out: outQty, net: inQty - outQty, movements };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kategori bazlı tam rapor.
|
||||||
|
* @param {number|null} categoryId kategori ID
|
||||||
|
* @param {Object} opts {fromDate, toDate}
|
||||||
|
* @returns {Object} {category, totalProducts, totalStockValue, incomingQty, outgoingQty, movements, lowStockCount}
|
||||||
|
*/
|
||||||
|
function categoryReport(categoryId = null, opts = {}) {
|
||||||
|
const { fromDate = '', toDate = '' } = opts || {};
|
||||||
|
const catId = Number(categoryId);
|
||||||
|
const categories = categoryModel.readAll();
|
||||||
|
const category =
|
||||||
|
(catId && categories.find((c) => c.id === catId)) ||
|
||||||
|
(String(categoryId).toLowerCase() === 'all' ? { id: 0, name: 'Tümü' } : null);
|
||||||
|
|
||||||
|
const products = category && category.id !== 0
|
||||||
|
? productModel.findByCategory(catId)
|
||||||
|
: productModel.readAll();
|
||||||
|
|
||||||
|
const totalStockValue = products.reduce((s, p) => s + Number(p.stock) * Number(p.unitPrice), 0);
|
||||||
|
const summary = movementSummary(catId || 0, fromDate, toDate);
|
||||||
|
|
||||||
|
return {
|
||||||
|
category,
|
||||||
|
totalProducts: products.length,
|
||||||
|
totalStockValue,
|
||||||
|
incomingQty: summary.in,
|
||||||
|
outgoingQty: summary.out,
|
||||||
|
netQty: summary.net,
|
||||||
|
movements: summary.movements,
|
||||||
|
lowStockCount: products.filter(
|
||||||
|
(p) => Number(p.stock) <= (Number(p.minimumStock) || 10)
|
||||||
|
).length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tüm kategorilerin özet raporları (dashboard / rapor sayfası tablosu).
|
||||||
|
* @returns {Array} [{id, name, totalProducts, totalStockValue, lowStockCount}]
|
||||||
|
*/
|
||||||
|
function allCategorySummary() {
|
||||||
|
const categories = categoryModel.readAll();
|
||||||
|
return categories.map((cat) => {
|
||||||
|
const products = productModel.findByCategory(cat.id);
|
||||||
|
const totalStockValue = products.reduce((s, p) => s + Number(p.stock) * Number(p.unitPrice), 0);
|
||||||
|
return {
|
||||||
|
id: cat.id,
|
||||||
|
name: cat.name,
|
||||||
|
slug: cat.slug,
|
||||||
|
totalProducts: products.length,
|
||||||
|
totalStockValue,
|
||||||
|
lowStockCount: products.filter(
|
||||||
|
(p) => Number(p.stock) <= (Number(p.minimumStock) || 10)
|
||||||
|
).length
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
categoryReport,
|
||||||
|
valueByCategory,
|
||||||
|
movementSummary,
|
||||||
|
allCategorySummary
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user