LCOV - code coverage report
Current view: top level - lib/pages/budget - budget_view_page.dart Coverage Total Hit
Test: lcov.info Lines: 0.0 % 113 0
Test Date: 2026-11-14 15:08:11 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2023 The terCAD team. All rights reserved.
       2              : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3              : 
       4              : import 'dart:collection';
       5              : 
       6              : import 'package:app_finance/_classes/controller/iterator_controller.dart';
       7              : import 'package:app_finance/_classes/herald/app_locale.dart';
       8              : import 'package:app_finance/_classes/controller/flow_state_machine.dart';
       9              : import 'package:app_finance/_classes/herald/app_start_of_month.dart';
      10              : import 'package:app_finance/_classes/storage/app_data.dart';
      11              : import 'package:app_finance/_classes/storage/history_data.dart';
      12              : import 'package:app_finance/_classes/structure/account_app_data.dart';
      13              : import 'package:app_finance/_classes/structure/bill_app_data.dart';
      14              : import 'package:app_finance/_classes/structure/budget_app_data.dart';
      15              : import 'package:app_finance/_classes/structure/currency/exchange.dart';
      16              : import 'package:app_finance/_configs/account_type.dart';
      17              : import 'package:app_finance/_configs/screen_helper.dart';
      18              : import 'package:app_finance/_configs/theme_helper.dart';
      19              : import 'package:app_finance/_classes/structure/navigation/app_route.dart';
      20              : import 'package:app_finance/_ext/date_time_ext.dart';
      21              : import 'package:app_finance/pages/_interfaces/abstract_page_state.dart';
      22              : import 'package:app_finance/pages/budget/widgets/budget_header_widget.dart';
      23              : import 'package:app_finance/design/generic/base_line_widget.dart';
      24              : import 'package:app_finance/design/generic/base_list_infinite_widget.dart';
      25              : import 'package:app_finance/design/wrapper/confirmation_wrapper.dart';
      26              : import 'package:app_finance/design/wrapper/tab_widget.dart';
      27              : import 'package:material_ui/material_ui.dart';
      28              : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
      29              : import 'package:intl/intl.dart';
      30              : 
      31              : class BudgetViewPage extends StatefulWidget {
      32              :   final String uuid;
      33              : 
      34            0 :   const BudgetViewPage({
      35              :     super.key,
      36              :     required this.uuid,
      37              :   });
      38              : 
      39            0 :   @override
      40            0 :   BudgetViewPageState createState() => BudgetViewPageState();
      41              : }
      42              : 
      43              : class BudgetViewPageState extends AbstractPageState<BudgetViewPage> with TickerProviderStateMixin {
      44              :   late double width;
      45              :   int focus = 0;
      46              : 
      47            0 :   @override
      48              :   String getTitle() {
      49            0 :     final item = super.state.getByUuid(widget.uuid) as BudgetAppData;
      50            0 :     return item.title;
      51              :   }
      52              : 
      53            0 :   @override
      54              :   String getButtonName() => '';
      55              : 
      56            0 :   @override
      57              :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
      58            0 :     double indent = ThemeHelper.getIndent(4);
      59            0 :     NavigatorState nav = Navigator.of(context);
      60            0 :     return Container(
      61            0 :       margin: EdgeInsets.only(left: 2 * indent, right: 2 * indent),
      62            0 :       child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
      63            0 :         FloatingActionButton(
      64              :           heroTag: 'budget_view_page_edit',
      65            0 :           onPressed: () => nav.pushNamed(AppRoute.budgetEditRoute, arguments: {routeArguments.uuid: widget.uuid}),
      66            0 :           tooltip: AppLocale.labels.editBudgetTooltip,
      67              :           child: const Icon(Icons.edit),
      68              :         ),
      69            0 :         FloatingActionButton(
      70              :           heroTag: 'budget_view_page_deactivate',
      71            0 :           onPressed: () => ConfirmationWrapper.show(
      72              :             context,
      73            0 :             () => FlowStateMachine.deactivate(nav, store: super.state, uuid: widget.uuid),
      74              :           ),
      75            0 :           tooltip: AppLocale.labels.deleteBudgetTooltip,
      76              :           child: const Icon(Icons.delete),
      77              :         ),
      78              :       ]),
      79              :     );
      80              :   }
      81              : 
      82            0 :   Widget buildDeltaListWidget(item, BuildContext context) {
      83            0 :     final value = (item.delta as double).toCurrency(currency: item.currency, withPattern: false);
      84            0 :     return BaseLineWidget(
      85              :       uuid: '',
      86              :       title: '',
      87            0 :       description: (item.timestamp as DateTime).yMEd(),
      88              :       progress: 1.0,
      89            0 :       details: item.delta > 0 ? '+$value' : value,
      90              :       color: Colors.transparent,
      91            0 :       width: width,
      92              :     );
      93              :   }
      94              : 
      95            0 :   Widget buildBillListWidget(item, BuildContext context) {
      96            0 :     return BaseLineWidget(
      97            0 :       uuid: item.uuid ?? '',
      98            0 :       title: item.title ?? '',
      99            0 :       description: item.description ?? '',
     100            0 :       details: item.detailsFormatted,
     101            0 :       progress: item.progress,
     102            0 :       color: item.color ?? Colors.transparent,
     103            0 :       icon: item.icon ?? Icons.radio_button_unchecked_sharp,
     104            0 :       hidden: item.hidden,
     105            0 :       skip: item.skip,
     106            0 :       width: width,
     107              :       route: AppRoute.billViewRoute,
     108              :     );
     109              :   }
     110              : 
     111            0 :   Widget buildSummaryListWidget(item, BuildContext context) {
     112            0 :     return BaseLineWidget(
     113            0 :       uuid: item.uuid ?? '',
     114            0 :       title: item.title ?? '',
     115            0 :       description: item.description ?? '',
     116            0 :       details: item.detailsFormatted,
     117            0 :       progress: item.progress,
     118            0 :       color: item.color ?? Colors.transparent,
     119            0 :       icon: item.icon ?? Icons.radio_button_unchecked_sharp,
     120            0 :       hidden: item.hidden,
     121            0 :       skip: item.skip,
     122            0 :       width: width,
     123              :       route: AppRoute.billScopeViewRoute,
     124            0 :       routeCondition: {routeArguments.search: item.uuid, routeArguments.uuid: widget.uuid},
     125              :     );
     126              :   }
     127              : 
     128            0 :   InterfaceIterator<num, dynamic, dynamic> _getSummary() {
     129            0 :     var exchange = Exchange(store: state);
     130            0 :     var category = super.state.getByUuid(widget.uuid) as BudgetAppData;
     131            0 :     var bills = state.getStream(AppDataType.bills, filter: (o) => o.category != widget.uuid);
     132            0 :     DateTime now = DateTime.now();
     133            0 :     int startingDay = AppStartOfMonth.get();
     134            0 :     DateTime curr = now.getStartingDay(startingDay);
     135              :     int iteration = 0;
     136              :     BillAppData? item;
     137            0 :     AccountAppData summary = AccountAppData(
     138            0 :       uuid: curr.toString(),
     139            0 :       type: AppAccountType.cash.toString(),
     140            0 :       title: DateFormat.MMMM().format(curr),
     141            0 :       description: curr.year.toString(),
     142            0 :       currency: category.currency,
     143              :       progress: 1,
     144              :     );
     145            0 :     var data = SplayTreeMap<num, AccountAppData>();
     146              :     while (true) {
     147            0 :       item = bills.next;
     148            0 :       if (item == null || item.createdAt.isBefore(curr)) {
     149            0 :         if (category.amountLimit < summary.details) {
     150            0 :           summary.color = Colors.red;
     151              :         }
     152            0 :         data[iteration] = summary.clone();
     153              :         if (item == null) {
     154              :           break;
     155              :         }
     156            0 :         iteration++;
     157            0 :         curr = DateTime(now.year, now.month - iteration, now.day).getStartingDay(startingDay);
     158            0 :         summary.uuid = curr.toString();
     159            0 :         summary.details = 0.0;
     160            0 :         summary.color = null;
     161            0 :         summary.title = DateFormat.MMMM().format(curr);
     162            0 :         summary.description = curr.year.toString();
     163              :       }
     164            0 :       summary.details = summary.details + exchange.reform(item.details ?? 0.0, item.currency, summary.currency);
     165              :     }
     166            0 :     return IteratorController(data, transform: (v) => v);
     167              :   }
     168              : 
     169            0 :   @override
     170              :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     171            0 :     final indent = ThemeHelper.getIndent();
     172            0 :     final pageWidth = ThemeHelper.getWidth(context, 3, constraints);
     173            0 :     width = pageWidth - indent;
     174            0 :     final isLeft = ScreenHelper.state().isLeftBar;
     175              :     if (isLeft) {
     176            0 :       width -= ThemeHelper.barHeight;
     177              :     }
     178            0 :     var category = state.getByUuid(widget.uuid) as BudgetAppData;
     179            0 :     final boundary = category.getDateBoundary().millisecondsSinceEpoch + 0.0;
     180            0 :     return Padding(
     181            0 :       padding: EdgeInsets.only(top: indent),
     182            0 :       child: Column(
     183            0 :         children: [
     184            0 :           BudgetHeaderWidget(item: category, width: pageWidth),
     185              :           ThemeHelper.hIndent05,
     186              :           const Divider(height: 2),
     187            0 :           Expanded(
     188            0 :             child: TabWidget(
     189              :               type: TabType.secondary,
     190              :               isLeft: isLeft,
     191            0 :               focus: focus,
     192            0 :               callback: (data) => setState(() => focus = data),
     193            0 :               tabs: [
     194            0 :                 Tab(text: AppLocale.labels.billHeadline),
     195            0 :                 Tab(text: AppLocale.labels.summary),
     196            0 :                 Tab(text: AppLocale.labels.budgetLimitHeadline),
     197              :               ],
     198            0 :               children: [
     199            0 :                 BaseListInfiniteWidget(
     200              :                   stream:
     201            0 :                       state.getStream(AppDataType.bills, boundary: boundary, filter: (o) => o.category != widget.uuid),
     202            0 :                   width: width,
     203            0 :                   buildListWidget: buildBillListWidget,
     204              :                 ),
     205            0 :                 BaseListInfiniteWidget(
     206            0 :                   stream: _getSummary(),
     207            0 :                   width: width,
     208            0 :                   buildListWidget: buildSummaryListWidget,
     209              :                 ),
     210            0 :                 BaseListInfiniteWidget(
     211            0 :                   stream: HistoryData.getStream(widget.uuid),
     212            0 :                   width: width,
     213            0 :                   buildListWidget: buildDeltaListWidget,
     214              :                 ),
     215              :               ],
     216              :             ),
     217              :           ),
     218              :         ],
     219              :       ),
     220              :     );
     221              :   }
     222              : }
        

Generated by: LCOV version 2.0-1