docs(balance): architecture + ADRs + user guide (#145) #153
5 changed files with 80 additions and 0 deletions
|
|
@ -3,6 +3,7 @@
|
|||
## [Non publié]
|
||||
|
||||
### Ajouté
|
||||
- **Bilan — documentation et ADRs** (`docs/`) : finalise le milestone Bilan avec la passe documentaire. `docs/architecture.md` répertorie désormais les 5 nouvelles tables (`balance_categories`, `balance_accounts`, `balance_snapshots`, `balance_snapshot_lines`, `balance_account_transfers`), les 7 nouveaux index, les invariants CHECK et FK (CAD seulement, invariants de type, `RESTRICT` sur `transaction_id` pour la reproductibilité Modified Dietz), le découpage 4 sections de `balance.service.ts` (CRUD / snapshots+lignes / rendements+transferts / prix), les 3 hooks scoped par page (`useBalanceAccounts`, `useSnapshotEditor`, `useBalanceOverview`), la commande Tauri `compute_account_return` (avec mention de la future commande `fetch_price` Phase 5), et les 3 nouvelles routes `/balance*`. Trois nouveaux ADRs accompagnent : **0008 — Modified Dietz** (justifie le choix vs ROI / TWR / IRR avec référence à `return_calculator.rs`) ; **0009 — Proxy price-fetching via maximus-api** (architecture documentée maintenant, implémentation BLOQUÉE en attendant la Phase 2 de maximus-api — couvre les considérations privacy comme le strip de headers, l'absence de corrélation `(symbole, licence)` dans les logs et le User-Agent fixe `simpl-resultat`, l'abstraction adapter Yahoo + CoinGecko, la stratégie d'auth Bearer, le rate-limiting client + serveur et le double gating premium UI + serveur) ; **0010 — FK RESTRICT sur `balance_account_transfers.transaction_id`** (justifie l'arbitrage intégrité vs friction pour la reproductibilité Modified Dietz). Le guide utilisateur gagne une nouvelle section *Bilan* qui détaille la saisie de snapshot (simple + coté), la liaison de transferts, la lecture des rendements multi-horizons (3M / 1A / depuis création avec colonne non-ajustée côte à côte), avec la mention « à venir Phase 5 » pour le price-fetching premium. Clés i18n `docs.balance.*` (FR + EN) ajoutées pour que le guide in-app reflète la nouvelle section (#145)
|
||||
- **Bilan — suite de tests d'intégration cross-cutting** (infrastructure de tests) : clôt la feature *Bilan* avec une couche de tests d'intégration qui exerce toute la surface TypeScript en un seul flux de bout en bout (compte → catégorie cotée → snapshot coté → transfert lié → rendement) et des assertions dédiées sur le verrou de devise (CAD seulement au MVP, refusé à la fois côté service et côté CHECK SQL), la sécurité de tolérance pour le type coté (un mauvais enregistrement ne doit PAS supprimer les lignes existantes), le câblage de `computeAccountReturn` (résolution du profil actif, transmission des dates ISO, conservation telle quelle d'une réponse de période partielle). Trois nouveaux tests Rust d'intégration appliquent la migration v9 par-dessus un schéma v1 seedé contenant déjà des transactions pour vérifier (1) aucune perte ni mutation de données, (2) le round-trip lier / délier sur de vraies `transaction_id`, (3) la chaîne FK RESTRICT (suppression d'une transaction liée bloquée, autorisée après détachement), (4) la cohabitation indépendante des espaces d'identifiants `categories.id` (v1) et `balance_categories.id` (v9). Un test de non-régression au niveau source sur `TransactionTable.tsx` verrouille le contrat de l'icône de transfert inlinée : prop optionnelle, court-circuit en chaînage optionnel, clés i18n, aria-label, layout partagé de la cellule description — pour que la page reste rendue à l'identique en l'absence de transferts liés. (#144)
|
||||
- **Bilan — rendements Modified Dietz et liaison de transferts** (route `/balance`) : le rendement par compte arrive enfin. Nouveau module Rust `commands/return_calculator.rs` qui implémente la formule Modified Dietz `R = (V_fin − V_début − ΣCF_i) / (V_début + ΣW_i × CF_i)` avec pondération des apports à la précision du jour `W_i = (T − t_i) / T`, et annualisation `(1 + R)^(365/T) − 1`. Les cas limites — snapshot d'extrémité manquant, aucun flux taggé sur la période, compte créé en cours de période, vidé puis rechargé, période de durée nulle — sont surfacés via les flags explicites `is_partial` / `has_no_transfers_warning` pour que l'UI affiche un tiret + tooltip clair plutôt qu'un nombre incompréhensible. Nouvelle commande Tauri `compute_account_return(account_id, period_start, period_end)` qui exécute trois lectures SQL courtes contre la BD du profil actif (dernier snapshot ≤ début de période, dernier snapshot ≤ fin de période, transferts joints aux transactions filtrés sur la période) puis alimente le calculateur. Sept tests Rust co-localisés en TDD couvrent chaque cas avant l'implémentation. Le tableau des comptes sur `/balance` affiche désormais quatre colonnes supplémentaires côte à côte : 3M / 1A / Depuis création (Modified Dietz) plus une colonne *Non ajusté* qui calcule simplement `(V_fin − V_début) / V_début` pour qu'on voie d'un coup d'œil quelle part du rendement vient de la pondération des apports. Le menu d'actions de chaque ligne reçoit l'item *Lier transferts* qui ouvre une modal de sélection multiple avec filtres période / catégorie / recherche texte ; la modal propose automatiquement le sens (`in` pour les montants bancaires négatifs, `out` pour les positifs) et l'utilisateur peut inverser ligne par ligne avant de soumettre. Les transactions liées à un ou plusieurs comptes de bilan affichent maintenant une petite icône `Link2` à côté de la description dans la page *Transactions*, avec un tooltip listant les noms et sens des comptes. Les chemins de suppression en lot (par fichier importé et tout effacer) pré-vérifient l'existence d'un lien dans `balance_account_transfers` et surfacent l'erreur typée `TransactionLinkedToBalanceError` (« Cette transaction est liée au compte de bilan X — déliez-la avant de supprimer ») au lieu de laisser fuiter l'erreur SQLite brute. Le graphique d'évolution sur `/balance` superpose désormais des lignes verticales de référence à chaque date de transfert lié (vert pour `in`, rouge pour `out`). Nouvelles clés i18n sous `balance.returns.*`, `balance.accountsTable.*`, `balance.transfers.*`, `balance.evolution.*`, `transactions.transferIcon.*` (FR + EN) (#142)
|
||||
- **Bilan — page `/balance` avec graphique d'évolution et entrée sidebar** (route `/balance`) : quatrième tranche de la feature *Bilan*, qui la rend enfin accessible depuis la navigation. La nouvelle page compose (1) une carte d'aperçu avec la valeur nette agrégée du dernier snapshot, le Δ% par rapport au snapshot chronologiquement précédent (affiché « — » quand il n'existe qu'un seul snapshot), un avertissement de fraîcheur quand le dernier snapshot date de plus de 60 jours, et un CTA *Nouveau snapshot* qui pointe vers `/balance/snapshot` ; (2) un sélecteur de période (3 mois / 6 mois / 1 an / 3 ans / Tout) qui recharge toutes les séries en parallèle ; (3) un graphique d'évolution avec deux modes — *Ligne* (une seule série `SUM(value) GROUP BY snapshot_date`) et *Empilé par catégorie* (une `<Area stackId>` Recharts par `balance_categories.key`) ; (4) un tableau des comptes listant chaque compte actif avec sa dernière valeur snapshot, le Δ% par compte sur la période active (valeur la plus récente vs valeur du premier snapshot dans la fenêtre — null si pas d'ancrage, affiché « — »), et un menu d'actions (Détail désactivé en attendant la #142, Archiver). Les colonnes de rendement (3M / 1A / depuis création / non ajusté) sont réservées pour une version ultérieure avec un commentaire `TODO`. La sidebar expose désormais l'entrée *Bilan* (icône `Wallet`) entre *Rapports* et *Paramètres*. Le service gagne trois helpers de série temporelle : `getSnapshotTotalsByDate(range?)`, `getSnapshotTotalsByCategoryAndDate(range?)`, `getAccountsLatestSnapshot()` ainsi qu'un calcul d'ancrage par compte `getAccountsPeriodAnchor(range)` — tous couverts par des tests unitaires. Nouveau hook `useBalanceOverview` (`useReducer` scoped) qui pilote l'état de la page. Nouvelles clés i18n sous `balance.overview.*`, `balance.period.*`, `balance.chart.*` plus `nav.balance` (FR + EN) (#141)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- **Balance sheet — documentation and ADRs** (`docs/`): closes the Bilan milestone with the documentation pass. `docs/architecture.md` now lists the 5 new tables (`balance_categories`, `balance_accounts`, `balance_snapshots`, `balance_snapshot_lines`, `balance_account_transfers`), the 7 new indexes, the SQL CHECK and FK invariants (CAD-only, kind invariants, `RESTRICT` on `transaction_id` for Modified Dietz reproducibility), the `balance.service.ts` 4-section layout (CRUD / snapshots+lines / returns+transfers / prices), the 3 page-scoped hooks (`useBalanceAccounts`, `useSnapshotEditor`, `useBalanceOverview`), the `compute_account_return` Tauri command (with the `fetch_price` future-Phase-5 mention), and the 3 new `/balance*` routes. Three new ADRs land alongside: **0008 — Modified Dietz** (justifies the choice vs. ROI / TWR / IRR with reference to `return_calculator.rs`); **0009 — Proxy price-fetching via maximus-api** (architecture documented now, implementation stays BLOCKED by maximus-api Phase 2 — covers privacy considerations like header stripping, no `(symbol, license)` log correlation and the fixed `simpl-resultat` UA, the Yahoo + CoinGecko provider abstraction, the Bearer auth strategy, the client + server rate limiting and the dual-side premium gating); **0010 — FK RESTRICT on `balance_account_transfers.transaction_id`** (justifies the integrity over friction trade-off for Modified Dietz reproducibility). The user guide gains a new *Balance sheet* section walking through snapshot entry (simple + priced), transfer linking, multi-horizon return reading (3M / 1Y / since inception with the side-by-side unadjusted column), with the price-fetching premium flagged "coming in Phase 5". `docs.balance.*` i18n keys (FR + EN) ship so the in-app guide reflects the new section (#145)
|
||||
- **Balance sheet — cross-cutting integration test suite** (test infrastructure): closes out the *Bilan* feature with a layer of integration tests that exercise the whole TypeScript surface in a single happy-path flow (account → priced category → priced snapshot → linked transfer → return) plus dedicated assertions for currency lock (CAD-only at the MVP, rejected at both the service layer and SQL CHECK), priced-kind tolerance safety (a bad save must NOT clear pre-existing lines), `computeAccountReturn` wiring (active-profile resolution, ISO date forwarding, partial-period payload pass-through). Three new Rust integration tests apply migration v9 on top of a seeded v1 schema with pre-existing transactions to verify (1) no row loss / data mutation, (2) link / unlink transfer round-trip on real transaction ids, (3) the FK RESTRICT chain (linked transaction deletion blocked, unblocked after unlink), (4) the v1 `categories.id` and v9 `balance_categories.id` namespaces coexist independently. A non-regression source-level test on `TransactionTable.tsx` locks down the inlined transfer icon contract: optional prop, optional-chaining short-circuit, i18n keys, aria-label, shared description-cell layout — so the page renders identically when no transfers are linked. (#144)
|
||||
- **Balance sheet — Modified Dietz returns and transfer linking** (route `/balance`): per-account performance now ships. New Rust module `commands/return_calculator.rs` implements the Modified Dietz formula `R = (V_end − V_start − ΣCF_i) / (V_start + ΣW_i × CF_i)` with day-precision contribution weights `W_i = (T − t_i) / T`, plus `(1 + R)^(365/T) − 1` annualization. Edge cases — missing endpoint snapshot, no flows tagged in the period, account created mid-period, depleted-then-refilled, zero-length period — are surfaced with explicit `is_partial` / `has_no_transfers_warning` flags so the UI shows a clean dash + tooltip instead of a confusing number. The new Tauri command `compute_account_return(account_id, period_start, period_end)` runs three short SQL reads against the active profile DB (latest snapshot ≤ period start, latest snapshot ≤ period end, transfers JOINed with transactions filtered to the period) and feeds the calculator. Seven co-located TDD tests cover every case before the implementation. The accounts table on `/balance` now shows four extra columns side-by-side: 3M / 1Y / Since-inception (Modified Dietz) plus an *Unadjusted* column showing the simple `(V_end − V_start) / V_start` so the user can see at a glance how much of the return came from contribution timing. Each row's actions menu gains a *Link transfers* item that opens a multi-select modal with date range / category / free-text filters; the modal auto-proposes the direction (`in` for negative bank amounts, `out` for positive) and the user can flip it per row before submitting. Transactions linked to one or more balance accounts now show a small `Link2` icon next to the description in the *Transactions* page, with a tooltip listing the account name(s) and direction(s). Bulk transaction-deletion paths (per-imported-file and clear-all) now pre-check for any link in `balance_account_transfers` and surface a typed `TransactionLinkedToBalanceError` ("This transaction is linked to balance account X — unlink it before deleting") instead of leaking the raw SQLite FK error. The evolution chart on `/balance` now overlays vertical reference lines at every linked-transfer date (green for `in`, red for `out`). New i18n keys under `balance.returns.*`, `balance.accountsTable.*`, `balance.transfers.*`, `balance.evolution.*`, `transactions.transferIcon.*` (FR + EN) (#142)
|
||||
- **Balance sheet — `/balance` overview page, evolution chart and sidebar entry** (route `/balance`): fourth slice of the *Bilan* feature finally surfaces it in the navigation. The new page composes (1) an overview card with the latest aggregate net worth, the Δ% versus the previous chronological snapshot (rendered as "—" when only one snapshot exists), a 60-day staleness warning when the latest snapshot is older than that threshold, and a *New snapshot* CTA pointing at `/balance/snapshot`; (2) a period selector (3 months / 6 months / 1 year / 3 years / All) that re-fetches every series in parallel; (3) an evolution chart with two modes — *Line* (single series of `SUM(value) GROUP BY snapshot_date`) and *Stacked by category* (one Recharts `<Area stackId>` per `balance_categories.key`); (4) an accounts table listing every active account with its latest snapshot value, the per-account Δ% over the active period (latest value vs the value at the earliest snapshot inside the window — null when no anchor exists, rendered as "—"), and an actions menu (Details placeholder, Archive). Return-metric columns (3M / 1Y / since-creation / unadjusted) are reserved for a later release with a `TODO` marker. The sidebar now exposes the *Balance sheet* entry (`Wallet` icon) between *Reports* and *Settings*. The service grows three time-series helpers: `getSnapshotTotalsByDate(range?)`, `getSnapshotTotalsByCategoryAndDate(range?)`, `getAccountsLatestSnapshot()` and a per-account anchor query `getAccountsPeriodAnchor(range)` — all guarded by unit tests. New `useBalanceOverview` hook (scoped `useReducer`) drives the page state. New i18n keys under `balance.overview.*`, `balance.period.*`, `balance.chart.*` plus `nav.balance` (FR + EN) (#141)
|
||||
|
|
|
|||
|
|
@ -901,6 +901,44 @@
|
|||
"Seasonality, top movers, and budget adherence stay monthly even when the toggle is set to YTD — only the 4 KPI numbers change"
|
||||
]
|
||||
},
|
||||
"balance": {
|
||||
"title": "Balance Sheet",
|
||||
"overview": "The Balance Sheet is a net-worth view: you periodically enter a dated snapshot of all your accounts (cash, RRSP, TFSA, funds, stocks, crypto, other), track their evolution over time, and compute the true return of each investment account by linking transfers (deposits/withdrawals) to the matching accounts.",
|
||||
"features": [
|
||||
"7 standard categories pre-installed (Cash, TFSA, RRSP, Fund, Stock, Crypto, Other) — renameable, non-deletable",
|
||||
"Custom category creation with simple (direct amount) or priced (quantity × unit price) kind",
|
||||
"Accounts per category: name, optional symbol, currency (CAD at MVP), notes",
|
||||
"Dated snapshots with a UNIQUE constraint per date — editing means revisiting the same date, never duplicating",
|
||||
"\"Prefill from previous snapshot\" button: copies simple values + priced quantities",
|
||||
"Linking existing transactions to a balance account (modal with filters and auto-suggested direction)",
|
||||
"Attribution icon in the Transactions page for transactions linked to a transfer",
|
||||
"Evolution chart with line or stacked-area-by-category mode + vertical markers for tagged transfers (green = in, red = out)",
|
||||
"Accounts table with 3 Modified Dietz return columns (3M / 1Y / since inception) + side-by-side unadjusted return column",
|
||||
"Warning if the latest snapshot is more than 60 days old",
|
||||
"Soft-delete of accounts (Archive) — hidden from new snapshots, preserved in history",
|
||||
"Snapshot deletion with double-confirmation by retyping the date",
|
||||
"Privacy-first: everything stays local, no outbound calls at MVP"
|
||||
],
|
||||
"steps": [
|
||||
"Go to /balance/accounts → Categories tab to create an extra category if needed (RRIF as simple, or Stocks Wealthsimple as priced)",
|
||||
"Go to the Accounts tab to create each account (TFSA Tangerine under TFSA, BTC Ledger under Crypto with symbol BTC)",
|
||||
"Click \"+ New snapshot\" from /balance to open /balance/snapshot at today's date",
|
||||
"Fill in values per account (grouped by category). For priced accounts, enter quantity and unit price — value is computed",
|
||||
"Save. The chart on /balance refreshes immediately",
|
||||
"To compute the real return of an investment account, open the actions menu → \"Link transfers\" → check the transactions matching deposits/withdrawals — direction (in/out) is auto-proposed",
|
||||
"The accounts table now shows Modified Dietz returns over 3M / 1Y / since inception, side-by-side with the unadjusted return",
|
||||
"To edit an existing snapshot, click its point on the chart or use the date picker — the page opens in edit mode (date is immutable)",
|
||||
"To delete a snapshot, click \"Delete\" in its editor and retype the date to confirm"
|
||||
],
|
||||
"tips": [
|
||||
"Take snapshots on a regular cadence (monthly or quarterly) — return quality depends on regularity",
|
||||
"The unadjusted return on the right shows \"account value\" vs \"true performance\": the difference comes from contributions, not from performance",
|
||||
"Vertical chart markers help you read value jumps: a jump followed by a green marker isn't \"performance\", it's a deposit",
|
||||
"If you try to delete a transaction linked to a balance account, the app asks you to unlink it first — this friction preserves the reproducibility of past returns",
|
||||
"The \"balance out of date\" warning appears if your latest snapshot is more than 60 days old",
|
||||
"(Coming in Phase 5) Automatic price fetching for Stocks/Crypto via a private proxy (premium-only) that anonymizes your request — manual entry remains always available"
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"overview": "Configure app preferences, check for updates, access the user guide, and manage your data with export/import tools.",
|
||||
|
|
|
|||
|
|
@ -901,6 +901,44 @@
|
|||
"La saisonnalité, les top mouvements et l'adhésion budgétaire restent mensuels même quand le toggle est sur YTD — seuls les 4 chiffres KPI changent"
|
||||
]
|
||||
},
|
||||
"balance": {
|
||||
"title": "Bilan",
|
||||
"overview": "Le Bilan est une vue patrimoniale : vous saisissez périodiquement un snapshot daté de l'ensemble de vos comptes (encaisse, REER, CELI, fonds, actions, crypto, autres), suivez leur évolution dans le temps et calculez le vrai rendement de chaque compte d'investissement en liant les transferts (apports/retraits) aux comptes correspondants.",
|
||||
"features": [
|
||||
"7 catégories standard pré-installées (Encaisse, CELI, REER, Fonds, Actions, Crypto, Autres) — renommables, non-supprimables",
|
||||
"Création de catégories personnalisées avec choix simple (montant direct) ou priced (quantité × prix unitaire)",
|
||||
"Comptes par catégorie : nom, symbole optionnel, devise (CAD au MVP), notes",
|
||||
"Snapshots datés avec contrainte UNIQUE par date — éditer = revenir sur la même date, jamais dupliquer",
|
||||
"Bouton « Pré-remplir depuis le snapshot précédent » : copie les valeurs simples + les quantités priced",
|
||||
"Liaison de transactions existantes à un compte de bilan (modal avec filtres et sens auto-proposé)",
|
||||
"Icône d'attribution dans la page Transactions pour les transactions liées à un transfert",
|
||||
"Graphique d'évolution avec mode courbe ou aire empilée par catégorie + marqueurs verticaux pour les transferts (vert = in, rouge = out)",
|
||||
"Tableau des comptes avec 3 colonnes de rendement Modified Dietz (3M / 1A / depuis création) + colonne rendement non-ajusté côte-à-côte",
|
||||
"Avertissement si le dernier snapshot remonte à plus de 60 jours",
|
||||
"Soft-delete des comptes (Archiver) — masqués des nouveaux snapshots, conservés dans l'historique",
|
||||
"Suppression d'un snapshot avec double-confirmation par re-saisie de la date",
|
||||
"Privacy-first : tout est local, aucun appel sortant au MVP"
|
||||
],
|
||||
"steps": [
|
||||
"Allez dans /balance/accounts → onglet Catégories pour créer si besoin une catégorie supplémentaire (FERR en simple, ou Stocks Wealthsimple en priced)",
|
||||
"Allez dans l'onglet Comptes pour créer chaque compte (TFSA Tangerine rattaché à CELI, BTC Ledger rattaché à Crypto avec symbole BTC)",
|
||||
"Cliquez « + Nouveau snapshot » depuis /balance pour ouvrir /balance/snapshot à la date du jour",
|
||||
"Remplissez les valeurs par compte (groupées par catégorie). Pour les comptes priced, saisissez la quantité et le prix unitaire — la valeur est calculée",
|
||||
"Enregistrez. Le graphique sur /balance s'actualise immédiatement",
|
||||
"Pour calculer le rendement réel d'un compte d'investissement, ouvrez le menu actions → « Lier transferts » → cochez les transactions qui correspondent à des apports/retraits — le sens (in/out) est proposé automatiquement",
|
||||
"Le tableau des comptes affiche maintenant les rendements Modified Dietz sur 3M / 1A / depuis création, avec le rendement non-ajusté côte-à-côte",
|
||||
"Pour éditer un snapshot existant, cliquez sur son point dans le graphique ou utilisez le sélecteur de date — la page s'ouvre en mode édition (la date est immutable)",
|
||||
"Pour supprimer un snapshot, cliquez « Supprimer » dans son éditeur et re-saisissez la date pour confirmer"
|
||||
],
|
||||
"tips": [
|
||||
"Saisissez vos snapshots à un rythme régulier (mensuel ou trimestriel) — la qualité des rendements dépend de la régularité",
|
||||
"Le rendement non-ajusté à droite vous permet de voir « valeur du compte » vs « vraie performance » : la différence vient des apports, pas de la performance",
|
||||
"Les marqueurs verticaux du graphique aident à lire les sauts de valeur : un saut suivi d'un marqueur vert n'est pas une « performance », c'est un dépôt",
|
||||
"Si vous tentez de supprimer une transaction liée à un compte de bilan, l'app vous demande de la délier d'abord — cette friction préserve la reproductibilité de vos rendements passés",
|
||||
"L'avertissement « bilan pas à jour » apparaît si votre dernier snapshot remonte à plus de 60 jours",
|
||||
"(À venir Phase 5) Récupération automatique des prix pour Actions/Crypto via un proxy privé (premium-only) qui anonymise votre requête — la saisie manuelle reste toujours disponible"
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"title": "Paramètres",
|
||||
"overview": "Configurez les préférences de l'application, vérifiez les mises à jour, accédez au guide utilisateur et gérez vos données avec les outils d'export/import.",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
SlidersHorizontal,
|
||||
PiggyBank,
|
||||
BarChart3,
|
||||
Wallet,
|
||||
Settings,
|
||||
ArrowLeft,
|
||||
Lightbulb,
|
||||
|
|
@ -29,6 +30,7 @@ const SECTIONS = [
|
|||
{ key: "adjustments", icon: SlidersHorizontal },
|
||||
{ key: "budget", icon: PiggyBank },
|
||||
{ key: "reports", icon: BarChart3 },
|
||||
{ key: "balance", icon: Wallet },
|
||||
{ key: "settings", icon: Settings },
|
||||
] as const;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue