diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index 2e21ae9..e66aa30 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -2,6 +2,15 @@ ## [Non publié] +## [0.6.2] + +### Ajouté +- Tableau de budget : sous-totaux par section (dépenses, revenus, transferts) affichés après chaque groupe (#11) +- Rapport Budget vs Réel : sous-totaux par section avec réel, prévu, écart ($) et écart (%) par type (#11) + +### Corrigé +- Page catégories : le panneau de détail reste maintenant visible lors du défilement d'une longue liste de catégories (#12) + ## [0.6.1] ### Ajouté diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f0ef8..427e85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.6.2] + +### Added +- Budget table: section subtotals for expenses, income, and transfers displayed after each group (#11) +- Budget vs Actual report: section subtotals with actual, planned, variation ($) and variation (%) per type (#11) + +### Fixed +- Category page: detail panel now stays visible when scrolling through a long category list (#12) + ## [0.6.1] ### Added diff --git a/package.json b/package.json index b1ebbbd..016a496 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "simpl_result_scaffold", "private": true, - "version": "0.6.1", + "version": "0.6.2", "license": "GPL-3.0-only", "type": "module", "scripts": { diff --git a/public/CHANGELOG.fr.md b/public/CHANGELOG.fr.md index 2e21ae9..e66aa30 100644 --- a/public/CHANGELOG.fr.md +++ b/public/CHANGELOG.fr.md @@ -2,6 +2,15 @@ ## [Non publié] +## [0.6.2] + +### Ajouté +- Tableau de budget : sous-totaux par section (dépenses, revenus, transferts) affichés après chaque groupe (#11) +- Rapport Budget vs Réel : sous-totaux par section avec réel, prévu, écart ($) et écart (%) par type (#11) + +### Corrigé +- Page catégories : le panneau de détail reste maintenant visible lors du défilement d'une longue liste de catégories (#12) + ## [0.6.1] ### Ajouté diff --git a/public/CHANGELOG.md b/public/CHANGELOG.md index 12f0ef8..427e85d 100644 --- a/public/CHANGELOG.md +++ b/public/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.6.2] + +### Added +- Budget table: section subtotals for expenses, income, and transfers displayed after each group (#11) +- Budget vs Actual report: section subtotals with actual, planned, variation ($) and variation (%) per type (#11) + +### Fixed +- Category page: detail panel now stays visible when scrolling through a long category list (#12) + ## [0.6.1] ### Added diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3c2b5c8..ab0f3e8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "simpl-result" -version = "0.6.1" +version = "0.6.2" description = "Personal finance management app" license = "GPL-3.0-only" authors = ["you"] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index f277e9e..b6f7d48 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Simpl Resultat", - "version": "0.6.1", + "version": "0.6.2", "identifier": "com.simpl.resultat", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/components/budget/BudgetTable.tsx b/src/components/budget/BudgetTable.tsx index 949ed8f..b7740d0 100644 --- a/src/components/budget/BudgetTable.tsx +++ b/src/components/budget/BudgetTable.tsx @@ -184,6 +184,11 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu income: "budget.income", transfer: "budget.transfers", }; + const typeTotalKeys: Record = { + expense: "budget.totalExpenses", + income: "budget.totalIncome", + transfer: "budget.totalTransfers", + }; // Column totals with sign convention (only count leaf rows to avoid double-counting parents) const monthTotals: number[] = Array(12).fill(0); @@ -360,6 +365,16 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu {typeOrder.map((type) => { const group = grouped[type]; if (!group || group.length === 0) return null; + const sign = signFor(type); + const leaves = group.filter((r) => !r.is_parent); + const sectionMonthTotals: number[] = Array(12).fill(0); + let sectionAnnualTotal = 0; + for (const row of leaves) { + for (let m = 0; m < 12; m++) { + sectionMonthTotals[m] += row.months[m] * sign; + } + sectionAnnualTotal += row.annual * sign; + } return ( @@ -371,6 +386,17 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu {reorderRows(group, subtotalsOnTop).map((row) => renderRow(row))} + + + {t(typeTotalKeys[type])} + + {formatSigned(sectionAnnualTotal)} + {sectionMonthTotals.map((total, mIdx) => ( + + {formatSigned(total)} + + ))} + ); })} diff --git a/src/components/reports/BudgetVsActualTable.tsx b/src/components/reports/BudgetVsActualTable.tsx index 86c214b..37fdda8 100644 --- a/src/components/reports/BudgetVsActualTable.tsx +++ b/src/components/reports/BudgetVsActualTable.tsx @@ -105,6 +105,11 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) income: t("budget.income"), transfer: t("budget.transfers"), }; + const typeTotalKeys: Record = { + expense: "budget.totalExpenses", + income: "budget.totalIncome", + transfer: "budget.totalTransfers", + }; let currentType: SectionType | null = null; for (const row of data) { @@ -184,7 +189,22 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) - {sections.map((section) => ( + {sections.map((section) => { + const sectionLeaves = section.rows.filter((r) => !r.is_parent); + const sectionTotals = sectionLeaves.reduce( + (acc, r) => ({ + monthActual: acc.monthActual + r.monthActual, + monthBudget: acc.monthBudget + r.monthBudget, + monthVariation: acc.monthVariation + r.monthVariation, + ytdActual: acc.ytdActual + r.ytdActual, + ytdBudget: acc.ytdBudget + r.ytdBudget, + ytdVariation: acc.ytdVariation + r.ytdVariation, + }), + { monthActual: 0, monthBudget: 0, monthVariation: 0, ytdActual: 0, ytdBudget: 0, ytdVariation: 0 } + ); + const sectionMonthPct = sectionTotals.monthBudget !== 0 ? sectionTotals.monthVariation / Math.abs(sectionTotals.monthBudget) : null; + const sectionYtdPct = sectionTotals.ytdBudget !== 0 ? sectionTotals.ytdVariation / Math.abs(sectionTotals.ytdBudget) : null; + return ( @@ -236,8 +256,32 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) ); })} + + {t(typeTotalKeys[section.type])} + + {cadFormatter(sectionTotals.monthActual)} + + {cadFormatter(sectionTotals.monthBudget)} + + {cadFormatter(sectionTotals.monthVariation)} + + + {pctFormatter(sectionMonthPct)} + + + {cadFormatter(sectionTotals.ytdActual)} + + {cadFormatter(sectionTotals.ytdBudget)} + + {cadFormatter(sectionTotals.ytdVariation)} + + + {pctFormatter(sectionYtdPct)} + + - ))} + ); + })} {/* Grand totals */} {t("common.total")} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 69ef0c1..069bf54 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -325,6 +325,9 @@ "expenses": "Expenses", "income": "Income", "transfers": "Transfers", + "totalExpenses": "Total Expenses", + "totalIncome": "Total Income", + "totalTransfers": "Total Transfers", "totalPlanned": "Total Planned", "totalActual": "Total Actual", "totalDifference": "Difference", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index f819d88..d7e07d3 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -325,6 +325,9 @@ "expenses": "Dépenses", "income": "Revenus", "transfers": "Transferts", + "totalExpenses": "Total des dépenses", + "totalIncome": "Total des revenus", + "totalTransfers": "Total des transferts", "totalPlanned": "Total prévu", "totalActual": "Total réel", "totalDifference": "Écart", diff --git a/src/pages/CategoriesPage.tsx b/src/pages/CategoriesPage.tsx index 9d39703..568dc54 100644 --- a/src/pages/CategoriesPage.tsx +++ b/src/pages/CategoriesPage.tsx @@ -90,7 +90,7 @@ export default function CategoriesPage() { onRemove={removeKeyword} /> ) : ( -
+