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 'package:app_finance/_classes/herald/app_locale.dart';
5 : import 'package:app_finance/_classes/structure/abstract_app_data.dart';
6 : import 'package:app_finance/_classes/storage/app_data.dart';
7 : import 'package:app_finance/_classes/structure/currency/exchange.dart';
8 : import 'package:app_finance/_classes/structure/invoice_app_data.dart';
9 : import 'package:app_finance/_ext/int_ext.dart';
10 : import 'package:app_finance/_ext/string_ext.dart';
11 : import 'package:app_finance/_mixins/storage_mixin.dart';
12 : import 'package:flutter/material.dart';
13 : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
14 :
15 : class BudgetAppData extends AbstractAppData with StorageMixin {
16 : double amount;
17 : Map<int, double> amountSet;
18 : final int _month = DateTime.now().month;
19 :
20 0 : BudgetAppData({
21 : required super.title,
22 : super.uuid,
23 : super.progress = 0.0,
24 : super.color,
25 : super.icon,
26 : super.currency,
27 : super.updatedAt,
28 : super.createdAt,
29 : super.createdAtFormatted,
30 : amountLimit = 0.0,
31 : this.amountSet = const {},
32 : this.amount = 0.0,
33 : super.hidden,
34 0 : }) : super(
35 : details: amountLimit,
36 : );
37 :
38 0 : @override
39 : String getClassName() => 'BudgetAppData';
40 :
41 0 : @override
42 : AppDataType getType() => AppDataType.budgets;
43 :
44 0 : @override
45 : BudgetAppData clone() {
46 0 : return BudgetAppData(
47 0 : title: super.title,
48 0 : uuid: super.uuid,
49 0 : progress: super.progress,
50 0 : color: super.color,
51 0 : icon: super.icon,
52 0 : currency: super.currency,
53 0 : createdAt: super.createdAt,
54 0 : amountLimit: super.details,
55 0 : amountSet: amountSet,
56 0 : amount: amount,
57 0 : hidden: super.hidden,
58 : );
59 : }
60 :
61 0 : factory BudgetAppData.fromJson(Map<String, dynamic> json) {
62 0 : return BudgetAppData(
63 0 : title: json['title'],
64 0 : uuid: json['uuid'],
65 0 : progress: 0.0 + json['progress'],
66 0 : color: json['color'] != null ? MaterialColor(json['color'], const <int, Color>{}) : null,
67 0 : icon: (json['icon'] as int?)?.toIcon(),
68 0 : currency: CurrencyProvider.find(json['currency']),
69 0 : updatedAt: DateTime.parse(json['updatedAt']),
70 0 : createdAt: DateTime.parse(json['createdAt']),
71 0 : amountLimit: 0.0 + json['amountLimit'],
72 0 : amountSet: json['amountSet'] != null ? json['amountSet'].toString().toMap<int, double>() : {},
73 0 : hidden: json['hidden'],
74 : );
75 : }
76 :
77 0 : @override
78 0 : Map<String, dynamic> toJson() => {
79 0 : ...super.toJson()..remove('description'),
80 0 : 'amountLimit': amountLimit,
81 0 : 'amountSet': amountSet.toString(),
82 : };
83 :
84 0 : @override
85 : double get details {
86 0 : if (super.details > 0 && super.details < 1) {
87 0 : return _relativeAmountLimit() - amount;
88 0 : } else if (amountLimit == 0) {
89 0 : return -amount;
90 0 : } else if (super.details > 0) {
91 0 : return amountLimit * (1 - progress);
92 : } else {
93 : return 0.0;
94 : }
95 : }
96 :
97 0 : String get detailsFormatted {
98 0 : if (super.details > 0 && super.details < 1) {
99 0 : final curr = (_relativeAmountLimit() - amount).toCurrency(currency: currency, withPattern: false);
100 0 : return '$curr ${AppLocale.labels.left}';
101 0 : } else if (super.details > 0) {
102 0 : return '${details.toCurrency(currency: currency, withPattern: false)} ${AppLocale.labels.left}';
103 : } else {
104 0 : return '${amount.toCurrency(currency: currency, withPattern: false)} ${AppLocale.labels.spent}';
105 : }
106 : }
107 :
108 0 : String _description() {
109 0 : if (super.details > 0 && super.details < 1) {
110 0 : final percents = (amountLimit * 100).toStringAsFixed(2);
111 0 : return '${(amount).toCurrency(currency: currency, withPattern: false)} /'
112 0 : ' ${_relativeAmountLimit().toCurrency(currency: currency, withPattern: false)} ($percents%)';
113 0 : } else if (super.details > 0) {
114 0 : return '${(amountLimit * progress).toCurrency(currency: currency, withPattern: false)} /'
115 0 : ' ${(amountLimit).toCurrency(currency: currency, withPattern: false)}'
116 0 : '${multiplication > 1 ? ' (${multiplication.toStringAsFixed(2)} ${AppLocale.labels.coef})' : ''}';
117 : } else {
118 : return '';
119 : }
120 : }
121 :
122 0 : double get progressLeft => progress < 1 ? 1 - progress : 0.0;
123 :
124 0 : double get multiplication => amountSet[_month] ?? 1.0;
125 :
126 0 : double get amountLimit => super.details * multiplication;
127 0 : set amountLimit(double value) => super.details = value;
128 :
129 0 : double _relativeAmountLimit() {
130 0 : final ex = Exchange(store: super.getState());
131 0 : return amountLimit *
132 0 : getState()
133 0 : .getActualList(AppDataType.invoice)
134 0 : .cast<InvoiceAppData>()
135 0 : .where((e) => e.accountFrom == null)
136 0 : .fold(0.0, (v, e) => v + ex.reform(e.details, e.currency, currency));
137 : }
138 :
139 0 : @override
140 0 : String get description => _description();
141 :
142 0 : @override
143 0 : set description(String? value) => {};
144 : }
|