From 231d49e3e8b89c4cd9006d52daeadfd37d8a82aa Mon Sep 17 00:00:00 2001 From: Kelly Date: Mon, 15 Dec 2025 12:02:36 -0700 Subject: [PATCH] feat(brands): Add margin estimation to stores/performance endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ?margin_pct query param (default 50% industry standard) - Returns margin_pct and margin_est per store - Includes margin_pct_assumed in response metadata 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- backend/src/routes/brands.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/src/routes/brands.ts b/backend/src/routes/brands.ts index eaaf1dc4..56b8d7fa 100644 --- a/backend/src/routes/brands.ts +++ b/backend/src/routes/brands.ts @@ -710,6 +710,8 @@ export function createBrandsRouter(pool: Pool): Router { const stateCode = req.query.state as string | undefined; const limit = parseLimit(req.query.limit as string, 100); const offset = parseOffset(req.query.offset as string); + // Margin assumption - default 50% (industry standard) + const marginPct = Math.min(Math.max(parseFloat(req.query.margin_pct as string) || 50, 0), 100); const startDate = new Date(); startDate.setDate(startDate.getDate() - days); @@ -879,11 +881,15 @@ export function createBrandsRouter(pool: Pool): Router { brand: brandName, period_days: days, state: stateCode || 'all', + margin_pct_assumed: marginPct, summary: { total_stores: parseInt(totals.total_stores) || 0, total_oos: parseInt(totals.total_oos) || 0, }, - stores: result.rows.map(row => ({ + stores: result.rows.map(row => { + const totalSalesEst = row.total_sales_est ? parseFloat(row.total_sales_est) : null; + const marginEst = totalSalesEst ? totalSalesEst * (marginPct / 100) : null; + return { store_id: row.store_id, store_name: row.store_name, state_code: row.state_code, @@ -896,7 +902,10 @@ export function createBrandsRouter(pool: Pool): Router { oos_pct: parseInt(row.oos_pct) || 0, // Velocity & Sales avg_daily_units: row.avg_daily_units ? parseFloat(row.avg_daily_units) : null, - total_sales_est: row.total_sales_est ? parseFloat(row.total_sales_est) : null, + total_sales_est: totalSalesEst, + // Margin (estimated) + margin_pct: marginPct, + margin_est: marginEst ? parseFloat(marginEst.toFixed(2)) : null, // Inventory avg_days_on_hand: row.avg_days_on_hand ? parseFloat(row.avg_days_on_hand) : null, total_stock: row.total_stock ? parseInt(row.total_stock) : null, @@ -906,7 +915,7 @@ export function createBrandsRouter(pool: Pool): Router { lost_opportunity: row.lost_opportunity ? parseFloat(row.lost_opportunity) : null, // Categories breakdown categories: row.categories || {}, - })), + };}), pagination: { limit, offset,