Compare commits

..

2 commits

16 changed files with 357 additions and 71 deletions

View file

@ -18,25 +18,43 @@ class WalletCategory {
required this.color, required this.color,
}); });
/// Connects the generated fromJson method /// Generates a class instance from a Map
factory WalletCategory.fromJson(Map<String, dynamic> json) => factory WalletCategory.fromJson(Map<String, dynamic> json) =>
_$WalletCategoryFromJson(json); _$WalletCategoryFromJson(json);
/// Default [WalletCategory] instance for json_serializable
factory WalletCategory.unknown() => WalletCategory(
name: "Unknown",
id: -1,
icon: Icons.question_mark,
color: Colors.green,
);
/// User-defined name /// User-defined name
@JsonKey(defaultValue: "Unknown")
String name; String name;
/// Unique identificator of the category /// Unique identificator of the category
@JsonKey(required: true, disallowNullValue: true)
final int id; final int id;
/// Selected Icon for the category /// Selected Icon for the category
@JsonKey(fromJson: _iconDataFromJson, toJson: _iconDataToJson) @JsonKey(
fromJson: _iconDataFromJson,
toJson: _iconDataToJson,
defaultValue: _defaultIcon,
)
IconData icon; IconData icon;
/// The color that will be displayed with entry /// The color that will be displayed with entry
@JsonKey(fromJson: _colorFromJson, toJson: _colorToJson) @JsonKey(
fromJson: _colorFromJson,
toJson: _colorToJson,
defaultValue: _defaultColor,
)
Color color; Color color;
/// Connects the generated toJson method /// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$WalletCategoryToJson(this); Map<String, dynamic> toJson() => _$WalletCategoryToJson(this);
@override @override
@ -62,3 +80,6 @@ enum EntryType {
/// Income /// Income
income income
} }
IconData _defaultIcon() => Icons.question_mark;
Color _defaultColor() => Colors.green;

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: (C) 2024 Matyáš Caras
//
// SPDX-License-Identifier: AGPL-3.0-only
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'category.dart'; part of 'category.dart';
@ -10,13 +6,23 @@ part of 'category.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
WalletCategory _$WalletCategoryFromJson(Map<String, dynamic> json) => WalletCategory _$WalletCategoryFromJson(Map<String, dynamic> json) {
WalletCategory( $checkKeys(
name: json['name'] as String, json,
id: json['id'] as int, requiredKeys: const ['id'],
icon: _iconDataFromJson(json['icon'] as Map<String, dynamic>), disallowNullValues: const ['id'],
color: _colorFromJson(json['color'] as int), );
); return WalletCategory(
name: json['name'] as String? ?? 'Unknown',
id: (json['id'] as num).toInt(),
icon: json['icon'] == null
? _defaultIcon()
: _iconDataFromJson(json['icon'] as Map<String, dynamic>),
color: json['color'] == null
? _defaultColor()
: _colorFromJson((json['color'] as num).toInt()),
);
}
Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) => Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) =>
<String, dynamic>{ <String, dynamic>{

42
lib/api/debt_entry.dart Normal file
View file

@ -0,0 +1,42 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:prasule/api/debt_person.dart';
part 'debt_entry.g.dart';
/// Single transaction
///
/// The debt will be split between people who are not in [whoPayed]
@JsonSerializable()
class DebtEntry {
/// Single transaction
///
/// The debt will be split between people who are not in [whoPayed]
DebtEntry({
required this.id,
required this.amount,
required this.name,
required this.whoPayed,
}) : assert(whoPayed.isNotEmpty, "There has to be at least one payer");
/// Generates a class instance from a Map
factory DebtEntry.fromJson(Map<String, dynamic> json) =>
_$DebtEntryFromJson(json);
/// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$DebtEntryToJson(this);
/// Unique identifier
@JsonKey(required: true, disallowNullValue: true)
final int id;
/// The payed amount
@JsonKey(defaultValue: 0)
int amount;
/// User-friendly identifier for the transaction
@JsonKey(defaultValue: "Unknown")
String name;
/// List of people who payed
@JsonKey(defaultValue: DebtPerson.unknownPerson)
List<DebtPerson> whoPayed;
}

31
lib/api/debt_entry.g.dart Normal file
View file

@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'debt_entry.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DebtEntry _$DebtEntryFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
requiredKeys: const ['id'],
disallowNullValues: const ['id'],
);
return DebtEntry(
id: (json['id'] as num).toInt(),
amount: (json['amount'] as num?)?.toInt() ?? 0,
name: json['name'] as String? ?? 'Unknown',
whoPayed: (json['whoPayed'] as List<dynamic>?)
?.map((e) => DebtPerson.fromJson(e as Map<String, dynamic>))
.toList() ??
DebtPerson.unknownPerson(),
);
}
Map<String, dynamic> _$DebtEntryToJson(DebtEntry instance) => <String, dynamic>{
'id': instance.id,
'amount': instance.amount,
'name': instance.name,
'whoPayed': instance.whoPayed,
};

