mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 10:28:47 +02:00
Fix dashboard data findings from browser verification
- Próximas cuotas: headline is the upcoming billing batch (sum of every active plan's next cuota, using the rounding-absorbing last cuota when it's the pending one) — the in-cycle formula was ~always zero since cuotas bill on the 19th, day one of the next cycle. - useAgent must name the 'wealthysmart' agent (default crashed the page). - /transactions/recent excludes future-dated Tasa Cero cuotas. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -253,9 +253,15 @@ def recent_transactions(
|
|||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
_user: str = Depends(get_current_user),
|
_user: str = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
|
from app.timeutil import utcnow
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
select(Transaction)
|
select(Transaction)
|
||||||
.where(Transaction.source == TransactionSource.CREDIT_CARD)
|
.where(
|
||||||
|
Transaction.source == TransactionSource.CREDIT_CARD,
|
||||||
|
# Tasa Cero generates future-dated cuotas; "recent" means billed.
|
||||||
|
Transaction.date <= utcnow(),
|
||||||
|
)
|
||||||
.order_by(col(Transaction.date).desc(), col(Transaction.id).desc())
|
.order_by(col(Transaction.date).desc(), col(Transaction.id).desc())
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function Asistente() {
|
|||||||
// append it as a user message and run the agent once.
|
// append it as a user message and run the agent once.
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { agent } = useAgent();
|
const { agent } = useAgent({ agentId: "wealthysmart" });
|
||||||
const { copilotkit } = useCopilotKit();
|
const { copilotkit } = useCopilotKit();
|
||||||
const sentRef = useRef(false);
|
const sentRef = useRef(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -132,18 +132,21 @@ function ProximasCuotasCard() {
|
|||||||
const active = (q.data?.plans ?? []).filter(
|
const active = (q.data?.plans ?? []).filter(
|
||||||
(p) => !p.is_completed && p.next_cuota_date,
|
(p) => !p.is_completed && p.next_cuota_date,
|
||||||
);
|
);
|
||||||
const inCycle = active.filter((p) => {
|
// One cuota per plan per cycle, so the sum of every active plan's next
|
||||||
const d = new Date(p.next_cuota_date!);
|
// cuota is the load of the upcoming billing batch (next 19th). The last
|
||||||
return d >= CYCLE.start && d < CYCLE.end;
|
// cuota absorbs BAC's rounding, so use it when it's the one pending.
|
||||||
});
|
const nextCuotaOf = (p: (typeof active)[number]) =>
|
||||||
const cycleTotal = inCycle.reduce((s, p) => s + p.installment_amount, 0);
|
p.cuotas_billed === p.num_installments - 1
|
||||||
const upcoming = [...active]
|
? p.last_installment_amount
|
||||||
.sort(
|
: p.installment_amount;
|
||||||
(a, b) =>
|
const nextBatchTotal = active.reduce((s, p) => s + nextCuotaOf(p), 0);
|
||||||
new Date(a.next_cuota_date!).getTime() -
|
const upcoming = [...active].sort(
|
||||||
new Date(b.next_cuota_date!).getTime(),
|
(a, b) =>
|
||||||
)
|
new Date(a.next_cuota_date!).getTime() -
|
||||||
.slice(0, 3);
|
new Date(b.next_cuota_date!).getTime(),
|
||||||
|
);
|
||||||
|
const nextDate = upcoming[0]?.next_cuota_date;
|
||||||
|
const topThree = upcoming.slice(0, 3);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StatCardShell to="/financiamientos" ariaLabel="Ver financiamientos">
|
<StatCardShell to="/financiamientos" ariaLabel="Ver financiamientos">
|
||||||
@@ -151,21 +154,25 @@ function ProximasCuotasCard() {
|
|||||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||||
Próximas cuotas
|
Próximas cuotas
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>Tasa Cero en este ciclo</CardDescription>
|
<CardDescription>
|
||||||
|
{nextDate
|
||||||
|
? `Tasa Cero · próximo cobro ${formatShortDate(nextDate)}`
|
||||||
|
: "Tasa Cero"}
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2">
|
<CardContent className="space-y-2">
|
||||||
<StatValue
|
<StatValue
|
||||||
loading={q.isPending}
|
loading={q.isPending}
|
||||||
value={q.isError ? null : formatAmount(cycleTotal, "CRC")}
|
value={q.isError ? null : formatAmount(nextBatchTotal, "CRC")}
|
||||||
/>
|
/>
|
||||||
{upcoming.length > 0 && (
|
{topThree.length > 0 && (
|
||||||
<ul className="space-y-0.5 text-xs text-muted-foreground">
|
<ul className="space-y-0.5 text-xs text-muted-foreground">
|
||||||
{upcoming.map((p) => (
|
{topThree.map((p) => (
|
||||||
<li key={p.id} className="flex justify-between gap-2">
|
<li key={p.id} className="flex justify-between gap-2">
|
||||||
<span className="truncate">{p.merchant}</span>
|
<span className="truncate">{p.merchant}</span>
|
||||||
<span data-sensitive className="font-mono shrink-0">
|
<span data-sensitive className="font-mono shrink-0">
|
||||||
{formatShortDate(p.next_cuota_date!)} ·{" "}
|
{formatShortDate(p.next_cuota_date!)} ·{" "}
|
||||||
{formatAmount(p.installment_amount, p.currency)}
|
{formatAmount(nextCuotaOf(p), p.currency)}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user