161 lines
4.6 KiB
Dart
161 lines
4.6 KiB
Dart
import 'dart:collection';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:pshared/models/payment/operation.dart';
|
|
import 'package:pshared/models/payment/payment.dart';
|
|
import 'package:pshared/models/payment/source_type.dart';
|
|
import 'package:pshared/models/payment/status.dart';
|
|
import 'package:pshared/provider/payment/payments.dart';
|
|
|
|
import 'package:pweb/models/state/load_more_state.dart';
|
|
import 'package:pweb/utils/report/operations/operations.dart';
|
|
import 'package:pweb/utils/report/payment_mapper.dart';
|
|
import 'package:pweb/utils/report/source_filter.dart';
|
|
|
|
|
|
class ReportOperationsController extends ChangeNotifier {
|
|
PaymentsProvider? _payments;
|
|
PaymentSourceType? _sourceType;
|
|
Set<String> _sourceRefs = const <String>{};
|
|
DateTimeRange? _selectedRange;
|
|
final Set<OperationStatus> _selectedStatuses = {};
|
|
List<Payment> _paymentItems = const [];
|
|
List<OperationItem> _operations = const [];
|
|
List<OperationItem> _filtered = const [];
|
|
|
|
List<OperationItem> get operations => _operations;
|
|
List<OperationItem> get filteredOperations => _filtered;
|
|
DateTimeRange? get selectedRange => _selectedRange;
|
|
Set<OperationStatus> get selectedStatuses =>
|
|
UnmodifiableSetView(_selectedStatuses);
|
|
|
|
bool get isLoading => _payments?.isLoading ?? false;
|
|
Exception? get error => _payments?.error;
|
|
LoadMoreState get loadMoreState {
|
|
if (_payments?.isLoadingMore ?? false) {
|
|
return LoadMoreState.loading;
|
|
}
|
|
if (_payments?.canLoadMore ?? false) {
|
|
return LoadMoreState.available;
|
|
}
|
|
return LoadMoreState.hidden;
|
|
}
|
|
|
|
void update(
|
|
PaymentsProvider provider, {
|
|
PaymentSourceType? sourceType,
|
|
String? sourceRef,
|
|
List<String>? sourceRefs,
|
|
}) {
|
|
if (!identical(_payments, provider)) {
|
|
_payments = provider;
|
|
}
|
|
_sourceType = sourceType;
|
|
final effectiveSourceRefs =
|
|
sourceRefs ??
|
|
(sourceRef == null ? const <String>[] : <String>[sourceRef]);
|
|
_sourceRefs = _normalizeRefs(effectiveSourceRefs);
|
|
_rebuildOperations();
|
|
}
|
|
|
|
void setRange(DateTimeRange? range) {
|
|
if (_isSameRange(_selectedRange, range)) return;
|
|
_selectedRange = range;
|
|
_rebuildFiltered();
|
|
}
|
|
|
|
void toggleStatus(OperationStatus status) {
|
|
if (_selectedStatuses.contains(status)) {
|
|
_selectedStatuses.remove(status);
|
|
} else {
|
|
_selectedStatuses.add(status);
|
|
}
|
|
_rebuildFiltered();
|
|
}
|
|
|
|
void clearFilters() {
|
|
if (_selectedRange == null && _selectedStatuses.isEmpty) return;
|
|
_selectedRange = null;
|
|
_selectedStatuses.clear();
|
|
_rebuildFiltered();
|
|
}
|
|
|
|
Future<void> refresh() async {
|
|
await _payments?.refresh();
|
|
}
|
|
|
|
Future<void> loadMore() async {
|
|
await _payments?.loadMore();
|
|
}
|
|
|
|
void _rebuildOperations() {
|
|
_paymentItems = _payments?.payments ?? const [];
|
|
_operations = _paymentItems
|
|
.where(_matchesCurrentSource)
|
|
.map(mapPaymentToOperation)
|
|
.toList();
|
|
_rebuildFiltered(notify: true);
|
|
}
|
|
|
|
void _rebuildFiltered({bool notify = true}) {
|
|
_filtered = _applyFilters(sortOperations(_operations));
|
|
if (notify) {
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
List<OperationItem> _applyFilters(List<OperationItem> operations) {
|
|
if (_selectedRange == null && _selectedStatuses.isEmpty) {
|
|
return operations;
|
|
}
|
|
|
|
final filtered = operations.where((op) {
|
|
final statusMatch =
|
|
_selectedStatuses.isEmpty || _selectedStatuses.contains(op.status);
|
|
final dateMatch =
|
|
_selectedRange == null ||
|
|
isUnknownDate(op.date) ||
|
|
(op.date.isAfter(
|
|
_selectedRange!.start.subtract(const Duration(seconds: 1)),
|
|
) &&
|
|
op.date.isBefore(
|
|
_selectedRange!.end.add(const Duration(seconds: 1)),
|
|
));
|
|
return statusMatch && dateMatch;
|
|
}).toList();
|
|
|
|
return filtered;
|
|
}
|
|
|
|
bool _matchesCurrentSource(Payment payment) {
|
|
final sourceType = _sourceType;
|
|
if (sourceType == null || _sourceRefs.isEmpty) return true;
|
|
for (final sourceRef in _sourceRefs) {
|
|
if (paymentMatchesSource(
|
|
payment,
|
|
sourceType: sourceType,
|
|
sourceRef: sourceRef,
|
|
)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Set<String> _normalizeRefs(List<String> refs) {
|
|
final normalized = refs
|
|
.map((value) => value.trim())
|
|
.where((value) => value.isNotEmpty)
|
|
.toSet();
|
|
return normalized;
|
|
}
|
|
|
|
bool _isSameRange(DateTimeRange? left, DateTimeRange? right) {
|
|
if (left == null && right == null) return true;
|
|
if (left == null || right == null) return false;
|
|
return left.start.isAtSameMomentAs(right.start) &&
|
|
left.end.isAtSameMomentAs(right.end);
|
|
}
|
|
}
|