28
lib/api/debt_person.dart Normal file
View file

@ -0,0 +1,28 @@
import 'package:json_annotation/json_annotation.dart';
part 'debt_person.g.dart';
@JsonSerializable()
/// Represents a single person in a debt scenario
class DebtPerson {
/// Represents a single person in a debt scenario
DebtPerson({required this.id, required this.name});
/// Default [DebtPerson] instance for json_serializable
factory DebtPerson.unknownPerson() => DebtPerson(id: -1, name: "Unknown");
/// Generates a class instance from a Map
factory DebtPerson.fromJson(Map<String, dynamic> json) =>
_$DebtPersonFromJson(json);
/// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$DebtPersonToJson(this);
/// Unique identifier
@JsonKey(required: true, disallowNullValue: true)
final int id;
/// Identifier that the user will see
@JsonKey(defaultValue: "Unknown")
String name;
}

View file

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'debt_person.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DebtPerson _$DebtPersonFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
requiredKeys: const ['id'],
disallowNullValues: const ['id'],
);
return DebtPerson(
id: (json['id'] as num).toInt(),
name: json['name'] as String? ?? 'Unknown',
);
}
Map<String, dynamic> _$DebtPersonToJson(DebtPerson instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
};

View file

@ -0,0 +1,46 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:prasule/api/debt_entry.dart';
import 'package:prasule/api/debt_person.dart';
part 'debt_scenario.g.dart';
/// A folder for different entries and people
@JsonSerializable()
class DebtScenario {
/// A folder for different entries and people
DebtScenario({
required this.id,
required this.name,
required this.isArchived,
this.entries = const [],
this.people = const [],
});
/// Generates a class instance from a Map
factory DebtScenario.fromJson(Map<String, dynamic> json) =>
_$DebtScenarioFromJson(json);
/// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$DebtScenarioToJson(this);
/// Unique identified
@JsonKey(disallowNullValue: true)
final int id;
/// User-friendly identifier
@JsonKey(defaultValue: "Unknown")
String name;
/// Whether this scenario should be shown under archived ones
@JsonKey(defaultValue: false)
bool isArchived;
/// All entries
@JsonKey(defaultValue: [])
List<DebtEntry> entries;
/// All people
@JsonKey(defaultValue: _defaultPeopleList)
List<DebtPerson> people;
}
List<DebtPerson> _defaultPeopleList() => [DebtPerson.unknownPerson()];

View file

@ -0,0 +1,36 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'debt_scenario.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DebtScenario _$DebtScenarioFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
disallowNullValues: const ['id'],
);
return DebtScenario(
id: (json['id'] as num).toInt(),
name: json['name'] as String? ?? 'Unknown',
isArchived: json['isArchived'] as bool? ?? false,
entries: (json['entries'] as List<dynamic>?)
?.map((e) => DebtEntry.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
people: (json['people'] as List<dynamic>?)
?.map((e) => DebtPerson.fromJson(e as Map<String, dynamic>))
.toList() ??
_defaultPeopleList(),
);
}
Map<String, dynamic> _$DebtScenarioToJson(DebtScenario instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'isArchived': instance.isArchived,
'entries': instance.entries,
'people': instance.people,
};

View file

