diff --git a/lib/backend/api/api_calls.dart b/lib/backend/api/api_calls.dart index 5438e3b..29f26a4 100644 --- a/lib/backend/api/api_calls.dart +++ b/lib/backend/api/api_calls.dart @@ -324,7 +324,7 @@ class ApiCalls { } } - static Future updateAllMoveLineOnStockPiking({ + static Future updateAllMoveLineOnStockPicking({ required int stockPickingId, required List moveLineDto, }) async { @@ -352,12 +352,43 @@ class ApiCalls { if (response.statusCode == 200) { final data = response.data as Map; if (data.containsKey('result')) { - final datas = data['result'] as List; - if (datas.isNotEmpty) { - return true; - } else { - return false; - } + return true; + } else { + return false; + } + } else { + debugPrint('Erreur réseau: ${response.statusCode}'); + return false; + } + } catch (e) { + debugPrint('Erreur lors de la requête: $e'); + return false; + } + } + + static Future validateStockPicking({ + required int stockPickingId, + }) async { + try { + final response = await dioService.post( + path: '/web/dataset/call_kw/stock.picking/button_validate', + data: { + "jsonrpc": "2.0", + "method": "call", + "params": { + "model": "stock.picking", + "method": "button_validate", + "args": [ + [stockPickingId], + ], + "kwargs": {"context": {}}, + }, + }, + ); + if (response.statusCode == 200) { + final data = response.data as Map; + if (data.containsKey('result')) { + return true; } else { return false; } diff --git a/lib/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart b/lib/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart index 84bb20e..a08ab2e 100644 --- a/lib/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart +++ b/lib/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart @@ -5,7 +5,7 @@ import 'package:objectbox/objectbox.dart'; class StockPickingRecordEntity { StockPickingRecordEntity({ this.id = 0, - this.isDraft = false, + this.synchronized = true, this.priority, this.name, this.pickingTypeCode, @@ -29,7 +29,7 @@ class StockPickingRecordEntity { }); @Id(assignable: true) int id; - bool isDraft; + bool synchronized; String? priority; String? name; String? pickingTypeCode; diff --git a/lib/backend/objectbox/objectbox-model.json b/lib/backend/objectbox/objectbox-model.json index b538611..8c751e3 100644 --- a/lib/backend/objectbox/objectbox-model.json +++ b/lib/backend/objectbox/objectbox-model.json @@ -156,7 +156,7 @@ }, { "id": "7:7263194599189060077", - "lastPropertyId": "27:7762235054004255701", + "lastPropertyId": "28:1976672364117660903", "name": "StockPickingRecordEntity", "properties": [ { @@ -241,8 +241,8 @@ "relationTarget": "StockPickingTypeEntity" }, { - "id": "27:7762235054004255701", - "name": "isDraft", + "id": "28:1976672364117660903", + "name": "synchronized", "type": 1 } ], @@ -295,7 +295,8 @@ 2889425908429352139, 934877054574553245, 7012525525648469072, - 4739641817802949530 + 4739641817802949530, + 7762235054004255701 ], "retiredRelationUids": [], "version": 1 diff --git a/lib/backend/objectbox/objectbox.g.dart b/lib/backend/objectbox/objectbox.g.dart index 413f36f..0ddc6c6 100644 --- a/lib/backend/objectbox/objectbox.g.dart +++ b/lib/backend/objectbox/objectbox.g.dart @@ -193,7 +193,7 @@ final _entities = [ obx_int.ModelEntity( id: const obx_int.IdUid(7, 7263194599189060077), name: 'StockPickingRecordEntity', - lastPropertyId: const obx_int.IdUid(27, 7762235054004255701), + lastPropertyId: const obx_int.IdUid(28, 1976672364117660903), flags: 0, properties: [ obx_int.ModelProperty( @@ -285,8 +285,8 @@ final _entities = [ relationTarget: 'StockPickingTypeEntity', ), obx_int.ModelProperty( - id: const obx_int.IdUid(27, 7762235054004255701), - name: 'isDraft', + id: const obx_int.IdUid(28, 1976672364117660903), + name: 'synchronized', type: 1, flags: 0, ), @@ -393,6 +393,7 @@ obx_int.ModelDefinition getObjectBoxModel() { 934877054574553245, 7012525525648469072, 4739641817802949530, + 7762235054004255701, ], retiredRelationUids: const [], modelVersion: 5, @@ -713,7 +714,7 @@ obx_int.ModelDefinition getObjectBoxModel() { final originOffset = object.origin == null ? null : fbb.writeString(object.origin!); - fbb.startTable(28); + fbb.startTable(29); fbb.addInt64(0, object.id); fbb.addOffset(1, priorityOffset); fbb.addOffset(2, nameOffset); @@ -727,7 +728,7 @@ obx_int.ModelDefinition getObjectBoxModel() { fbb.addInt64(23, object.locationId.targetId); fbb.addInt64(24, object.locationDestId.targetId); fbb.addInt64(25, object.pickingTypeId.targetId); - fbb.addBool(26, object.isDraft); + fbb.addBool(27, object.synchronized); fbb.finish(fbb.endTable()); return object.id; }, @@ -740,10 +741,10 @@ obx_int.ModelDefinition getObjectBoxModel() { 4, 0, ); - final isDraftParam = const fb.BoolReader().vTableGet( + final synchronizedParam = const fb.BoolReader().vTableGet( buffer, rootOffset, - 56, + 58, false, ); final priorityParam = const fb.StringReader( @@ -773,7 +774,7 @@ obx_int.ModelDefinition getObjectBoxModel() { ); final object = StockPickingRecordEntity( id: idParam, - isDraft: isDraftParam, + synchronized: synchronizedParam, priority: priorityParam, name: nameParam, pickingTypeCode: pickingTypeCodeParam, @@ -1071,10 +1072,11 @@ class StockPickingRecordEntity_ { _entities[6].properties[12], ); - /// See [StockPickingRecordEntity.isDraft]. - static final isDraft = obx.QueryBooleanProperty( - _entities[6].properties[13], - ); + /// See [StockPickingRecordEntity.synchronized]. + static final synchronized = + obx.QueryBooleanProperty( + _entities[6].properties[13], + ); /// see [StockPickingRecordEntity.moveLineIdsWithoutPackage] static final moveLineIdsWithoutPackage = diff --git a/lib/components/quick_action_component.dart b/lib/components/quick_action_component.dart index fe3b684..43ee5ae 100644 --- a/lib/components/quick_action_component.dart +++ b/lib/components/quick_action_component.dart @@ -9,11 +9,17 @@ class QuickActionComponent extends StatelessWidget { this.onTapScan, this.onTapSearch, this.onTapValidateReception, + this.onTapSyncReception, + this.syncReceptionLoading = false, + this.validateReceptionLoading = false, }); final VoidCallback? onTapAdd; final VoidCallback? onTapScan; final VoidCallback? onTapSearch; final VoidCallback? onTapValidateReception; + final VoidCallback? onTapSyncReception; + final bool syncReceptionLoading; + final bool validateReceptionLoading; @override Widget build(BuildContext context) { @@ -59,6 +65,7 @@ class QuickActionComponent extends StatelessWidget { SizedBox( width: double.maxFinite, child: PrimaryButtonComponent( + loading: validateReceptionLoading, centered: true, leading: Icon(Icons.save, color: AppTheme.of(context).white), text: 'Valider la réception', @@ -66,6 +73,22 @@ class QuickActionComponent extends StatelessWidget { ), ), ], + if (onTapSyncReception != null) ...[ + const SizedBox(height: 16), + SizedBox( + width: double.maxFinite, + child: PrimaryButtonComponent( + loading: syncReceptionLoading, + centered: true, + leading: Icon( + Icons.cloud_upload_outlined, + color: AppTheme.of(context).white, + ), + text: 'Synchroniser les données', + onPressed: onTapSyncReception, + ), + ), + ], if (onTapAdd != null) ...[ const SizedBox(height: 16), SizedBox( diff --git a/lib/components/stock_picking_card_component.dart b/lib/components/stock_picking_card_component.dart index 59f595c..e957860 100644 --- a/lib/components/stock_picking_card_component.dart +++ b/lib/components/stock_picking_card_component.dart @@ -11,11 +11,11 @@ class StockPickingCard extends StatelessWidget { required this.origin, required this.status, required this.isDone, - this.isDraft = false, + this.synchronized = true, this.margin, }); final bool isDone; - final bool isDraft; + final bool synchronized; final String? reference; final String? from; final String? to; @@ -56,7 +56,7 @@ class StockPickingCard extends StatelessWidget { ], ), ), - if (isDraft) + if (!synchronized) Chip( backgroundColor: Colors.red, label: Row( @@ -64,7 +64,7 @@ class StockPickingCard extends StatelessWidget { children: [ Icon(Icons.cloud_off_outlined, color: Colors.white), Text( - 'Brouillon', + 'Non synchronisé', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, diff --git a/lib/pages/operation/reception/reception_details_page.dart b/lib/pages/operation/reception/reception_details_page.dart index 491dc85..e5efaec 100644 --- a/lib/pages/operation/reception/reception_details_page.dart +++ b/lib/pages/operation/reception/reception_details_page.dart @@ -55,7 +55,31 @@ class _ReceptionDetailsPageState extends ConsumerState { const SizedBox(height: 16), if (reception?.isDone == false) ...[ QuickActionComponent( - onTapValidateReception: reception?.isDraft == true + onTapValidateReception: reception?.synchronized == false + ? () { + ref + .read( + receptionDetailsPageModelProvider + .notifier, + ) + .validate( + receptionId: widget.receptionId, + onSuccess: () { + Toast.showSuccess( + 'Réception validée avec succès.', + ); + }, + onError: () { + Toast.showError( + 'Connexion impossible. Les données seront synchronisées plus tard.', + ); + }, + ); + } + : null, + validateReceptionLoading: state.validateLoading, + syncReceptionLoading: state.saveLoading, + onTapSyncReception: reception?.synchronized == false ? () { ref .read( @@ -66,7 +90,7 @@ class _ReceptionDetailsPageState extends ConsumerState { receptionId: widget.receptionId, onSuccess: () { Toast.showSuccess( - 'Réception validée avec succès.', + 'Les données sont synchronisées.', ); }, onError: () { @@ -102,7 +126,7 @@ class _ReceptionDetailsPageState extends ConsumerState { ), SizedBox(height: 10), StockPickingCard( - isDraft: reception?.isDraft == true, + synchronized: reception?.synchronized == true, isDone: reception?.isDone == true, margin: EdgeInsets.symmetric(horizontal: 5), reference: reception?.name ?? '', diff --git a/lib/pages/operation/reception/reception_details_page_model.dart b/lib/pages/operation/reception/reception_details_page_model.dart index 25b848a..7c9abc4 100644 --- a/lib/pages/operation/reception/reception_details_page_model.dart +++ b/lib/pages/operation/reception/reception_details_page_model.dart @@ -38,7 +38,7 @@ class ReceptionDetailsPageModel VoidCallback? onError, }) async { try { - state = state.copyWith(validateLoading: true); + state = state.copyWith(saveLoading: true); final stockPickingRecords = objectboxManager.store .box(); final stockPikingEntity = stockPickingRecords.get(receptionId); @@ -53,15 +53,48 @@ class ReceptionDetailsPageModel ) .toList() ?? []; - final res = await ApiCalls.updateAllMoveLineOnStockPiking( + final res = await ApiCalls.updateAllMoveLineOnStockPicking( stockPickingId: receptionId, moveLineDto: moveLinesDto, ); if (res) { + stockPikingEntity?.synchronized = true; + stockPickingRecords.put(stockPikingEntity!); + getReceptionById(id: receptionId); onSuccess?.call(); } else { onError?.call(); } + state = state.copyWith(saveLoading: false); + } catch (e) { + onError?.call(); + state = state.copyWith(saveLoading: false); + } + } + + Future validate({ + required int receptionId, + VoidCallback? onSuccess, + VoidCallback? onError, + }) async { + await save(receptionId: receptionId); + try { + final stockPickingRecords = objectboxManager.store + .box(); + final stockPikingEntity = stockPickingRecords.get(receptionId); + state = state.copyWith(validateLoading: true); + final res = await ApiCalls.validateStockPicking( + stockPickingId: receptionId, + ); + if (res) { + onSuccess?.call(); + stockPikingEntity?.synchronized = true; + stockPikingEntity?.state = 'done'; + stockPickingRecords.put(stockPikingEntity!); + getReceptionById(id: receptionId); + } else { + onError?.call(); + } state = state.copyWith(validateLoading: false); } catch (e) { onError?.call(); @@ -76,5 +109,6 @@ abstract class ReceptionDetailsPageState with _$ReceptionDetailsPageState { StockPickingRecordEntity? reception, @Default(false) bool loading, @Default(false) bool validateLoading, + @Default(false) bool saveLoading, }) = _ReceptionDetailsPageState; } diff --git a/lib/pages/operation/reception/reception_details_page_model.freezed.dart b/lib/pages/operation/reception/reception_details_page_model.freezed.dart index c363291..b52e8dc 100644 --- a/lib/pages/operation/reception/reception_details_page_model.freezed.dart +++ b/lib/pages/operation/reception/reception_details_page_model.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$ReceptionDetailsPageState implements DiagnosticableTreeMixin { - StockPickingRecordEntity? get reception; bool get loading; bool get validateLoading; + StockPickingRecordEntity? get reception; bool get loading; bool get validateLoading; bool get saveLoading; /// Create a copy of ReceptionDetailsPageState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -27,21 +27,21 @@ $ReceptionDetailsPageStateCopyWith get copyWith => _$ void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'ReceptionDetailsPageState')) - ..add(DiagnosticsProperty('reception', reception))..add(DiagnosticsProperty('loading', loading))..add(DiagnosticsProperty('validateLoading', validateLoading)); + ..add(DiagnosticsProperty('reception', reception))..add(DiagnosticsProperty('loading', loading))..add(DiagnosticsProperty('validateLoading', validateLoading))..add(DiagnosticsProperty('saveLoading', saveLoading)); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is ReceptionDetailsPageState&&(identical(other.reception, reception) || other.reception == reception)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.validateLoading, validateLoading) || other.validateLoading == validateLoading)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is ReceptionDetailsPageState&&(identical(other.reception, reception) || other.reception == reception)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.validateLoading, validateLoading) || other.validateLoading == validateLoading)&&(identical(other.saveLoading, saveLoading) || other.saveLoading == saveLoading)); } @override -int get hashCode => Object.hash(runtimeType,reception,loading,validateLoading); +int get hashCode => Object.hash(runtimeType,reception,loading,validateLoading,saveLoading); @override String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { - return 'ReceptionDetailsPageState(reception: $reception, loading: $loading, validateLoading: $validateLoading)'; + return 'ReceptionDetailsPageState(reception: $reception, loading: $loading, validateLoading: $validateLoading, saveLoading: $saveLoading)'; } @@ -52,7 +52,7 @@ abstract mixin class $ReceptionDetailsPageStateCopyWith<$Res> { factory $ReceptionDetailsPageStateCopyWith(ReceptionDetailsPageState value, $Res Function(ReceptionDetailsPageState) _then) = _$ReceptionDetailsPageStateCopyWithImpl; @useResult $Res call({ - StockPickingRecordEntity? reception, bool loading, bool validateLoading + StockPickingRecordEntity? reception, bool loading, bool validateLoading, bool saveLoading }); @@ -69,11 +69,12 @@ class _$ReceptionDetailsPageStateCopyWithImpl<$Res> /// Create a copy of ReceptionDetailsPageState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? reception = freezed,Object? loading = null,Object? validateLoading = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? reception = freezed,Object? loading = null,Object? validateLoading = null,Object? saveLoading = null,}) { return _then(_self.copyWith( reception: freezed == reception ? _self.reception : reception // ignore: cast_nullable_to_non_nullable as StockPickingRecordEntity?,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable as bool,validateLoading: null == validateLoading ? _self.validateLoading : validateLoading // ignore: cast_nullable_to_non_nullable +as bool,saveLoading: null == saveLoading ? _self.saveLoading : saveLoading // ignore: cast_nullable_to_non_nullable as bool, )); } @@ -85,12 +86,13 @@ as bool, class _ReceptionDetailsPageState with DiagnosticableTreeMixin implements ReceptionDetailsPageState { - const _ReceptionDetailsPageState({this.reception, this.loading = false, this.validateLoading = false}); + const _ReceptionDetailsPageState({this.reception, this.loading = false, this.validateLoading = false, this.saveLoading = false}); @override final StockPickingRecordEntity? reception; @override@JsonKey() final bool loading; @override@JsonKey() final bool validateLoading; +@override@JsonKey() final bool saveLoading; /// Create a copy of ReceptionDetailsPageState /// with the given fields replaced by the non-null parameter values. @@ -103,21 +105,21 @@ _$ReceptionDetailsPageStateCopyWith<_ReceptionDetailsPageState> get copyWith => void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'ReceptionDetailsPageState')) - ..add(DiagnosticsProperty('reception', reception))..add(DiagnosticsProperty('loading', loading))..add(DiagnosticsProperty('validateLoading', validateLoading)); + ..add(DiagnosticsProperty('reception', reception))..add(DiagnosticsProperty('loading', loading))..add(DiagnosticsProperty('validateLoading', validateLoading))..add(DiagnosticsProperty('saveLoading', saveLoading)); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _ReceptionDetailsPageState&&(identical(other.reception, reception) || other.reception == reception)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.validateLoading, validateLoading) || other.validateLoading == validateLoading)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ReceptionDetailsPageState&&(identical(other.reception, reception) || other.reception == reception)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.validateLoading, validateLoading) || other.validateLoading == validateLoading)&&(identical(other.saveLoading, saveLoading) || other.saveLoading == saveLoading)); } @override -int get hashCode => Object.hash(runtimeType,reception,loading,validateLoading); +int get hashCode => Object.hash(runtimeType,reception,loading,validateLoading,saveLoading); @override String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { - return 'ReceptionDetailsPageState(reception: $reception, loading: $loading, validateLoading: $validateLoading)'; + return 'ReceptionDetailsPageState(reception: $reception, loading: $loading, validateLoading: $validateLoading, saveLoading: $saveLoading)'; } @@ -128,7 +130,7 @@ abstract mixin class _$ReceptionDetailsPageStateCopyWith<$Res> implements $Recep factory _$ReceptionDetailsPageStateCopyWith(_ReceptionDetailsPageState value, $Res Function(_ReceptionDetailsPageState) _then) = __$ReceptionDetailsPageStateCopyWithImpl; @override @useResult $Res call({ - StockPickingRecordEntity? reception, bool loading, bool validateLoading + StockPickingRecordEntity? reception, bool loading, bool validateLoading, bool saveLoading }); @@ -145,11 +147,12 @@ class __$ReceptionDetailsPageStateCopyWithImpl<$Res> /// Create a copy of ReceptionDetailsPageState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? reception = freezed,Object? loading = null,Object? validateLoading = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? reception = freezed,Object? loading = null,Object? validateLoading = null,Object? saveLoading = null,}) { return _then(_ReceptionDetailsPageState( reception: freezed == reception ? _self.reception : reception // ignore: cast_nullable_to_non_nullable as StockPickingRecordEntity?,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable as bool,validateLoading: null == validateLoading ? _self.validateLoading : validateLoading // ignore: cast_nullable_to_non_nullable +as bool,saveLoading: null == saveLoading ? _self.saveLoading : saveLoading // ignore: cast_nullable_to_non_nullable as bool, )); } diff --git a/lib/pages/operation/reception/reception_page.dart b/lib/pages/operation/reception/reception_page.dart index 34f434e..103b747 100644 --- a/lib/pages/operation/reception/reception_page.dart +++ b/lib/pages/operation/reception/reception_page.dart @@ -79,7 +79,7 @@ class _ReceptionPageState extends ConsumerState { ).push(context); }, child: StockPickingCard( - isDraft: reception.isDraft == true, + synchronized: reception.synchronized == true, margin: EdgeInsets.symmetric( horizontal: 5, vertical: 5, diff --git a/lib/pages/operation/reception/reception_scan_page_model.dart b/lib/pages/operation/reception/reception_scan_page_model.dart index d2ef4e4..37df30d 100644 --- a/lib/pages/operation/reception/reception_scan_page_model.dart +++ b/lib/pages/operation/reception/reception_scan_page_model.dart @@ -57,32 +57,21 @@ class ReceptionScanPageModel final stockPickingRecordBox = objectboxManager.store .box(); final moveBox = objectboxManager.store.box(); - final productBox = objectboxManager.store.box(); - final productEntity = productBox - .query(ProductEntity_.barcode.equals(barcode)) - .build() - .findFirst(); - final productId = productEntity?.id; final stockPickingRecord = stockPickingRecordBox.get(receptionId); - if (productId != null) { - final moveLineEntity = moveLineBox - .query(MoveLineWithoutPackageEntity_.productId.equals(productId)) - .build() - .findFirst(); - final moveEntity = moveBox - .query(MoveWithoutPackageEntity_.productId.equals(productId)) - .build() - .findFirst(); - if (moveLineEntity != null && - moveEntity != null && - stockPickingRecord != null) { - moveLineEntity.quantity = (moveLineEntity.quantity ?? 0) + 1; - moveEntity.quantity = (moveEntity.quantity ?? 0) + 1; - stockPickingRecord.isDraft = true; - moveLineBox.put(moveLineEntity); - moveBox.put(moveEntity); - stockPickingRecordBox.put(stockPickingRecord); - } + final moveLineEntity = stockPickingRecord?.moveLineIdsWithoutPackage + .firstWhere((e) => e.productId.target?.barcode == barcode); + final moveEntity = stockPickingRecord?.moveIdsWithoutPackage.firstWhere( + (e) => e.productId.target?.barcode == barcode, + ); + if (moveLineEntity != null && + moveEntity != null && + stockPickingRecord != null) { + moveLineEntity.quantity = (moveLineEntity.quantity ?? 0) + 1; + moveEntity.quantity = (moveEntity.quantity ?? 0) + 1; + stockPickingRecord.synchronized = false; + moveLineBox.put(moveLineEntity); + moveBox.put(moveEntity); + stockPickingRecordBox.put(stockPickingRecord); } } }