Skip to content

Commit a959ce5

Browse files
committed
waterfall
1 parent 94ffb98 commit a959ce5

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

app.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3347,6 +3347,110 @@ def _format_rupee_match(match):
33473347
story.append(Paragraph("No critical flags identified.", styles["BodyText"]))
33483348
story.append(Spacer(1, 12))
33493349

3350+
# ==========================================================================
3351+
# CASHFLOW WATERFALL (from Sentinel)
3352+
# ==========================================================================
3353+
cashflow_waterfall = analysis.get("cashflow_waterfall") or []
3354+
if cashflow_waterfall:
3355+
story.append(Paragraph("Cashflow Waterfall", styles["h2"]))
3356+
waterfall_rows = []
3357+
for item in cashflow_waterfall:
3358+
label = item.get("label", "")
3359+
formatted = item.get("formatted", "")
3360+
waterfall_rows.append([label, formatted])
3361+
3362+
if waterfall_rows:
3363+
wf_table = Table(
3364+
waterfall_rows,
3365+
hAlign="LEFT",
3366+
colWidths=[300, 200],
3367+
)
3368+
wf_table.setStyle(TableStyle([
3369+
('GRID', (0,0), (-1,-1), 0.5, colors.grey),
3370+
('BACKGROUND', (0,-1), (-1,-1), colors.HexColor('#E8F4EA')), # Highlight net available
3371+
('FONTNAME', (0,-1), (-1,-1), 'Helvetica-Bold'),
3372+
('BOTTOMPADDING', (0,0), (-1,-1), 4),
3373+
('TOPPADDING', (0,0), (-1,-1), 4),
3374+
]))
3375+
story.append(wf_table)
3376+
story.append(Spacer(1, 12))
3377+
3378+
# ==========================================================================
3379+
# ALLOCATION PRIORITIES (from Sentinel)
3380+
# ==========================================================================
3381+
allocation_priorities = analysis.get("allocation_priorities") or []
3382+
if allocation_priorities:
3383+
story.append(Paragraph("Recommended Fund Allocation", styles["h2"]))
3384+
priority_rows = []
3385+
for p in allocation_priorities:
3386+
priority = p.get("priority", "")
3387+
label = p.get("label", "")
3388+
formatted = p.get("formatted", "")
3389+
reasoning = p.get("reasoning", "")
3390+
priority_rows.append([f"P{priority}", label, formatted, reasoning])
3391+
3392+
if priority_rows:
3393+
pr_table = Table(
3394+
[["#", "Category", "Amount/mo", "Reason"]] + priority_rows,
3395+
hAlign="LEFT",
3396+
colWidths=[30, 120, 80, 270],
3397+
)
3398+
pr_table.setStyle(TableStyle([
3399+
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#457B9D')),
3400+
('TEXTCOLOR', (0,0), (-1,0), colors.white),
3401+
('GRID', (0,0), (-1,-1), 0.5, colors.grey),
3402+
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
3403+
('BOTTOMPADDING', (0,0), (-1,-1), 4),
3404+
('VALIGN', (0,0), (-1,-1), 'TOP'),
3405+
]))
3406+
story.append(pr_table)
3407+
story.append(Spacer(1, 12))
3408+
3409+
# ==========================================================================
3410+
# TAX EFFICIENCY (from Sentinel)
3411+
# ==========================================================================
3412+
tax_efficiency = analysis.get("tax_efficiency") or {}
3413+
tax_recommendations = tax_efficiency.get("recommendations") or []
3414+
total_tax_alpha = tax_efficiency.get("total_tax_alpha", 0)
3415+
3416+
if tax_recommendations:
3417+
story.append(Paragraph("Tax Savings Opportunities", styles["h2"]))
3418+
if total_tax_alpha > 0:
3419+
story.append(Paragraph(f"<b>Potential Tax Savings: Rs. {_format_indian_amount(total_tax_alpha)}</b>", styles["BodyText"]))
3420+
3421+
tax_rows = []
3422+
for rec in tax_recommendations:
3423+
section = rec.get("section", "")
3424+
gap = rec.get("formatted_gap", "")
3425+
savings = rec.get("formatted_savings", "")
3426+
deadline = rec.get("deadline", "")
3427+
tax_rows.append([section, gap, savings, f"by {deadline}"])
3428+
3429+
if tax_rows:
3430+
tax_table = Table(
3431+
[["Section", "Gap", "Tax Saved", "Deadline"]] + tax_rows,
3432+
hAlign="LEFT",
3433+
colWidths=[80, 100, 100, 220],
3434+
)
3435+
tax_table.setStyle(TableStyle([
3436+
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#2A9D8F')),
3437+
('TEXTCOLOR', (0,0), (-1,0), colors.white),
3438+
('GRID', (0,0), (-1,-1), 0.5, colors.grey),
3439+
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
3440+
('BOTTOMPADDING', (0,0), (-1,-1), 4),
3441+
]))
3442+
story.append(tax_table)
3443+
3444+
# LTCG Harvest Recommendation
3445+
ltcg = tax_efficiency.get("ltcg_harvest")
3446+
if ltcg:
3447+
story.append(Spacer(1, 6))
3448+
ltcg_rec = ltcg.get("recommendation", "")
3449+
if ltcg_rec:
3450+
story.append(Paragraph(f"<b>LTCG Opportunity:</b> {ltcg_rec}", styles["BodyText"]))
3451+
3452+
story.append(Spacer(1, 12))
3453+
33503454
# Categorized Recommendations
33513455
story.append(Paragraph("Recommendations", styles["h2"]))
33523456
for cat, items in categorized.items():
@@ -3369,6 +3473,7 @@ def _format_rupee_match(match):
33693473
"risk_rationale",
33703474
"goals_strategy",
33713475
"portfolio_rebalance",
3476+
"tax_efficiency",
33723477
]
33733478
for key in section_order:
33743479
sec = narratives.get(key)

0 commit comments

Comments
 (0)