@ -11,19 +11,25 @@ class EntryData {
/// Contains raw data /// Contains raw data
EntryData({required this.name, required this.amount, this.description = ""}); EntryData({required this.name, required this.amount, this.description = ""});
/// Connects the generated fromJson method /// Generates a class instance from a Map
factory EntryData.fromJson(Map<String, dynamic> json) => factory EntryData.fromJson(Map<String, dynamic> json) =>
_$EntryDataFromJson(json); _$EntryDataFromJson(json);
/// [EntryData] instance used as a default value for json_serializable
factory EntryData.unknown() => EntryData(name: "Unknown", amount: 0);
/// Name of entry /// Name of entry
@JsonKey(defaultValue: "Unknown")
String name; String name;
/// Optional description, default is empty /// Optional description, default is empty
@JsonKey(defaultValue: "")
String description; String description;
/// Amount for entry /// Amount for entry
@JsonKey(defaultValue: 0)
double amount; double amount;
/// Connects the generated toJson method /// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$EntryDataToJson(this); Map<String, dynamic> toJson() => _$EntryDataToJson(this);
} }

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: (C) 2024 Matyáš Caras
//
// SPDX-License-Identifier: AGPL-3.0-only
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'entry_data.dart'; part of 'entry_data.dart';
@ -11,9 +7,9 @@ part of 'entry_data.dart';
// ************************************************************************** // **************************************************************************
EntryData _$EntryDataFromJson(Map<String, dynamic> json) => EntryData( EntryData _$EntryDataFromJson(Map<String, dynamic> json) => EntryData(
name: json['name'] as String, name: json['name'] as String? ?? 'Unknown',
amount: (json['amount'] as num).toDouble(), amount: (json['amount'] as num?)?.toDouble() ?? 0,
description: json['description'] as String? ?? "", description: json['description'] as String? ?? '',
); );
Map<String, dynamic> _$EntryDataToJson(EntryData instance) => <String, dynamic>{ Map<String, dynamic> _$EntryDataToJson(EntryData instance) => <String, dynamic>{

View file

@ -24,21 +24,24 @@ class RecurringWalletEntry extends WalletSingleEntry {
this.repeatAfter = 1, this.repeatAfter = 1,
}); });
/// Connects the generated fromJson method /// Generates a class instance from a Map
factory RecurringWalletEntry.fromJson(Map<String, dynamic> json) => factory RecurringWalletEntry.fromJson(Map<String, dynamic> json) =>
_$RecurringWalletEntryFromJson(json); _$RecurringWalletEntryFromJson(json);
/// Connects the generated toJson method /// Converts the data in this instance into a Map
@override @override
Map<String, dynamic> toJson() => _$RecurringWalletEntryToJson(this); Map<String, dynamic> toJson() => _$RecurringWalletEntryToJson(this);
/// Last date the recurring entry was added into the single entry list /// Last date the recurring entry was added into the single entry list
@JsonKey(defaultValue: DateTime.now)
DateTime lastRunDate; DateTime lastRunDate;
/// After how many {recurType} should the entry recur /// After how many {recurType} should the entry recur
@JsonKey(defaultValue: 1)
int repeatAfter; int repeatAfter;
/// What type of recurrence should happen /// What type of recurrence should happen
@JsonKey(defaultValue: RecurType.month)
RecurType recurType; RecurType recurType;
} }

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: (C) 2024 Matyáš Caras
//
// SPDX-License-Identifier: AGPL-3.0-only
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'recurring_entry.dart'; part of 'recurring_entry.dart';
@ -10,19 +6,33 @@ part of 'recurring_entry.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
RecurringWalletEntry _$RecurringWalletEntryFromJson( RecurringWalletEntry _$RecurringWalletEntryFromJson(Map<String, dynamic> json) {
Map<String, dynamic> json) => $checkKeys(
RecurringWalletEntry( json,
data: EntryData.fromJson(json['data'] as Map<String, dynamic>), requiredKeys: const ['id'],
type: $enumDecode(_$EntryTypeEnumMap, json['type']), disallowNullValues: const ['id'],
date: DateTime.parse(json['date'] as String), );
category: return RecurringWalletEntry(
WalletCategory.fromJson(json['category'] as Map<String, dynamic>), data: json['data'] == null
id: json['id'] as int, ? EntryData.unknown()
lastRunDate: DateTime.parse(json['lastRunDate'] as String), : EntryData.fromJson(json['data'] as Map<String, dynamic>),
repeatAfter: json['repeatAfter'] as int, type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ??
recurType: $enumDecode(_$RecurTypeEnumMap, json['recurType']), EntryType.expense,
); date: json['date'] == null
? DateTime.now()
: DateTime.parse(json['date'] as String),
category: json['category'] == null
? WalletCategory.unknown()
: WalletCategory.fromJson(json['category'] as Map<String, dynamic>),
id: (json['id'] as num).toInt(),
lastRunDate: json['lastRunDate'] == null
? DateTime.now()
: DateTime.parse(json['lastRunDate'] as String),
recurType: $enumDecodeNullable(_$RecurTypeEnumMap, json['recurType']) ??
RecurType.month,
repeatAfter: (json['repeatAfter'] as num?)?.toInt() ?? 1,
);
}
Map<String, dynamic> _$RecurringWalletEntryToJson( Map<String, dynamic> _$RecurringWalletEntryToJson(
RecurringWalletEntry instance) => RecurringWalletEntry instance) =>

