Optimize getInventory method in FalukantService by replacing nested includes with a single SQL query for improved performance. Add error handling for invalid branchId input.

This commit is contained in:
Torsten Schulz (local)
2025-12-20 16:13:33 +01:00
parent fbe0d1bcd1
commit c5804f764c

View File

@@ -1547,46 +1547,62 @@ class FalukantService extends BaseService {
} }
async getInventory(hashedUserId, branchId) { async getInventory(hashedUserId, branchId) {
// PERFORMANCE: Diese Route war langsam wegen doppelter/verschachtelter Includes (Branch->Stocks->Region + Inventory->Stock->Branch->Region).
// Wir holen stattdessen genau die benötigten Felder in EINER aggregierenden SQL-Query.
const u = await getFalukantUserOrFail(hashedUserId); const u = await getFalukantUserOrFail(hashedUserId);
const f = branchId ? { id: branchId, falukantUserId: u.id } : { falukantUserId: u.id }; const branchIdInt = branchId == null ? null : parseInt(branchId, 10);
const br = await Branch.findAll({ if (branchId != null && Number.isNaN(branchIdInt)) {
where: f, throw new Error('Invalid branchId');
include: [ }
{ model: FalukantStock, as: 'stocks', include: [{ model: FalukantStockType, as: 'stockType' }] },
{ model: RegionData, as: 'region', include: [{ model: RegionType, as: 'regionType' }] } const rows = await sequelize.query(
] `
}); SELECT
const stockIds = br.flatMap(b => b.stocks.map(s => s.id)); r.id AS region_id,
const inv = await Inventory.findAll({ r.name AS region_name,
where: { stockId: stockIds }, rt.id AS region_type_id,
include: [ rt.label_tr AS region_type_label_tr,
p.id AS product_id,
p.label_tr AS product_label_tr,
p.sell_cost AS product_sell_cost,
i.quality AS quality,
SUM(i.quantity)::int AS total_quantity
FROM falukant_data.inventory i
JOIN falukant_data.stock s ON s.id = i.stock_id
JOIN falukant_data.branch b ON b.id = s.branch_id
JOIN falukant_data.region r ON r.id = b.region_id
LEFT JOIN falukant_type.region rt ON rt.id = r.region_type_id
JOIN falukant_type.product p ON p.id = i.product_id
WHERE b.falukant_user_id = :falukantUserId
AND (:branchId::int IS NULL OR b.id = :branchId::int)
GROUP BY
r.id, r.name, rt.id, rt.label_tr,
p.id, p.label_tr, p.sell_cost,
i.quality
ORDER BY r.id, p.id, i.quality
`,
{ {
model: FalukantStock, replacements: { falukantUserId: u.id, branchId: branchIdInt },
as: 'stock', type: sequelize.QueryTypes.SELECT
include: [ }
{ );
model: Branch,
as: 'branch', return (rows || []).map(r => ({
include: [{ model: RegionData, as: 'region', include: [{ model: RegionType, as: 'regionType' }] }] region: {
id: r.region_id,
name: r.region_name,
regionType: r.region_type_id
? { id: r.region_type_id, labelTr: r.region_type_label_tr }
: null
}, },
{ model: FalukantStockType, as: 'stockType' } product: {
] id: r.product_id,
labelTr: r.product_label_tr,
sellCost: r.product_sell_cost
}, },
{ model: ProductType, as: 'productType' } quality: r.quality,
] totalQuantity: r.total_quantity
}); }));
const grouped = inv.reduce((acc, i) => {
const r = i.stock.branch.region;
const k = `${r.id}-${i.productType.id}-${i.quality}`;
acc[k] = acc[k] || { region: r, product: i.productType, quality: i.quality, totalQuantity: 0 };
acc[k].totalQuantity += i.quantity;
return acc;
}, {});
return Object.values(grouped).sort((a, b) => {
if (a.region.id !== b.region.id) return a.region.id - b.region.id;
if (a.product.id !== b.product.id) return a.product.id - b.product.id;
return a.quality - b.quality;
});
} }
async sellProduct(hashedUserId, branchId, productId, quality, quantity) { async sellProduct(hashedUserId, branchId, productId, quality, quantity) {