From 4da6a12871c67b4ea0a8ef9896c7040e226a93bf Mon Sep 17 00:00:00 2001 From: Prasanna Anbazhagan Date: Wed, 3 Jul 2024 01:20:36 +0530 Subject: [PATCH] added expense and aggregated expense service --- lib/api/apis/aggregated_expense_api.dart | 12 +- lib/api/apis/expense_api.dart | 19 +- lib/api/db/app_database.dart | 12 +- lib/api/db/app_database.g.dart | 208 +++++++++---------- lib/domain/models/aggregated_expense.dart | 19 ++ lib/domain/models/expense.dart | 21 ++ lib/domain/models/expense_tag.dart | 18 ++ lib/domain/services/expense_service.dart | 122 +++++++++++ lib/domain/services/expense_tag_service.dart | 45 ++++ 9 files changed, 357 insertions(+), 119 deletions(-) create mode 100644 lib/domain/models/aggregated_expense.dart create mode 100644 lib/domain/models/expense.dart create mode 100644 lib/domain/models/expense_tag.dart create mode 100644 lib/domain/services/expense_service.dart create mode 100644 lib/domain/services/expense_tag_service.dart diff --git a/lib/api/apis/aggregated_expense_api.dart b/lib/api/apis/aggregated_expense_api.dart index b351268..5e1ab85 100644 --- a/lib/api/apis/aggregated_expense_api.dart +++ b/lib/api/apis/aggregated_expense_api.dart @@ -13,9 +13,9 @@ class AggregatedExpenseApi { required final List tags}) async { return _db.into(_db.aggregatedExpenseTable).insert( AggregatedExpenseTableCompanion.insert( - amount: Value(amount), - tags: Value(tags.join(',')), - createdMonthDate: Value(monthDate))); + amount: amount, + tags: tags.join(','), + createdMonthDate: monthDate)); } Future> get() async { @@ -30,6 +30,12 @@ class AggregatedExpenseApi { .getSingle(); } + Future getByMonthAndTag({required final DateTime monthDate, required final List tags}) async { + return (_db.select(_db.aggregatedExpenseTable) + ..where((t) => t.createdMonthDate.equals(monthDate) & t.tags.equals(tags.join(',')))) + .getSingleOrNull(); + } + Future update( {required final int id, required final double amount, diff --git a/lib/api/apis/expense_api.dart b/lib/api/apis/expense_api.dart index f0f0fb8..0c99d16 100644 --- a/lib/api/apis/expense_api.dart +++ b/lib/api/apis/expense_api.dart @@ -12,10 +12,10 @@ class ExpenseApi { required final DateTime createdOn, required final List tags}) async { return _db.into(_db.expenseTable).insert(ExpenseTableCompanion.insert( - amount: Value(amount), + amount: amount, description: Value(description), - tags: Value(tags.join(',')), - createdOn: Value(createdOn))); + tags: tags.join(','), + createdOn: createdOn)); } Future> get() async { @@ -24,6 +24,16 @@ class ExpenseApi { .get(); } + Future> getByMonth( + {required final DateTime monthDate}) async { + return (_db.select(_db.expenseTable) + ..where((t) => + t.createdOn.year.equals(monthDate.year) & + t.createdOn.month.equals(monthDate.month)) + ..orderBy([(t) => OrderingTerm(expression: t.createdOn)])) + .get(); + } + Future getBy({required final int id}) async { return (_db.select(_db.expenseTable)..where((t) => t.id.equals(id))) .getSingle(); @@ -44,7 +54,6 @@ class ExpenseApi { } Future deleteBy({required final int id}) async { - return (_db.delete(_db.expenseTable)..where((t) => t.id.equals(id))) - .go(); + return (_db.delete(_db.expenseTable)..where((t) => t.id.equals(id))).go(); } } diff --git a/lib/api/db/app_database.dart b/lib/api/db/app_database.dart index 0d6172b..98ca7e4 100644 --- a/lib/api/db/app_database.dart +++ b/lib/api/db/app_database.dart @@ -260,24 +260,24 @@ class ExpenseTable extends Table { TextColumn get description => text().nullable().named('DESCRIPTION')(); - RealColumn get amount => real().nullable().named('AMOUNT')(); + RealColumn get amount => real().named('AMOUNT')(); - TextColumn get tags => text().nullable().named('TAGS')(); + TextColumn get tags => text().named('TAGS')(); DateTimeColumn get createdOn => - dateTime().nullable().named('CREATED_ON')(); + dateTime().named('CREATED_ON')(); } @DataClassName('AggregatedExpenseDO') class AggregatedExpenseTable extends Table { IntColumn get id => integer().named('ID').autoIncrement()(); - RealColumn get amount => real().nullable().named('AMOUNT')(); + RealColumn get amount => real().named('AMOUNT')(); - TextColumn get tags => text().nullable().named('TAGS')(); + TextColumn get tags => text().named('TAGS')(); DateTimeColumn get createdMonthDate => - dateTime().nullable().named('CREATED_MONTH_DATE')(); + dateTime().named('CREATED_MONTH_DATE')(); } @DataClassName('ExpenseTagDO') diff --git a/lib/api/db/app_database.g.dart b/lib/api/db/app_database.g.dart index 6e97a9e..05e51e7 100644 --- a/lib/api/db/app_database.g.dart +++ b/lib/api/db/app_database.g.dart @@ -2458,19 +2458,19 @@ class $ExpenseTableTable extends ExpenseTable static const VerificationMeta _amountMeta = const VerificationMeta('amount'); @override late final GeneratedColumn amount = GeneratedColumn( - 'AMOUNT', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'AMOUNT', aliasedName, false, + type: DriftSqlType.double, requiredDuringInsert: true); static const VerificationMeta _tagsMeta = const VerificationMeta('tags'); @override late final GeneratedColumn tags = GeneratedColumn( - 'TAGS', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'TAGS', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); static const VerificationMeta _createdOnMeta = const VerificationMeta('createdOn'); @override late final GeneratedColumn createdOn = GeneratedColumn( - 'CREATED_ON', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'CREATED_ON', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); @override List get $columns => [id, description, amount, tags, createdOn]; @@ -2496,14 +2496,20 @@ class $ExpenseTableTable extends ExpenseTable if (data.containsKey('AMOUNT')) { context.handle(_amountMeta, amount.isAcceptableOrUnknown(data['AMOUNT']!, _amountMeta)); + } else if (isInserting) { + context.missing(_amountMeta); } if (data.containsKey('TAGS')) { context.handle( _tagsMeta, tags.isAcceptableOrUnknown(data['TAGS']!, _tagsMeta)); + } else if (isInserting) { + context.missing(_tagsMeta); } if (data.containsKey('CREATED_ON')) { context.handle(_createdOnMeta, createdOn.isAcceptableOrUnknown(data['CREATED_ON']!, _createdOnMeta)); + } else if (isInserting) { + context.missing(_createdOnMeta); } return context; } @@ -2519,11 +2525,11 @@ class $ExpenseTableTable extends ExpenseTable description: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}DESCRIPTION']), amount: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}AMOUNT']), + .read(DriftSqlType.double, data['${effectivePrefix}AMOUNT'])!, tags: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}TAGS']), + .read(DriftSqlType.string, data['${effectivePrefix}TAGS'])!, createdOn: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}CREATED_ON']), + .read(DriftSqlType.dateTime, data['${effectivePrefix}CREATED_ON'])!, ); } @@ -2536,15 +2542,15 @@ class $ExpenseTableTable extends ExpenseTable class ExpenseDO extends DataClass implements Insertable { final int id; final String? description; - final double? amount; - final String? tags; - final DateTime? createdOn; + final double amount; + final String tags; + final DateTime createdOn; const ExpenseDO( {required this.id, this.description, - this.amount, - this.tags, - this.createdOn}); + required this.amount, + required this.tags, + required this.createdOn}); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2552,15 +2558,9 @@ class ExpenseDO extends DataClass implements Insertable { if (!nullToAbsent || description != null) { map['DESCRIPTION'] = Variable(description); } - if (!nullToAbsent || amount != null) { - map['AMOUNT'] = Variable(amount); - } - if (!nullToAbsent || tags != null) { - map['TAGS'] = Variable(tags); - } - if (!nullToAbsent || createdOn != null) { - map['CREATED_ON'] = Variable(createdOn); - } + map['AMOUNT'] = Variable(amount); + map['TAGS'] = Variable(tags); + map['CREATED_ON'] = Variable(createdOn); return map; } @@ -2570,12 +2570,9 @@ class ExpenseDO extends DataClass implements Insertable { description: description == null && nullToAbsent ? const Value.absent() : Value(description), - amount: - amount == null && nullToAbsent ? const Value.absent() : Value(amount), - tags: tags == null && nullToAbsent ? const Value.absent() : Value(tags), - createdOn: createdOn == null && nullToAbsent - ? const Value.absent() - : Value(createdOn), + amount: Value(amount), + tags: Value(tags), + createdOn: Value(createdOn), ); } @@ -2585,9 +2582,9 @@ class ExpenseDO extends DataClass implements Insertable { return ExpenseDO( id: serializer.fromJson(json['id']), description: serializer.fromJson(json['description']), - amount: serializer.fromJson(json['amount']), - tags: serializer.fromJson(json['tags']), - createdOn: serializer.fromJson(json['createdOn']), + amount: serializer.fromJson(json['amount']), + tags: serializer.fromJson(json['tags']), + createdOn: serializer.fromJson(json['createdOn']), ); } @override @@ -2596,24 +2593,24 @@ class ExpenseDO extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'description': serializer.toJson(description), - 'amount': serializer.toJson(amount), - 'tags': serializer.toJson(tags), - 'createdOn': serializer.toJson(createdOn), + 'amount': serializer.toJson(amount), + 'tags': serializer.toJson(tags), + 'createdOn': serializer.toJson(createdOn), }; } ExpenseDO copyWith( {int? id, Value description = const Value.absent(), - Value amount = const Value.absent(), - Value tags = const Value.absent(), - Value createdOn = const Value.absent()}) => + double? amount, + String? tags, + DateTime? createdOn}) => ExpenseDO( id: id ?? this.id, description: description.present ? description.value : this.description, - amount: amount.present ? amount.value : this.amount, - tags: tags.present ? tags.value : this.tags, - createdOn: createdOn.present ? createdOn.value : this.createdOn, + amount: amount ?? this.amount, + tags: tags ?? this.tags, + createdOn: createdOn ?? this.createdOn, ); @override String toString() { @@ -2643,9 +2640,9 @@ class ExpenseDO extends DataClass implements Insertable { class ExpenseTableCompanion extends UpdateCompanion { final Value id; final Value description; - final Value amount; - final Value tags; - final Value createdOn; + final Value amount; + final Value tags; + final Value createdOn; const ExpenseTableCompanion({ this.id = const Value.absent(), this.description = const Value.absent(), @@ -2656,10 +2653,12 @@ class ExpenseTableCompanion extends UpdateCompanion { ExpenseTableCompanion.insert({ this.id = const Value.absent(), this.description = const Value.absent(), - this.amount = const Value.absent(), - this.tags = const Value.absent(), - this.createdOn = const Value.absent(), - }); + required double amount, + required String tags, + required DateTime createdOn, + }) : amount = Value(amount), + tags = Value(tags), + createdOn = Value(createdOn); static Insertable custom({ Expression? id, Expression? description, @@ -2679,9 +2678,9 @@ class ExpenseTableCompanion extends UpdateCompanion { ExpenseTableCompanion copyWith( {Value? id, Value? description, - Value? amount, - Value? tags, - Value? createdOn}) { + Value? amount, + Value? tags, + Value? createdOn}) { return ExpenseTableCompanion( id: id ?? this.id, description: description ?? this.description, @@ -2959,19 +2958,19 @@ class $AggregatedExpenseTableTable extends AggregatedExpenseTable static const VerificationMeta _amountMeta = const VerificationMeta('amount'); @override late final GeneratedColumn amount = GeneratedColumn( - 'AMOUNT', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'AMOUNT', aliasedName, false, + type: DriftSqlType.double, requiredDuringInsert: true); static const VerificationMeta _tagsMeta = const VerificationMeta('tags'); @override late final GeneratedColumn tags = GeneratedColumn( - 'TAGS', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'TAGS', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); static const VerificationMeta _createdMonthDateMeta = const VerificationMeta('createdMonthDate'); @override late final GeneratedColumn createdMonthDate = - GeneratedColumn('CREATED_MONTH_DATE', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn('CREATED_MONTH_DATE', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); @override List get $columns => [id, amount, tags, createdMonthDate]; @override @@ -2991,16 +2990,22 @@ class $AggregatedExpenseTableTable extends AggregatedExpenseTable if (data.containsKey('AMOUNT')) { context.handle(_amountMeta, amount.isAcceptableOrUnknown(data['AMOUNT']!, _amountMeta)); + } else if (isInserting) { + context.missing(_amountMeta); } if (data.containsKey('TAGS')) { context.handle( _tagsMeta, tags.isAcceptableOrUnknown(data['TAGS']!, _tagsMeta)); + } else if (isInserting) { + context.missing(_tagsMeta); } if (data.containsKey('CREATED_MONTH_DATE')) { context.handle( _createdMonthDateMeta, createdMonthDate.isAcceptableOrUnknown( data['CREATED_MONTH_DATE']!, _createdMonthDateMeta)); + } else if (isInserting) { + context.missing(_createdMonthDateMeta); } return context; } @@ -3014,11 +3019,11 @@ class $AggregatedExpenseTableTable extends AggregatedExpenseTable id: attachedDatabase.typeMapping .read(DriftSqlType.int, data['${effectivePrefix}ID'])!, amount: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}AMOUNT']), + .read(DriftSqlType.double, data['${effectivePrefix}AMOUNT'])!, tags: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}TAGS']), + .read(DriftSqlType.string, data['${effectivePrefix}TAGS'])!, createdMonthDate: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}CREATED_MONTH_DATE']), + DriftSqlType.dateTime, data['${effectivePrefix}CREATED_MONTH_DATE'])!, ); } @@ -3031,36 +3036,30 @@ class $AggregatedExpenseTableTable extends AggregatedExpenseTable class AggregatedExpenseDO extends DataClass implements Insertable { final int id; - final double? amount; - final String? tags; - final DateTime? createdMonthDate; + final double amount; + final String tags; + final DateTime createdMonthDate; const AggregatedExpenseDO( - {required this.id, this.amount, this.tags, this.createdMonthDate}); + {required this.id, + required this.amount, + required this.tags, + required this.createdMonthDate}); @override Map toColumns(bool nullToAbsent) { final map = {}; map['ID'] = Variable(id); - if (!nullToAbsent || amount != null) { - map['AMOUNT'] = Variable(amount); - } - if (!nullToAbsent || tags != null) { - map['TAGS'] = Variable(tags); - } - if (!nullToAbsent || createdMonthDate != null) { - map['CREATED_MONTH_DATE'] = Variable(createdMonthDate); - } + map['AMOUNT'] = Variable(amount); + map['TAGS'] = Variable(tags); + map['CREATED_MONTH_DATE'] = Variable(createdMonthDate); return map; } AggregatedExpenseTableCompanion toCompanion(bool nullToAbsent) { return AggregatedExpenseTableCompanion( id: Value(id), - amount: - amount == null && nullToAbsent ? const Value.absent() : Value(amount), - tags: tags == null && nullToAbsent ? const Value.absent() : Value(tags), - createdMonthDate: createdMonthDate == null && nullToAbsent - ? const Value.absent() - : Value(createdMonthDate), + amount: Value(amount), + tags: Value(tags), + createdMonthDate: Value(createdMonthDate), ); } @@ -3069,10 +3068,9 @@ class AggregatedExpenseDO extends DataClass serializer ??= driftRuntimeOptions.defaultSerializer; return AggregatedExpenseDO( id: serializer.fromJson(json['id']), - amount: serializer.fromJson(json['amount']), - tags: serializer.fromJson(json['tags']), - createdMonthDate: - serializer.fromJson(json['createdMonthDate']), + amount: serializer.fromJson(json['amount']), + tags: serializer.fromJson(json['tags']), + createdMonthDate: serializer.fromJson(json['createdMonthDate']), ); } @override @@ -3080,24 +3078,22 @@ class AggregatedExpenseDO extends DataClass serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'amount': serializer.toJson(amount), - 'tags': serializer.toJson(tags), - 'createdMonthDate': serializer.toJson(createdMonthDate), + 'amount': serializer.toJson(amount), + 'tags': serializer.toJson(tags), + 'createdMonthDate': serializer.toJson(createdMonthDate), }; } AggregatedExpenseDO copyWith( {int? id, - Value amount = const Value.absent(), - Value tags = const Value.absent(), - Value createdMonthDate = const Value.absent()}) => + double? amount, + String? tags, + DateTime? createdMonthDate}) => AggregatedExpenseDO( id: id ?? this.id, - amount: amount.present ? amount.value : this.amount, - tags: tags.present ? tags.value : this.tags, - createdMonthDate: createdMonthDate.present - ? createdMonthDate.value - : this.createdMonthDate, + amount: amount ?? this.amount, + tags: tags ?? this.tags, + createdMonthDate: createdMonthDate ?? this.createdMonthDate, ); @override String toString() { @@ -3125,9 +3121,9 @@ class AggregatedExpenseDO extends DataClass class AggregatedExpenseTableCompanion extends UpdateCompanion { final Value id; - final Value amount; - final Value tags; - final Value createdMonthDate; + final Value amount; + final Value tags; + final Value createdMonthDate; const AggregatedExpenseTableCompanion({ this.id = const Value.absent(), this.amount = const Value.absent(), @@ -3136,10 +3132,12 @@ class AggregatedExpenseTableCompanion }); AggregatedExpenseTableCompanion.insert({ this.id = const Value.absent(), - this.amount = const Value.absent(), - this.tags = const Value.absent(), - this.createdMonthDate = const Value.absent(), - }); + required double amount, + required String tags, + required DateTime createdMonthDate, + }) : amount = Value(amount), + tags = Value(tags), + createdMonthDate = Value(createdMonthDate); static Insertable custom({ Expression? id, Expression? amount, @@ -3156,9 +3154,9 @@ class AggregatedExpenseTableCompanion AggregatedExpenseTableCompanion copyWith( {Value? id, - Value? amount, - Value? tags, - Value? createdMonthDate}) { + Value? amount, + Value? tags, + Value? createdMonthDate}) { return AggregatedExpenseTableCompanion( id: id ?? this.id, amount: amount ?? this.amount, diff --git a/lib/domain/models/aggregated_expense.dart b/lib/domain/models/aggregated_expense.dart new file mode 100644 index 0000000..c5f6a3f --- /dev/null +++ b/lib/domain/models/aggregated_expense.dart @@ -0,0 +1,19 @@ +import 'package:wealth_wave/api/db/app_database.dart'; + +class AggregatedExpense { + final double amount; + final List tags; + final DateTime createdMonthDate; + + AggregatedExpense._({ + required this.amount, + required this.createdMonthDate, + required this.tags, + }); + + factory AggregatedExpense.from({required AggregatedExpenseDO expenseDO}) => + AggregatedExpense._( + amount: expenseDO.amount, + createdMonthDate: expenseDO.createdMonthDate, + tags: expenseDO.tags.split(',')); +} diff --git a/lib/domain/models/expense.dart b/lib/domain/models/expense.dart new file mode 100644 index 0000000..cb5a4cf --- /dev/null +++ b/lib/domain/models/expense.dart @@ -0,0 +1,21 @@ +import 'package:wealth_wave/api/db/app_database.dart'; + +class Expense { + final double amount; + final String? description; + final List tags; + final DateTime createdOn; + + Expense._({ + required this.amount, + required this.createdOn, + required this.description, + required this.tags, + }); + + factory Expense.from({required ExpenseDO expenseDO}) => Expense._( + amount: expenseDO.amount, + createdOn: expenseDO.createdOn, + description: expenseDO.description, + tags: expenseDO.tags.split(',')); +} diff --git a/lib/domain/models/expense_tag.dart b/lib/domain/models/expense_tag.dart new file mode 100644 index 0000000..721d216 --- /dev/null +++ b/lib/domain/models/expense_tag.dart @@ -0,0 +1,18 @@ +import 'package:wealth_wave/api/db/app_database.dart'; + +class ExpenseTag { + final int id; + final String name; + final String? description; + + ExpenseTag._( + {required this.id, + required this.name, + required this.description}); + + factory ExpenseTag.from({required final ExpenseTagDO expenseTagDO}) => + ExpenseTag._( + id: expenseTagDO.id, + name: expenseTagDO.name, + description: expenseTagDO.description); +} diff --git a/lib/domain/services/expense_service.dart b/lib/domain/services/expense_service.dart new file mode 100644 index 0000000..1f29642 --- /dev/null +++ b/lib/domain/services/expense_service.dart @@ -0,0 +1,122 @@ +import 'package:wealth_wave/api/apis/aggregated_expense_api.dart'; +import 'package:wealth_wave/api/apis/expense_api.dart'; +import 'package:wealth_wave/domain/models/aggregated_expense.dart'; +import 'package:wealth_wave/domain/models/expense.dart'; + +class ExpenseService { + final ExpenseApi _expenseApi; + final AggregatedExpenseApi _aggregatedExpenseApi; + + factory ExpenseService() { + return _instance; + } + + static final ExpenseService _instance = ExpenseService._(); + + ExpenseService._( + {final ExpenseApi? expenseApi, + final AggregatedExpenseApi? aggregatedExpenseApi}) + : _expenseApi = expenseApi ?? ExpenseApi(), + _aggregatedExpenseApi = aggregatedExpenseApi ?? AggregatedExpenseApi(); + + Future createExpense( + {required final String? description, + required final double amount, + required final List tags, + required final DateTime createdOn}) => + _expenseApi + .create( + description: description, + amount: amount, + tags: tags, + createdOn: createdOn) + .then((id) => _expenseApi.getBy(id: id)) + .then((expendsDO) { + final DateTime monthDate = DateTime(createdOn.year, createdOn.month); + return _aggregatedExpenseApi + .getByMonthAndTag(monthDate: monthDate, tags: tags) + .then((aggregatedExpense) { + if (aggregatedExpense == null) { + _aggregatedExpenseApi.create( + amount: amount, monthDate: monthDate, tags: tags); + } else { + _aggregatedExpenseApi.update( + id: aggregatedExpense.id, + amount: aggregatedExpense.amount + amount, + createdOn: monthDate, + tags: tags); + } + return expendsDO; + }); + }).then((expsenseDO) => Expense.from(expenseDO: expsenseDO)); + + Future updateExpense({ + required final int id, + required final String? description, + required final double amount, + required final List tags, + required final DateTime createdOn, + }) async { + final expenseDO = await _expenseApi.getBy(id: id); + DateTime monthDate = + DateTime(expenseDO.createdOn.year, expenseDO.createdOn.month); + + final aggregatedExpense = await _aggregatedExpenseApi.getByMonthAndTag( + monthDate: monthDate, tags: tags); + if (aggregatedExpense != null) { + await _aggregatedExpenseApi.update( + id: aggregatedExpense.id, + amount: aggregatedExpense.amount - + expenseDO.amount + + amount, // Adjust the amount correctly + createdOn: monthDate, + tags: tags, + ); + } else { + await _aggregatedExpenseApi.create( + amount: amount, monthDate: monthDate, tags: tags); + } + + await _expenseApi.update( + id: id, + description: description, + amount: amount, + tags: tags, + createdOn: createdOn, + ); + + return await _expenseApi + .getBy(id: id) + .then((value) => Expense.from(expenseDO: value)); + } + + Future getById({required final int id}) => _expenseApi + .getBy(id: id) + .then((expenseDO) => Expense.from(expenseDO: expenseDO)); + + Future deleteBy({required final int id}) => + _expenseApi.getBy(id: id).then((expenseDO) { + DateTime monthDate = + DateTime(expenseDO.createdOn.year, expenseDO.createdOn.month); + _aggregatedExpenseApi + .getByMonthAndTag( + monthDate: monthDate, tags: expenseDO.tags.split(',')) + .then((aggregatedExpense) { + if (aggregatedExpense != null) { + _aggregatedExpenseApi.update( + id: aggregatedExpense.id, + amount: aggregatedExpense.amount - expenseDO.amount, + createdOn: monthDate, + tags: expenseDO.tags.split(',')); + } + return null; + }).then((value) => _expenseApi.deleteBy(id: id)); + }); + + Future> getAggregatedExpenses() => + _aggregatedExpenseApi.get().then((aggregatedExpenseDOs) => + aggregatedExpenseDOs + .map((aggregatedExpenseDO) => + AggregatedExpense.from(expenseDO: aggregatedExpenseDO)) + .toList()); +} diff --git a/lib/domain/services/expense_tag_service.dart b/lib/domain/services/expense_tag_service.dart new file mode 100644 index 0000000..e646919 --- /dev/null +++ b/lib/domain/services/expense_tag_service.dart @@ -0,0 +1,45 @@ +import 'package:wealth_wave/api/apis/expense_tag_api.dart'; +import 'package:wealth_wave/api/db/app_database.dart'; +import 'package:wealth_wave/domain/models/expense_tag.dart'; + +class ExpenseTagService { + final ExpenseTagApi _expenseTagApi; + + factory ExpenseTagService() { + return _instance; + } + + static final ExpenseTagService _instance = ExpenseTagService._(); + + ExpenseTagService._({ExpenseTagApi? expenseTagApi}) + : _expenseTagApi = expenseTagApi ?? ExpenseTagApi(); + + Future create( + {required final String name, required final String description}) => + _expenseTagApi + .create(name: name, description: description) + .then((_) => {}); + + Future> get() async { + List expenseTagDOs = await _expenseTagApi.get(); + return expenseTagDOs + .map((basketDO) => ExpenseTag.from(expenseTagDO: basketDO)) + .toList(); + } + + Future getById({required final int id}) async { + ExpenseTagDO basketDO = await _expenseTagApi.getBy(id: id); + return ExpenseTag.from(expenseTagDO: basketDO); + } + + Future update( + {required final int id, + required final String name, + required final String? description}) => + _expenseTagApi + .update(id: id, name: name, description: description) + .then((_) => {}); + + Future deleteBy({required final int id}) => + _expenseTagApi.deleteBy(id: id); +}