"""
Affordability Core — shared engine used by multiple modules.
Provides pure logic only; no MCP or user-interaction.
"""
from utils.date_utils import days_remaining
from utils.status import get_status
def check_affordability(
amount: float,
category_remaining: float,
total_remaining: float,
category_budget: float,
total_budget: float
) -> dict:
"""
Legacy compatibility function (simple mode).
Uses only remaining_after and percentage impact.
"""
remaining_after = total_remaining - amount
impact_pct = (amount / total_budget * 100) if total_budget else 0
return {
"affordable": remaining_after >= 0,
"remaining_after": remaining_after,
"impact_pct": impact_pct,
"impact_status": get_status(impact_pct),
"days_remaining": days_remaining(),
}
def check_affordability_logic(
amount: float,
category_remaining: float,
total_remaining: float,
category_budget: float,
total_budget: float,
category: str = None,
date: str = None
) -> dict:
"""
Full-featured affordability logic used by:
- modules/expenses
- modules/affordability
- modules/search
"""
remaining_after_total = total_remaining - amount
remaining_after_category = (
category_remaining - amount if category_remaining is not None else None
)
total_impact_pct = (amount / total_budget * 100) if total_budget else 0
category_impact_pct = (
(amount / category_budget * 100) if category_budget else None
)
result = {
"amount": amount,
"days_remaining": days_remaining(),
"category": category,
"date": date,
"remaining_after_total": remaining_after_total,
"remaining_after_category": remaining_after_category,
"total_impact_pct": total_impact_pct,
"total_impact_status": get_status(total_impact_pct),
"category_impact_pct": category_impact_pct,
"category_impact_status": (
get_status(category_impact_pct) if category_impact_pct is not None else None
),
"affordable_total": remaining_after_total >= 0,
"affordable_category": (
remaining_after_category >= 0 if remaining_after_category is not None else None
),
}
return result