View file

@ -34,31 +34,39 @@ class Wallet {
this.starterBalance = 0, this.starterBalance = 0,
}); });
/// Connects the generated fromJson method /// Generates a class instance from a Map
factory Wallet.fromJson(Map<String, dynamic> json) => _$WalletFromJson(json); factory Wallet.fromJson(Map<String, dynamic> json) => _$WalletFromJson(json);
/// A list of all [RecurringWalletEntry]s /// A list of all [RecurringWalletEntry]s
@JsonKey(defaultValue: [])
final List<RecurringWalletEntry> recurringEntries; final List<RecurringWalletEntry> recurringEntries;
/// Name of the wallet /// Name of the wallet
@JsonKey(defaultValue: "Unknown")
final String name; final String name;
/// A list of available categories /// A list of available categories
@JsonKey(defaultValue: _defaultWalletCategory)
final List<WalletCategory> categories; final List<WalletCategory> categories;
/// List of saved entries /// List of saved entries
@JsonKey(defaultValue: [])
final List<WalletSingleEntry> entries; final List<WalletSingleEntry> entries;
/// The starting balance of the wallet /// The starting balance of the wallet
/// ///
/// Used to calculate current balance /// Used to calculate current balance
@JsonKey(defaultValue: 0)
double starterBalance; double starterBalance;
/// Selected currency /// Selected currency
@JsonKey(fromJson: _currencyFromJson) @JsonKey(
fromJson: _currencyFromJson,
defaultValue: _defaultCurrency,
)
final Currency currency; final Currency currency;
/// Connects the generated toJson method /// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$WalletToJson(this); Map<String, dynamic> toJson() => _$WalletToJson(this);
/// Getter for the next unused unique number ID in the wallet's **entry** list /// Getter for the next unused unique number ID in the wallet's **entry** list
@ -281,3 +289,20 @@ class Wallet {
WalletManager.saveWallet(this); WalletManager.saveWallet(this);
} }
} }
List<WalletCategory> _defaultWalletCategory() => [WalletCategory.unknown()];
Currency _defaultCurrency() => Currency.from(
json: {
"code": "USD",
"name": "United States Dollar",
"symbol": r"$",
"flag": "USD",
"decimal_digits": 2,
"number": 840,
"name_plural": "US dollars",
"thousands_separator": ",",
"decimal_separator": ".",
"space_between_amount_and_symbol": false,
"symbol_on_left": true,
},
);

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: (C) 2024 Matyáš Caras
//
// SPDX-License-Identifier: AGPL-3.0-only
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'wallet.dart'; part of 'wallet.dart';
@ -11,22 +7,24 @@ part of 'wallet.dart';
// ************************************************************************** // **************************************************************************
Wallet _$WalletFromJson(Map<String, dynamic> json) => Wallet( Wallet _$WalletFromJson(Map<String, dynamic> json) => Wallet(
name: json['name'] as String, name: json['name'] as String? ?? 'Unknown',
currency: _currencyFromJson(json['currency'] as Map<String, dynamic>), currency: json['currency'] == null
? _defaultCurrency()
: _currencyFromJson(json['currency'] as Map<String, dynamic>),
categories: (json['categories'] as List<dynamic>?) categories: (json['categories'] as List<dynamic>?)
?.map((e) => WalletCategory.fromJson(e as Map<String, dynamic>)) ?.map((e) => WalletCategory.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], _defaultWalletCategory(),
entries: (json['entries'] as List<dynamic>?) entries: (json['entries'] as List<dynamic>?)
?.map( ?.map(
(e) => WalletSingleEntry.fromJson(e as Map<String, dynamic>)) (e) => WalletSingleEntry.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], [],
recurringEntries: (json['recurringEntries'] as List<dynamic>?) recurringEntries: (json['recurringEntries'] as List<dynamic>?)
?.map((e) => ?.map((e) =>
RecurringWalletEntry.fromJson(e as Map<String, dynamic>)) RecurringWalletEntry.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], [],
starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0, starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0,
); );

View file

@ -21,25 +21,30 @@ class WalletSingleEntry {
required this.id, required this.id,
}); });
/// Connects the generated fromJson method /// Generates a class instance from a Map
factory WalletSingleEntry.fromJson(Map<String, dynamic> json) => factory WalletSingleEntry.fromJson(Map<String, dynamic> json) =>
_$WalletSingleEntryFromJson(json); _$WalletSingleEntryFromJson(json);
/// Expense or income /// Expense or income
@JsonKey(defaultValue: EntryType.expense)
EntryType type; EntryType type;
/// Actual entry data /// Actual entry data
@JsonKey(defaultValue: EntryData.unknown)
EntryData data; EntryData data;
/// Date of entry creation /// Date of entry creation
@JsonKey(defaultValue: DateTime.now)
DateTime date; DateTime date;
/// Selected category /// Selected category
@JsonKey(defaultValue: WalletCategory.unknown)
WalletCategory category; WalletCategory category;
/// Unique entry ID /// Unique entry ID
int id; @JsonKey(required: true, disallowNullValue: true)
final int id;
/// Connects the generated toJson method /// Converts the data in this instance into a Map
Map<String, dynamic> toJson() => _$WalletSingleEntryToJson(this); Map<String, dynamic> toJson() => _$WalletSingleEntryToJson(this);
} }

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: (C) 2024 Matyáš Caras
//
// SPDX-License-Identifier: AGPL-3.0-only
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'wallet_entry.dart'; part of 'wallet_entry.dart';
@ -10,15 +6,27 @@ part of 'wallet_entry.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
WalletSingleEntry _$WalletSingleEntryFromJson(Map<String, dynamic> json) => WalletSingleEntry _$WalletSingleEntryFromJson(Map<String, dynamic> json) {
WalletSingleEntry( $checkKeys(
data: EntryData.fromJson(json['data'] as Map<String, dynamic>), json,
type: $enumDecode(_$EntryTypeEnumMap, json['type']), requiredKeys: const ['id'],
date: DateTime.parse(json['date'] as String), disallowNullValues: const ['id'],
category: );
WalletCategory.fromJson(json['category'] as Map<String, dynamic>), return WalletSingleEntry(
id: json['id'] as int, data: json['data'] == null
); ? EntryData.unknown()
: EntryData.fromJson(json['data'] as Map<String, dynamic>),
type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ??
EntryType.expense,
date: json['date'] == null
? DateTime.now()
: DateTime.parse(json['date'] as String),
category: json['category'] == null
? WalletCategory.unknown()
: WalletCategory.fromJson(json['category'] as Map<String, dynamic>),
id: (json['id'] as num).toInt(),
);
}
Map<String, dynamic> _$WalletSingleEntryToJson(WalletSingleEntry instance) => Map<String, dynamic> _$WalletSingleEntryToJson(WalletSingleEntry instance) =>
<String, dynamic>{ <String, dynamic>{