diff --git a/backend/app/services/export_excel.py b/backend/app/services/export_excel.py index 387e719..93df8cc 100644 --- a/backend/app/services/export_excel.py +++ b/backend/app/services/export_excel.py @@ -64,10 +64,19 @@ async def export_ratecard_excel(db: AsyncSession, project: Project, efficiency_l gmals_result = await db.execute(select(GmalAsset).where(GmalAsset.id.in_(gmal_ids))) gmals = {g.id: g for g in gmals_result.scalars().all()} + # Load selected matches for caveat lookup + matches_result = await db.execute( + select(Match).where( + Match.client_asset_id.in_(asset_ids), + Match.is_selected == True, + ) + ) + caveat_by_asset = {m.client_asset_id: m.caveat_text or "" for m in matches_result.scalars().all()} + # Sheet 1: Ratecard Summary (roles x assets matrix) ws1 = wb.active ws1.title = "Ratecard Summary" - _build_ratecard_sheet(ws1, lines, roles, client_assets, gmals) + _build_ratecard_sheet(ws1, lines, roles, client_assets, gmals, caveat_by_asset) # Sheet 2: Asset Detail ws2 = wb.create_sheet("Asset Detail") @@ -86,8 +95,10 @@ async def export_ratecard_excel(db: AsyncSession, project: Project, efficiency_l return _workbook_to_bytes(wb) -def _build_ratecard_sheet(ws, lines, roles, client_assets, gmals): +def _build_ratecard_sheet(ws, lines, roles, client_assets, gmals, caveats: dict | None = None): """Build the main ratecard matrix: rows=roles, cols=client assets.""" + if caveats is None: + caveats = {} # Get unique sorted client assets and roles asset_ids_ordered = sorted(client_assets.keys()) role_ids_ordered = sorted(roles.keys(), key=lambda rid: (roles[rid].discipline, roles[rid].sort_order or 0)) @@ -127,9 +138,24 @@ def _build_ratecard_sheet(ws, lines, roles, client_assets, gmals): ws.cell(row=1, column=total_col, value="Total Hours").font = HEADER_FONT ws.cell(row=1, column=total_col).fill = HEADER_FILL + # Caveats row (row 2) + CAVEAT_FONT = Font(italic=True, size=9, color="555555") + CAVEAT_FILL = PatternFill(start_color="FFFBF0", end_color="FFFBF0", fill_type="solid") + ws.cell(row=2, column=1, value="").fill = CAVEAT_FILL + ws.cell(row=2, column=2, value="Assumptions / Caveats").font = Font(italic=True, bold=True, size=9, color="92400E") + ws.cell(row=2, column=2).fill = CAVEAT_FILL + for col_idx, asset_id in enumerate(asset_ids_ordered, 3): + caveat = caveats.get(asset_id, "") + cell = ws.cell(row=2, column=col_idx, value=caveat) + cell.font = CAVEAT_FONT + cell.fill = CAVEAT_FILL + cell.alignment = Alignment(wrap_text=True, vertical="top") + ws.cell(row=2, column=total_col).fill = CAVEAT_FILL + ws.row_dimensions[2].height = 60 + # Data rows current_discipline = None - row_idx = 2 + row_idx = 3 for role_id in role_ids_ordered: role = roles[role_id]