Charts
This commit is contained in:
parent
949f2b4985
commit
be449c1fc7
|
|
@ -1,45 +0,0 @@
|
|||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/client_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/company_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/invoice_model.dart';
|
||||
import 'package:invoiceninja_flutter/redux/reports/reports_state.dart';
|
||||
import 'package:invoiceninja_flutter/ui/reports/reports_screen.dart';
|
||||
|
||||
ReportResult invoiceReport({
|
||||
CompanyEntity company,
|
||||
ReportsUIState reportsUIState,
|
||||
BuiltMap<String, ClientEntity> clientMap,
|
||||
BuiltMap<String, InvoiceEntity> invoiceMap,
|
||||
}) {
|
||||
final List<List<ReportElement>> data = [];
|
||||
final Map<String, double> totalAmounts = {};
|
||||
final Map<String, double> totalPaid = {};
|
||||
|
||||
for (var invoiceId in invoiceMap.keys) {
|
||||
final invoice = invoiceMap[invoiceId];
|
||||
final client = clientMap[invoice.clientId];
|
||||
|
||||
if (invoice.isDeleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!totalAmounts.containsKey(client.id)) {
|
||||
totalAmounts[client.id] = 0;
|
||||
totalPaid[client.id] = 0;
|
||||
}
|
||||
totalAmounts[client.id] += invoice.amount;
|
||||
totalPaid[client.id] += invoice.amount - invoice.balance;
|
||||
}
|
||||
|
||||
for (var clientId in clientMap.keys) {
|
||||
final client = clientMap[clientId];
|
||||
if (client.isDeleted) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return ReportResult(
|
||||
columns: [],
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
|
|
@ -137,24 +137,25 @@ class ReportsScreen extends StatelessWidget {
|
|||
))
|
||||
.toList(),
|
||||
),
|
||||
AppDropdownButton<String>(
|
||||
labelText: localization.chart,
|
||||
value: reportsUIState.chart,
|
||||
blankValue: '',
|
||||
showBlank: true,
|
||||
onChanged: (dynamic value) {
|
||||
viewModel.onSettingsChanged(chart: value);
|
||||
},
|
||||
items: reportResult.columns
|
||||
.where((column) =>
|
||||
getReportColumnType(column) ==
|
||||
ReportColumnType.number)
|
||||
.map((column) => DropdownMenuItem(
|
||||
child: Text(localization.lookup(column)),
|
||||
value: column,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
if (reportsUIState.group.isNotEmpty)
|
||||
AppDropdownButton<String>(
|
||||
labelText: localization.chart,
|
||||
value: reportsUIState.chart,
|
||||
blankValue: '',
|
||||
showBlank: true,
|
||||
onChanged: (dynamic value) {
|
||||
viewModel.onSettingsChanged(chart: value);
|
||||
},
|
||||
items: reportResult.columns
|
||||
.where((column) =>
|
||||
getReportColumnType(column) ==
|
||||
ReportColumnType.number)
|
||||
.map((column) => DropdownMenuItem(
|
||||
child: Text(localization.lookup(column)),
|
||||
value: column,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
if (hasCustomDate) ...[
|
||||
DatePicker(
|
||||
labelText: localization.startDate,
|
||||
|
|
@ -333,7 +334,7 @@ class _ReportDataTableState extends State<ReportDataTable> {
|
|||
state.uiState.reportsUIState.filters
|
||||
.rebuild((b) => b..addAll({column: value})));
|
||||
}),
|
||||
...reportResult.tableRows(context),
|
||||
...reportResult.tableRows(context, widget.viewModel),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -364,9 +365,9 @@ ReportColumnType getReportColumnType(String column) {
|
|||
|
||||
class ReportResult {
|
||||
ReportResult({
|
||||
this.columns,
|
||||
this.allColumns,
|
||||
this.data,
|
||||
@required this.columns,
|
||||
@required this.allColumns,
|
||||
@required this.data,
|
||||
});
|
||||
|
||||
final List<String> columns;
|
||||
|
|
@ -611,7 +612,7 @@ class ReportResult {
|
|||
]);
|
||||
}
|
||||
|
||||
List<DataRow> tableRows(BuildContext context) {
|
||||
List<DataRow> tableRows(BuildContext context, ReportsScreenVM viewModel) {
|
||||
final rows = <DataRow>[];
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final reportState = store.state.uiState.reportsUIState;
|
||||
|
|
@ -629,43 +630,7 @@ class ReportResult {
|
|||
rows.add(DataRow(cells: cells));
|
||||
}
|
||||
} else {
|
||||
final Map<String, Map<String, double>> totals = {};
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
final row = data[i];
|
||||
for (var j = 0; j < row.length; j++) {
|
||||
final cell = row[j];
|
||||
final column = columns[j];
|
||||
final columnIndex = columns.indexOf(groupBy);
|
||||
|
||||
String value = row[columnIndex].renderText(context, column);
|
||||
|
||||
if (getReportColumnType(reportState.group) ==
|
||||
ReportColumnType.dateTime) {
|
||||
value = convertDateTimeToSqlDate(DateTime.tryParse(value));
|
||||
if (reportState.subgroup == kReportGroupYear) {
|
||||
value = value.substring(0, 4) + '-01-01';
|
||||
} else if (reportState.subgroup == kReportGroupMonth) {
|
||||
value = value.substring(0, 7) + '-01';
|
||||
}
|
||||
}
|
||||
|
||||
if (!totals.containsKey(value)) {
|
||||
totals[value] = {'count': 0};
|
||||
}
|
||||
if (column == groupBy) {
|
||||
totals[value]['count'] += 1;
|
||||
}
|
||||
if (cell is ReportNumberValue) {
|
||||
if (!totals[value].containsKey(column)) {
|
||||
totals[value][column] = 0;
|
||||
}
|
||||
totals[value][column] += cell.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totals.forEach((group, values) {
|
||||
viewModel.reportTotals.forEach((group, values) {
|
||||
final cells = <DataCell>[];
|
||||
for (var column in columns) {
|
||||
String value = '';
|
||||
|
|
@ -800,7 +765,6 @@ class ReportResult {
|
|||
rows.add(DataRow(cells: cells));
|
||||
});
|
||||
|
||||
print('## TOTALS: $totals');
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/company_model.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/reports/reports_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/reports/reports_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/reports/client_report.dart';
|
||||
import 'package:invoiceninja_flutter/ui/reports/reports_screen.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/dialogs.dart';
|
||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:memoize/memoize.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
|
||||
import 'reports_screen.dart';
|
||||
|
|
@ -39,11 +43,13 @@ class ReportsScreenVM {
|
|||
@required this.onReportColumnsChanged,
|
||||
@required this.onReportFiltersChanged,
|
||||
@required this.onReportSorted,
|
||||
@required this.reportTotals,
|
||||
@required this.reportResult,
|
||||
});
|
||||
|
||||
final AppState state;
|
||||
final ReportResult reportResult;
|
||||
final Map<String, Map<String, double>> reportTotals;
|
||||
final Function(BuildContext, List<String>) onReportColumnsChanged;
|
||||
final Function(BuildContext, BuiltMap<String, String>) onReportFiltersChanged;
|
||||
final Function(int, bool) onReportSorted;
|
||||
|
|
@ -73,9 +79,13 @@ class ReportsScreenVM {
|
|||
break;
|
||||
}
|
||||
|
||||
print('## TOTALS: ${memoizedReportTotals(reportResult, state.uiState.reportsUIState)}');
|
||||
|
||||
return ReportsScreenVM(
|
||||
state: state,
|
||||
reportResult: reportResult,
|
||||
reportTotals:
|
||||
memoizedReportTotals(reportResult, state.uiState.reportsUIState),
|
||||
onReportSorted: (index, ascending) {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: state.uiState.reportsUIState.report,
|
||||
|
|
@ -156,3 +166,59 @@ class ReportsScreenVM {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
var memoizedReportTotals = memo2((
|
||||
ReportResult reportResult,
|
||||
ReportsUIState reportUIState,
|
||||
) =>
|
||||
calculateReportTotals(
|
||||
reportResult: reportResult, reportUIState: reportUIState));
|
||||
|
||||
Map<String, Map<String, double>> calculateReportTotals({
|
||||
ReportResult reportResult,
|
||||
ReportsUIState reportUIState,
|
||||
}) {
|
||||
if (reportUIState.group.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, Map<String, double>> totals = {};
|
||||
final data = reportResult.data;
|
||||
final columns = reportResult.columns;
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
final row = data[i];
|
||||
for (var j = 0; j < row.length; j++) {
|
||||
final cell = row[j];
|
||||
final column = columns[j];
|
||||
final columnIndex = columns.indexOf(reportUIState.group);
|
||||
|
||||
String group = row[columnIndex].value;
|
||||
|
||||
if (getReportColumnType(reportUIState.group) ==
|
||||
ReportColumnType.dateTime) {
|
||||
group = convertDateTimeToSqlDate(DateTime.tryParse(group));
|
||||
if (reportUIState.subgroup == kReportGroupYear) {
|
||||
group = group.substring(0, 4) + '-01-01';
|
||||
} else if (reportUIState.subgroup == kReportGroupMonth) {
|
||||
group = group.substring(0, 7) + '-01';
|
||||
}
|
||||
}
|
||||
|
||||
if (!totals.containsKey(group)) {
|
||||
totals[group] = {'count': 0};
|
||||
}
|
||||
if (column == reportUIState.group) {
|
||||
totals[group]['count'] += 1;
|
||||
}
|
||||
if (cell is ReportNumberValue) {
|
||||
if (!totals[group].containsKey(column)) {
|
||||
totals[group][column] = 0;
|
||||
}
|
||||
totals[group][column] += cell.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totals;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue