Server Actions — Budget
Source : src/app/actions/budget.ts
getBudget()
Retourne le plan budget de l'utilisateur. Le crée automatiquement s'il n'existe pas encore.
export async function getBudget(): Promise<BudgetPlanWithRelations>
Comportement : cherche un BudgetPlan { userId, month: "reference" }. Si inexistant, le crée avec totalIncomeExpected = 0. Il y a donc toujours exactement un plan par utilisateur après ce premier appel.
Retour : plan avec incomeSources[] et allocations[] triés par order asc, puis id asc.
upsertIncomeSource(data)
Crée ou met à jour une source de revenus dans le plan.
export async function upsertIncomeSource(data: {
id?: string; // si fourni → update, sinon → create
budgetPlanId: string;
label: string;
type: IncomeType; // SALARY | FREELANCE | REIMBURSEMENT | INVESTMENT | OTHER
expectedMonthly: number;
color?: string; // défaut: "#10b981"
order?: number; // défaut: 0
}): Promise<UpsertResult>
Pré-conditions : budgetPlanId appartient à l'utilisateur courant.
Effets :
- Crée ou met à jour la source.
- Appelle
syncTotalIncome()→ recalculeBudgetPlan.totalIncomeExpected = Σ expectedMonthly. revalidatePath("/budget").
deleteIncomeSource(id)
Supprime une source de revenus et resynchronise le total.
export async function deleteIncomeSource(id: string): Promise<ActionResult>
Effets : suppression + syncTotalIncome() + revalidatePath("/budget").
upsertAllocation(data)
Crée ou met à jour une allocation budgétaire.
export async function upsertAllocation(data: {
id?: string;
budgetPlanId: string;
label: string;
type: AllocationType; // FIXED_EXPENSE | VARIABLE_EXPENSE | SAVINGS | INVESTMENT
targetAmount: number;
color?: string; // défaut: "#6366f1"
order?: number; // défaut: 0
}): Promise<UpsertResult>
Pré-conditions : budgetPlanId appartient à l'utilisateur courant.
Calcul automatique : targetPercent = (targetAmount / totalIncomeExpected) × 100. Si totalIncomeExpected = 0, targetPercent = 0.
Effets : revalidatePath("/budget").
deleteAllocation(id)
Supprime une allocation budgétaire.
export async function deleteAllocation(id: string): Promise<ActionResult>
Effets : revalidatePath("/budget").
Synchronisation totalIncomeExpected
La fonction interne syncTotalIncome(budgetPlanId) est appelée après chaque mutation de BudgetIncomeSource. Elle recalcule le total depuis la DB :
totalIncomeExpected = Σ incomeSources.expectedMonthly
Ce champ est donc toujours cohérent — il n'est jamais saisi directement.