diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 790d52d..6cb1a7f 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -1547,46 +1547,62 @@ class FalukantService extends BaseService { } 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 f = branchId ? { id: branchId, falukantUserId: u.id } : { falukantUserId: u.id }; - const br = await Branch.findAll({ - where: f, - include: [ - { model: FalukantStock, as: 'stocks', include: [{ model: FalukantStockType, as: 'stockType' }] }, - { model: RegionData, as: 'region', include: [{ model: RegionType, as: 'regionType' }] } - ] - }); - const stockIds = br.flatMap(b => b.stocks.map(s => s.id)); - const inv = await Inventory.findAll({ - where: { stockId: stockIds }, - include: [ - { - model: FalukantStock, - as: 'stock', - include: [ - { - model: Branch, - as: 'branch', - include: [{ model: RegionData, as: 'region', include: [{ model: RegionType, as: 'regionType' }] }] - }, - { model: FalukantStockType, as: 'stockType' } - ] - }, - { model: ProductType, as: 'productType' } - ] - }); - 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; - }); + const branchIdInt = branchId == null ? null : parseInt(branchId, 10); + if (branchId != null && Number.isNaN(branchIdInt)) { + throw new Error('Invalid branchId'); + } + + const rows = await sequelize.query( + ` + SELECT + r.id AS region_id, + r.name AS region_name, + rt.id AS region_type_id, + 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 + `, + { + replacements: { falukantUserId: u.id, branchId: branchIdInt }, + type: sequelize.QueryTypes.SELECT + } + ); + + return (rows || []).map(r => ({ + 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 + }, + product: { + id: r.product_id, + labelTr: r.product_label_tr, + sellCost: r.product_sell_cost + }, + quality: r.quality, + totalQuantity: r.total_quantity + })); } async sellProduct(hashedUserId, branchId, productId, quality, quantity) {