Expenses
This commit is contained in:
parent
d202a9cfcf
commit
cf4eb360bb
|
|
@ -275,7 +275,9 @@ abstract class ExpenseEntity extends Object
|
||||||
return true;
|
return true;
|
||||||
} else if (status.id == kExpenseStatusPending && isPending) {
|
} else if (status.id == kExpenseStatusPending && isPending) {
|
||||||
return true;
|
return true;
|
||||||
} else if (status.id == kExpenseStatusLogged && !isInvoiced && !isPending) {
|
} else if (status.id == kExpenseStatusLogged &&
|
||||||
|
!isInvoiced &&
|
||||||
|
!isPending) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -292,6 +294,10 @@ abstract class ExpenseEntity extends Object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isBetween(String startDate, String endDate) {
|
||||||
|
return startDate.compareTo(endDate) <= 0 && endDate.compareTo(endDate) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double get listDisplayAmount => null;
|
double get listDisplayAmount => null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,14 +320,6 @@ List<ChartDataGroup> chartPayments(
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
var memoizedChartTasks = memo6((CompanyEntity company,
|
|
||||||
DashboardUIState settings,
|
|
||||||
BuiltMap<int, TaskEntity> taskMap,
|
|
||||||
BuiltMap<int, InvoiceEntity> invoiceMap,
|
|
||||||
BuiltMap<int, ProjectEntity> projectMap,
|
|
||||||
BuiltMap<int, ClientEntity> clientMap) =>
|
|
||||||
chartTasks(company, settings, taskMap, invoiceMap, projectMap, clientMap));
|
|
||||||
|
|
||||||
List<ChartDataGroup> chartTasks(
|
List<ChartDataGroup> chartTasks(
|
||||||
CompanyEntity company,
|
CompanyEntity company,
|
||||||
DashboardUIState settings,
|
DashboardUIState settings,
|
||||||
|
|
@ -436,3 +428,126 @@ List<ChartDataGroup> chartTasks(
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var memoizedChartTasks = memo6((CompanyEntity company,
|
||||||
|
DashboardUIState settings,
|
||||||
|
BuiltMap<int, TaskEntity> taskMap,
|
||||||
|
BuiltMap<int, InvoiceEntity> invoiceMap,
|
||||||
|
BuiltMap<int, ProjectEntity> projectMap,
|
||||||
|
BuiltMap<int, ClientEntity> clientMap) =>
|
||||||
|
chartTasks(company, settings, taskMap, invoiceMap, projectMap, clientMap));
|
||||||
|
|
||||||
|
List<ChartDataGroup> chartExpenses(
|
||||||
|
CompanyEntity company,
|
||||||
|
DashboardUIState settings,
|
||||||
|
BuiltMap<int, InvoiceEntity> invoiceMap,
|
||||||
|
BuiltMap<int, ExpenseEntity> expenseMap) {
|
||||||
|
const STATUS_LOGGED = 'logged';
|
||||||
|
const STATUS_PENDING = 'pending';
|
||||||
|
const STATUS_INVOICED = 'invoiced';
|
||||||
|
const STATUS_PAID = 'paid';
|
||||||
|
|
||||||
|
final Map<String, int> counts = {
|
||||||
|
STATUS_LOGGED: 0,
|
||||||
|
STATUS_PENDING: 0,
|
||||||
|
STATUS_INVOICED: 0,
|
||||||
|
STATUS_PAID: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
final Map<String, Map<String, double>> totals = {
|
||||||
|
STATUS_LOGGED: {},
|
||||||
|
STATUS_PENDING: {},
|
||||||
|
STATUS_INVOICED: {},
|
||||||
|
STATUS_PAID: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expenseMap.forEach((int, expense) {
|
||||||
|
final currencyId = expense.expenseCurrencyId;
|
||||||
|
final date = expense.expenseDate;
|
||||||
|
final amount = expense.amountWithTax;
|
||||||
|
|
||||||
|
if (expense.isDeleted) {
|
||||||
|
// skip it
|
||||||
|
} else if (!expense.isBetween(
|
||||||
|
settings.startDate(company), settings.endDate(company))) {
|
||||||
|
// skip it
|
||||||
|
} else if (settings.currencyId > 0 && settings.currencyId != currencyId) {
|
||||||
|
// skip it
|
||||||
|
} else {
|
||||||
|
if (totals[STATUS_LOGGED][date] == null) {
|
||||||
|
totals[STATUS_LOGGED][date] = 0.0;
|
||||||
|
totals[STATUS_PENDING][date] = 0.0;
|
||||||
|
totals[STATUS_INVOICED][date] = 0.0;
|
||||||
|
totals[STATUS_PAID][date] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expense.isInvoiced) {
|
||||||
|
final invoice = invoiceMap[expense.invoiceId] ?? InvoiceEntity();
|
||||||
|
if (invoice.isPaid) {
|
||||||
|
totals[STATUS_PAID][date] += amount;
|
||||||
|
counts[STATUS_PAID]++;
|
||||||
|
} else {
|
||||||
|
totals[STATUS_INVOICED][date] += amount;
|
||||||
|
counts[STATUS_INVOICED]++;
|
||||||
|
}
|
||||||
|
} else if (expense.isPending) {
|
||||||
|
totals[STATUS_PENDING][date] += amount;
|
||||||
|
counts[STATUS_PENDING]++;
|
||||||
|
} else {
|
||||||
|
totals[STATUS_LOGGED][date] += amount;
|
||||||
|
counts[STATUS_LOGGED]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final ChartDataGroup loggedData = ChartDataGroup(STATUS_LOGGED);
|
||||||
|
final ChartDataGroup pendingData = ChartDataGroup(STATUS_PENDING);
|
||||||
|
final ChartDataGroup invoicedData = ChartDataGroup(STATUS_INVOICED);
|
||||||
|
final ChartDataGroup paidData = ChartDataGroup(STATUS_PAID);
|
||||||
|
|
||||||
|
var date = DateTime.parse(settings.startDate(company));
|
||||||
|
final endDate = DateTime.parse(settings.endDate(company));
|
||||||
|
|
||||||
|
while (!date.isAfter(endDate)) {
|
||||||
|
final key = convertDateTimeToSqlDate(date);
|
||||||
|
if (totals[STATUS_LOGGED].containsKey(key)) {
|
||||||
|
loggedData.rawSeries
|
||||||
|
.add(ChartMoneyData(date, totals[STATUS_LOGGED][key]));
|
||||||
|
loggedData.total += totals[STATUS_LOGGED][key];
|
||||||
|
pendingData.rawSeries
|
||||||
|
.add(ChartMoneyData(date, totals[STATUS_PENDING][key]));
|
||||||
|
pendingData.total += totals[STATUS_PENDING][key];
|
||||||
|
invoicedData.rawSeries
|
||||||
|
.add(ChartMoneyData(date, totals[STATUS_INVOICED][key]));
|
||||||
|
invoicedData.total += totals[STATUS_INVOICED][key];
|
||||||
|
paidData.rawSeries.add(ChartMoneyData(date, totals[STATUS_PAID][key]));
|
||||||
|
paidData.total += totals[STATUS_PAID][key];
|
||||||
|
} else {
|
||||||
|
loggedData.rawSeries.add(ChartMoneyData(date, 0.0));
|
||||||
|
pendingData.rawSeries.add(ChartMoneyData(date, 0.0));
|
||||||
|
invoicedData.rawSeries.add(ChartMoneyData(date, 0.0));
|
||||||
|
paidData.rawSeries.add(ChartMoneyData(date, 0.0));
|
||||||
|
}
|
||||||
|
date = date.add(Duration(days: 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
loggedData.average =
|
||||||
|
round(loggedData.total ?? 0 / counts[STATUS_LOGGED] ?? 0, 2);
|
||||||
|
invoicedData.average =
|
||||||
|
round(invoicedData.total ?? 0 / counts[STATUS_INVOICED] ?? 0, 2);
|
||||||
|
paidData.average = round(paidData.total ?? 0 / counts[STATUS_PAID] ?? 0, 2);
|
||||||
|
|
||||||
|
final List<ChartDataGroup> data = [
|
||||||
|
loggedData,
|
||||||
|
invoicedData,
|
||||||
|
paidData,
|
||||||
|
];
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var memoizedChartExpenses = memo4((CompanyEntity company,
|
||||||
|
DashboardUIState settings,
|
||||||
|
BuiltMap<int, InvoiceEntity> invoiceMap,
|
||||||
|
BuiltMap<int, ExpenseEntity> expenseMap) =>
|
||||||
|
chartExpenses(company, settings, invoiceMap, expenseMap));
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,30 @@ class DashboardPanels extends StatelessWidget {
|
||||||
title: AppLocalization.of(context).tasks);
|
title: AppLocalization.of(context).tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _expenseChart(BuildContext context) {
|
||||||
|
final settings = viewModel.dashboardUIState;
|
||||||
|
final state = viewModel.state;
|
||||||
|
final isLoaded = state.expenseState.isLoaded;
|
||||||
|
final currentData = memoizedChartExpenses(state.selectedCompany, settings,
|
||||||
|
state.invoiceState.map, state.expenseState.map);
|
||||||
|
|
||||||
|
List<ChartDataGroup> previousData;
|
||||||
|
if (settings.enableComparison) {
|
||||||
|
previousData = memoizedChartExpenses(
|
||||||
|
state.selectedCompany,
|
||||||
|
settings.rebuild((b) => b..offset += 1),
|
||||||
|
state.invoiceState.map,
|
||||||
|
state.expenseState.map);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buildChart(
|
||||||
|
context: context,
|
||||||
|
currentData: currentData,
|
||||||
|
previousData: previousData,
|
||||||
|
isLoaded: isLoaded,
|
||||||
|
title: AppLocalization.of(context).expenses);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = viewModel.state;
|
final state = viewModel.state;
|
||||||
|
|
@ -300,6 +324,9 @@ class DashboardPanels extends StatelessWidget {
|
||||||
company.isModuleEnabled(EntityType.task)
|
company.isModuleEnabled(EntityType.task)
|
||||||
? _taskChart(context)
|
? _taskChart(context)
|
||||||
: SizedBox(),
|
: SizedBox(),
|
||||||
|
company.isModuleEnabled(EntityType.expense)
|
||||||
|
? _expenseChart(context)
|
||||||
|
: SizedBox(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ class ExpenseList extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final localization = AppLocalization.of(context);
|
final localization = AppLocalization.of(context);
|
||||||
final listState = viewModel.listState;
|
final listState = viewModel.listState;
|
||||||
final filteredClientId = listState.filterEntityId;
|
|
||||||
final state = viewModel.state;
|
final state = viewModel.state;
|
||||||
final widgets = <Widget>[];
|
final widgets = <Widget>[];
|
||||||
BaseEntity filteredEntity;
|
BaseEntity filteredEntity;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class _ExpenseViewState extends State<ExpenseView>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final localization = AppLocalization.of(context);
|
//final localization = AppLocalization.of(context);
|
||||||
final viewModel = widget.viewModel;
|
final viewModel = widget.viewModel;
|
||||||
|
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue