barcode_scanner/lib/pages/operation/reception/reception_page_model.dart
your-name db71578ded feat: Improves data synchronization and connectivity UI
Implements automatic synchronization of unsynced local reception data (move line quantities) to the backend before fetching new receptions. This ensures local changes are pushed when connectivity is available.

Adds a pull-to-refresh mechanism on the reception page, allowing users to manually refresh the list of receptions.

Integrates an offline indicator in the main app bar, providing immediate visual feedback on the application's network connectivity status.
2025-07-31 04:00:06 +03:00

121 lines
3.8 KiB
Dart

import 'package:e_scan/backend/api/api_calls.dart';
import 'package:e_scan/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart';
import 'package:e_scan/backend/objectbox/objectbox_manager.dart';
import 'package:e_scan/backend/schema/stock_picking/update_move_line_dto.dart';
import 'package:e_scan/backend/schema/user/user_struct.dart';
import 'package:e_scan/services/secure_storage.dart';
import 'package:e_scan/services/token_provider.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'reception_page_model.freezed.dart';
final receptionPageModelProvider =
StateNotifierProvider<ReceptionPageModel, ReceptionPageState>((ref) {
return ReceptionPageModel(
secureStorage: ref.read(sharedPrefsProvider),
tokenProvider: ref.read(tokenProvider),
userConnectedProvider: ref.read(userConnectedProvider),
);
});
class ReceptionPageModel extends StateNotifier<ReceptionPageState> {
ReceptionPageModel({
required this.secureStorage,
required this.tokenProvider,
required this.userConnectedProvider,
}) : super(const ReceptionPageState()) {
getAllReceptions();
}
late FlutterSecureStorage secureStorage;
late TokenProvider tokenProvider;
final UserConnectedProvider userConnectedProvider;
Future getUserConnected() async {
state = state.copyWith(loadingUser: true);
final user = await userConnectedProvider.get();
state = state.copyWith(user: user, loadingUser: false);
}
Future save({
required int receptionId,
VoidCallback? onSuccess,
VoidCallback? onError,
}) async {
try {
final stockPickingRecords = objectboxManager.store
.box<StockPickingRecordEntity>();
final stockPikingEntity = stockPickingRecords.get(receptionId);
final moveLinesDto =
stockPikingEntity?.moveLineIdsWithoutPackage
.map(
(m) => UpdateMoveLineDto(
operation: 1,
moveLineId: m.id,
values: {"quantity": m.quantity},
),
)
.toList() ??
[];
final res = await ApiCalls.updateAllMoveLineOnStockPicking(
stockPickingId: receptionId,
moveLineDto: moveLinesDto,
);
if (res) {
stockPikingEntity?.synchronized = true;
stockPickingRecords.put(stockPikingEntity!);
onSuccess?.call();
} else {
onError?.call();
}
} catch (e) {
onError?.call();
}
}
Future<void> synchroAllData() async {
final stockPickingRecords = objectboxManager.store
.box<StockPickingRecordEntity>();
final records = stockPickingRecords
.query(StockPickingRecordEntity_.synchronized.equals(false))
.build()
.find();
for (var rec in records) {
await save(receptionId: rec.id);
}
}
Future getAllReceptions() async {
// sync all data first
await synchroAllData();
try {
state = state.copyWith(loadingReceptions: true);
final res = await ApiCalls.getAllStockPiking();
res.when(
(data) {
state = state.copyWith(receptions: data, loadingReceptions: false);
},
(error) {
state = state.copyWith(loadingReceptions: false);
},
);
} catch (e) {
state = state.copyWith(loadingReceptions: false);
}
}
}
@freezed
abstract class ReceptionPageState with _$ReceptionPageState {
const factory ReceptionPageState({
UserStruct? user,
@Default(<StockPickingRecordEntity>[])
List<StockPickingRecordEntity> receptions,
@Default(false) bool loadingReceptions,
@Default(false) bool loadingUser,
}) = _ReceptionPageState;
}