diff --git a/backend/src/dashboard/pages/sales_forecast.py b/backend/src/dashboard/pages/sales_forecast.py index a7ee684..8099a80 100644 --- a/backend/src/dashboard/pages/sales_forecast.py +++ b/backend/src/dashboard/pages/sales_forecast.py @@ -35,7 +35,7 @@ def weeks_between(start, end): # ----------------------------------------------------- -# Build Forecast DF +# Build Forecast DF (CORRECT PRICE × QUANTITY) # ----------------------------------------------------- def build_master_df(local=False) -> pd.DataFrame: if not local: @@ -58,15 +58,25 @@ def build_master_df(local=False) -> pd.DataFrame: if deal["company_info"]["name"] == "Apple": continue - price = next( + # -------- Match correct line item -------- + line_item = next( ( - float(item.get("price", 0)) + item for item in deal.get("line_items", []) if work_type.lower() in item.get("name", "").lower() ), - 0, + None, ) + if not line_item: + continue + + unit_price = float(line_item.get("price", 0)) + quantity = float(line_item.get("quantity", 1)) + + # 🔥 CRITICAL FIX + total_revenue = unit_price * quantity + start = pd.to_datetime( hubspot_data.to_date_only( deal["deal_properties"].get("expected_project_start_date") @@ -85,10 +95,13 @@ def build_master_df(local=False) -> pd.DataFrame: if pd.notna(end) and end >= start: weeks = weeks_between(start, end) - weekly_rev = price / len(weeks) if weeks else 0 + n = len(weeks) + weekly_revenue = total_revenue / n if n else 0 + weekly_quantity = quantity / n if n else 0 else: weeks = [pd.to_datetime(week_start_monday(start))] - weekly_rev = price + weekly_revenue = total_revenue + weekly_quantity = quantity for w in weeks: rows.append( @@ -98,12 +111,15 @@ def build_master_df(local=False) -> pd.DataFrame: "company_name": deal["company_info"]["name"], "work_type": work_type, "Planned Week": w.strftime("%Y-%m-%d"), - "revenue": weekly_rev, + "revenue": weekly_revenue, + "quantity": weekly_quantity, } ) df = pd.DataFrame(rows) df["revenue"] = pd.to_numeric(df["revenue"], errors="coerce").fillna(0) + df["quantity"] = pd.to_numeric(df["quantity"], errors="coerce").fillna(0) + return df @@ -117,26 +133,16 @@ layout = html.Div( html.H1("Sales Forecast", className="text-center"), html.Hr(), - # ---------------- SUMMARY TABLES ---------------- html.H3("Revenue by Work Type"), - dash_table.DataTable( - id="sf-worktype-table", - sort_action="native", - style_cell={"textAlign": "center"}, - ), + dash_table.DataTable(id="sf-worktype-table", sort_action="native"), html.Hr(), html.H3("Revenue by Company"), - dash_table.DataTable( - id="sf-company-table", - sort_action="native", - style_cell={"textAlign": "center"}, - ), + dash_table.DataTable(id="sf-company-table", sort_action="native"), html.Hr(), - # ---------------- WEEKLY FUTURE VIEW ---------------- html.H3("Weekly Planned Revenue (£)"), dash_table.DataTable( id="sf-weekly-table", @@ -149,7 +155,6 @@ layout = html.Div( dcc.Graph(id="sf-weekly-revenue-graph"), - # ---------------- MODAL (NAMESPACED) ---------------- dbc.Modal( [ dbc.ModalHeader("HubSpot Deals"), @@ -176,35 +181,33 @@ layout = html.Div( Output("sf-weekly-table", "data"), Output("sf-weekly-table", "columns"), Output("sf-weekly-revenue-graph", "figure"), - Input("sf-weekly-table", "id"), # run once + Input("sf-weekly-table", "id"), ) def build_outputs(_): - # -------- Revenue by work type -------- by_work = ( df.groupby("work_type") .agg( revenue=("revenue", "sum"), - jobs=("hubspot_id", "nunique"), + jobs=("quantity", "sum"), ) .reset_index() ) - # -------- Revenue by company -------- by_company = ( df.groupby("company_name") .agg( revenue=("revenue", "sum"), - jobs=("hubspot_id", "nunique"), + jobs=("quantity", "sum"), ) .reset_index() ) - # -------- Weekly pivot -------- pivot = ( df.groupby(["work_type", "Planned Week"]) .agg( revenue=("revenue", "sum"), + jobs=("quantity", "sum"), ids=("hubspot_id", lambda x: SAFE_DELIM.join(sorted(set(x)))), ) .reset_index() @@ -223,7 +226,6 @@ def build_outputs(_): weekly = revenue_tbl.merge(ids_tbl, on="Work Type", suffixes=("", "_ids")) - # -------- Weekly graph -------- weekly_rev = ( df.groupby("Planned Week")["revenue"] .sum() @@ -253,7 +255,7 @@ def build_outputs(_): # ----------------------------------------------------- -# Modal: HubSpot debug (SINGLE CALLBACK, NAMESPACED) +# Modal: HubSpot debug # ----------------------------------------------------- def id_to_link(deal_id): url = f"https://app.hubspot.com/contacts/145275138/record/0-3/{deal_id}" @@ -273,9 +275,7 @@ def id_to_link(deal_id): ) def open_modal(cell, close_click, table_data, is_open): - triggered = ctx.triggered_id - - if triggered == "sf-close-modal": + if ctx.triggered_id == "sf-close-modal": return False, "", None if not cell: @@ -296,6 +296,3 @@ def open_modal(cell, close_click, table_data, is_open): html.Ul([id_to_link(d) for d in ids.split(SAFE_DELIM)]), None, ) - - -# TODO: FOROGT TO ADD QUANTITY - IDIOT \ No newline at end of file