Files
sendico/frontend/pweb/lib/controllers/operations/report_operations.dart
2026-03-04 17:43:18 +03:00

118 lines
3.4 KiB
Dart

import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/operation.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';
class ReportOperationsController extends ChangeNotifier {
PaymentsProvider? _payments;
DateTimeRange? _selectedRange;
final Set<OperationStatus> _selectedStatuses = {};
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) {
if (!identical(_payments, provider)) {
_payments = provider;
}
_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() {
final items = _payments?.payments ?? const [];
_operations = items.map(mapPaymentToOperation).toList();
_rebuildFiltered(notify: true);
}
void _rebuildFiltered({bool notify = true}) {
_filtered = _applyFilters(_operations);
if (notify) {
notifyListeners();
}
}
List<OperationItem> _applyFilters(List<OperationItem> operations) {
if (_selectedRange == null && _selectedStatuses.isEmpty) {
return sortOperations(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 sortOperations(filtered);
}
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);
}
}