From 6e4997188373ace6c2661de69972e7861e157e0b Mon Sep 17 00:00:00 2001 From: mandreshope Date: Wed, 30 Jul 2025 14:16:27 +0300 Subject: [PATCH] enhance: Enhances data models and reception details Improves JSON deserialization for product and stock picking models by using custom `fromJson` methods and explicit `JsonKey` mappings. This ensures robust parsing of fields like barcode, display name, and product IDs. Adds a new `ecart` getter to calculate the difference between requested and received quantities for stock moves. Updates the reception details page to display a list of products with their requested, received, and calculated difference quantities. Removes redundant JSON-RPC fields from the `stock.picking/web_read` API call. --- lib/backend/api/api_calls.dart | 2 - lib/backend/schema/product/product_model.dart | 9 ++- .../schema/product/product_model.freezed.dart | 12 ++-- .../schema/product/product_model.g.dart | 6 +- .../stock_picking_record_model.dart | 11 ++- .../stock_picking_record_model.freezed.dart | 22 +++--- .../stock_picking_record_model.g.dart | 16 ++--- .../reception/reception_details_page.dart | 69 +++++++++++++++++++ lib/utils/utils.dart | 10 +++ 9 files changed, 122 insertions(+), 35 deletions(-) diff --git a/lib/backend/api/api_calls.dart b/lib/backend/api/api_calls.dart index de760db..b401ecc 100644 --- a/lib/backend/api/api_calls.dart +++ b/lib/backend/api/api_calls.dart @@ -181,8 +181,6 @@ class ApiCalls { final response = await dioService.post( path: '/web/dataset/call_kw/stock.picking/web_read', data: { - "jsonrpc": "2.0", - "method": "call", "params": { "model": "stock.picking", "method": "web_read", diff --git a/lib/backend/schema/product/product_model.dart b/lib/backend/schema/product/product_model.dart index bec6280..10d7b8a 100644 --- a/lib/backend/schema/product/product_model.dart +++ b/lib/backend/schema/product/product_model.dart @@ -1,4 +1,5 @@ import 'package:e_scan/backend/objectbox/entities/product/product_entity.dart'; +import 'package:e_scan/utils/utils.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'product_model.freezed.dart'; @@ -6,8 +7,12 @@ part 'product_model.g.dart'; @Freezed(toJson: true) abstract class ProductModel with _$ProductModel { - factory ProductModel({int? id, String? barcode, String? displayName}) = - _ProductModel; + factory ProductModel({ + int? id, + @JsonKey(fromJson: stringFromJson) String? barcode, + @JsonKey(name: 'display_name', fromJson: stringFromJson) + String? displayName, + }) = _ProductModel; factory ProductModel.fromJson(Map json) => _$ProductModelFromJson(json); diff --git a/lib/backend/schema/product/product_model.freezed.dart b/lib/backend/schema/product/product_model.freezed.dart index 2a8566e..789f5a5 100644 --- a/lib/backend/schema/product/product_model.freezed.dart +++ b/lib/backend/schema/product/product_model.freezed.dart @@ -16,7 +16,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$ProductModel { - int? get id; String? get barcode; String? get displayName; + int? get id;@JsonKey(fromJson: stringFromJson) String? get barcode;@JsonKey(name: 'display_name', fromJson: stringFromJson) String? get displayName; /// Create a copy of ProductModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -49,7 +49,7 @@ abstract mixin class $ProductModelCopyWith<$Res> { factory $ProductModelCopyWith(ProductModel value, $Res Function(ProductModel) _then) = _$ProductModelCopyWithImpl; @useResult $Res call({ - int? id, String? barcode, String? displayName + int? id,@JsonKey(fromJson: stringFromJson) String? barcode,@JsonKey(name: 'display_name', fromJson: stringFromJson) String? displayName }); @@ -82,12 +82,12 @@ as String?, @JsonSerializable() class _ProductModel implements ProductModel { - _ProductModel({this.id, this.barcode, this.displayName}); + _ProductModel({this.id, @JsonKey(fromJson: stringFromJson) this.barcode, @JsonKey(name: 'display_name', fromJson: stringFromJson) this.displayName}); factory _ProductModel.fromJson(Map json) => _$ProductModelFromJson(json); @override final int? id; -@override final String? barcode; -@override final String? displayName; +@override@JsonKey(fromJson: stringFromJson) final String? barcode; +@override@JsonKey(name: 'display_name', fromJson: stringFromJson) final String? displayName; /// Create a copy of ProductModel /// with the given fields replaced by the non-null parameter values. @@ -122,7 +122,7 @@ abstract mixin class _$ProductModelCopyWith<$Res> implements $ProductModelCopyWi factory _$ProductModelCopyWith(_ProductModel value, $Res Function(_ProductModel) _then) = __$ProductModelCopyWithImpl; @override @useResult $Res call({ - int? id, String? barcode, String? displayName + int? id,@JsonKey(fromJson: stringFromJson) String? barcode,@JsonKey(name: 'display_name', fromJson: stringFromJson) String? displayName }); diff --git a/lib/backend/schema/product/product_model.g.dart b/lib/backend/schema/product/product_model.g.dart index 5f9e271..ef71578 100644 --- a/lib/backend/schema/product/product_model.g.dart +++ b/lib/backend/schema/product/product_model.g.dart @@ -9,13 +9,13 @@ part of 'product_model.dart'; _ProductModel _$ProductModelFromJson(Map json) => _ProductModel( id: (json['id'] as num?)?.toInt(), - barcode: json['barcode'] as String?, - displayName: json['displayName'] as String?, + barcode: stringFromJson(json['barcode']), + displayName: stringFromJson(json['display_name']), ); Map _$ProductModelToJson(_ProductModel instance) => { 'id': instance.id, 'barcode': instance.barcode, - 'displayName': instance.displayName, + 'display_name': instance.displayName, }; diff --git a/lib/backend/schema/stock_picking/stock_picking_record_model.dart b/lib/backend/schema/stock_picking/stock_picking_record_model.dart index bafb979..d1f9343 100644 --- a/lib/backend/schema/stock_picking/stock_picking_record_model.dart +++ b/lib/backend/schema/stock_picking/stock_picking_record_model.dart @@ -72,6 +72,7 @@ List? _moveIdsWithoutPackageFromJson(dynamic json) => abstract class MoveLineWithoutPackageModel with _$MoveLineWithoutPackageModel { const factory MoveLineWithoutPackageModel({ int? id, + @JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? productId, double? quantity, }) = _MoveLineWithoutPackageModel; @@ -84,15 +85,23 @@ abstract class MoveLineWithoutPackageModel with _$MoveLineWithoutPackageModel { abstract class MoveWithoutPackageModel with _$MoveWithoutPackageModel { const factory MoveWithoutPackageModel({ int? id, + @JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? productId, double? quantity, - double? productUomQty, + @JsonKey(name: 'product_uom_qty') double? productUomQty, }) = _MoveWithoutPackageModel; factory MoveWithoutPackageModel.fromJson(Map json) => _$MoveWithoutPackageModelFromJson(json); } +ProductModel? _productFromJson(dynamic json) => + objectFromJson(json, ProductModel.fromJson); + extension StockPickingRecordModelExt on StockPickingRecordModel { bool get isDone => state == "done"; } + +extension MoveWithoutPackageModelExt on MoveWithoutPackageModel { + double get ecart => ((quantity ?? 0) - (productUomQty ?? 0)); +} diff --git a/lib/backend/schema/stock_picking/stock_picking_record_model.freezed.dart b/lib/backend/schema/stock_picking/stock_picking_record_model.freezed.dart index 7ed3682..c6786d6 100644 --- a/lib/backend/schema/stock_picking/stock_picking_record_model.freezed.dart +++ b/lib/backend/schema/stock_picking/stock_picking_record_model.freezed.dart @@ -350,7 +350,7 @@ $StockPickingTypeModelCopyWith<$Res>? get pickingTypeId { /// @nodoc mixin _$MoveLineWithoutPackageModel { - int? get id; ProductModel? get productId; double? get quantity; + int? get id;@JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? get productId; double? get quantity; /// Create a copy of MoveLineWithoutPackageModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -383,7 +383,7 @@ abstract mixin class $MoveLineWithoutPackageModelCopyWith<$Res> { factory $MoveLineWithoutPackageModelCopyWith(MoveLineWithoutPackageModel value, $Res Function(MoveLineWithoutPackageModel) _then) = _$MoveLineWithoutPackageModelCopyWithImpl; @useResult $Res call({ - int? id, ProductModel? productId, double? quantity + int? id,@JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? productId, double? quantity }); @@ -428,11 +428,11 @@ $ProductModelCopyWith<$Res>? get productId { @JsonSerializable() class _MoveLineWithoutPackageModel implements MoveLineWithoutPackageModel { - const _MoveLineWithoutPackageModel({this.id, this.productId, this.quantity}); + const _MoveLineWithoutPackageModel({this.id, @JsonKey(name: 'product_id', fromJson: _productFromJson) this.productId, this.quantity}); factory _MoveLineWithoutPackageModel.fromJson(Map json) => _$MoveLineWithoutPackageModelFromJson(json); @override final int? id; -@override final ProductModel? productId; +@override@JsonKey(name: 'product_id', fromJson: _productFromJson) final ProductModel? productId; @override final double? quantity; /// Create a copy of MoveLineWithoutPackageModel @@ -468,7 +468,7 @@ abstract mixin class _$MoveLineWithoutPackageModelCopyWith<$Res> implements $Mov factory _$MoveLineWithoutPackageModelCopyWith(_MoveLineWithoutPackageModel value, $Res Function(_MoveLineWithoutPackageModel) _then) = __$MoveLineWithoutPackageModelCopyWithImpl; @override @useResult $Res call({ - int? id, ProductModel? productId, double? quantity + int? id,@JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? productId, double? quantity }); @@ -513,7 +513,7 @@ $ProductModelCopyWith<$Res>? get productId { /// @nodoc mixin _$MoveWithoutPackageModel { - int? get id; ProductModel? get productId; double? get quantity; double? get productUomQty; + int? get id;@JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? get productId; double? get quantity;@JsonKey(name: 'product_uom_qty') double? get productUomQty; /// Create a copy of MoveWithoutPackageModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -546,7 +546,7 @@ abstract mixin class $MoveWithoutPackageModelCopyWith<$Res> { factory $MoveWithoutPackageModelCopyWith(MoveWithoutPackageModel value, $Res Function(MoveWithoutPackageModel) _then) = _$MoveWithoutPackageModelCopyWithImpl; @useResult $Res call({ - int? id, ProductModel? productId, double? quantity, double? productUomQty + int? id,@JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? productId, double? quantity,@JsonKey(name: 'product_uom_qty') double? productUomQty }); @@ -592,13 +592,13 @@ $ProductModelCopyWith<$Res>? get productId { @JsonSerializable() class _MoveWithoutPackageModel implements MoveWithoutPackageModel { - const _MoveWithoutPackageModel({this.id, this.productId, this.quantity, this.productUomQty}); + const _MoveWithoutPackageModel({this.id, @JsonKey(name: 'product_id', fromJson: _productFromJson) this.productId, this.quantity, @JsonKey(name: 'product_uom_qty') this.productUomQty}); factory _MoveWithoutPackageModel.fromJson(Map json) => _$MoveWithoutPackageModelFromJson(json); @override final int? id; -@override final ProductModel? productId; +@override@JsonKey(name: 'product_id', fromJson: _productFromJson) final ProductModel? productId; @override final double? quantity; -@override final double? productUomQty; +@override@JsonKey(name: 'product_uom_qty') final double? productUomQty; /// Create a copy of MoveWithoutPackageModel /// with the given fields replaced by the non-null parameter values. @@ -633,7 +633,7 @@ abstract mixin class _$MoveWithoutPackageModelCopyWith<$Res> implements $MoveWit factory _$MoveWithoutPackageModelCopyWith(_MoveWithoutPackageModel value, $Res Function(_MoveWithoutPackageModel) _then) = __$MoveWithoutPackageModelCopyWithImpl; @override @useResult $Res call({ - int? id, ProductModel? productId, double? quantity, double? productUomQty + int? id,@JsonKey(name: 'product_id', fromJson: _productFromJson) ProductModel? productId, double? quantity,@JsonKey(name: 'product_uom_qty') double? productUomQty }); diff --git a/lib/backend/schema/stock_picking/stock_picking_record_model.g.dart b/lib/backend/schema/stock_picking/stock_picking_record_model.g.dart index a193e1b..700f7a6 100644 --- a/lib/backend/schema/stock_picking/stock_picking_record_model.g.dart +++ b/lib/backend/schema/stock_picking/stock_picking_record_model.g.dart @@ -76,9 +76,7 @@ _MoveLineWithoutPackageModel _$MoveLineWithoutPackageModelFromJson( Map json, ) => _MoveLineWithoutPackageModel( id: (json['id'] as num?)?.toInt(), - productId: json['productId'] == null - ? null - : ProductModel.fromJson(json['productId'] as Map), + productId: _productFromJson(json['product_id']), quantity: (json['quantity'] as num?)?.toDouble(), ); @@ -86,7 +84,7 @@ Map _$MoveLineWithoutPackageModelToJson( _MoveLineWithoutPackageModel instance, ) => { 'id': instance.id, - 'productId': instance.productId, + 'product_id': instance.productId, 'quantity': instance.quantity, }; @@ -94,18 +92,16 @@ _MoveWithoutPackageModel _$MoveWithoutPackageModelFromJson( Map json, ) => _MoveWithoutPackageModel( id: (json['id'] as num?)?.toInt(), - productId: json['productId'] == null - ? null - : ProductModel.fromJson(json['productId'] as Map), + productId: _productFromJson(json['product_id']), quantity: (json['quantity'] as num?)?.toDouble(), - productUomQty: (json['productUomQty'] as num?)?.toDouble(), + productUomQty: (json['product_uom_qty'] as num?)?.toDouble(), ); Map _$MoveWithoutPackageModelToJson( _MoveWithoutPackageModel instance, ) => { 'id': instance.id, - 'productId': instance.productId, + 'product_id': instance.productId, 'quantity': instance.quantity, - 'productUomQty': instance.productUomQty, + 'product_uom_qty': instance.productUomQty, }; diff --git a/lib/pages/operation/reception/reception_details_page.dart b/lib/pages/operation/reception/reception_details_page.dart index 0dcb358..c4c0a86 100644 --- a/lib/pages/operation/reception/reception_details_page.dart +++ b/lib/pages/operation/reception/reception_details_page.dart @@ -52,6 +52,13 @@ class _ReceptionDetailsPageState extends ConsumerState { ), const SizedBox(height: 16), ], + Text( + 'Détails réception', + style: AppTheme.of( + context, + ).bodyMedium.copyWith(fontWeight: FontWeight.bold), + ), + SizedBox(height: 10), StockPickingCard( isDone: reception?.isDone == true, margin: EdgeInsets.symmetric(horizontal: 5), @@ -62,6 +69,68 @@ class _ReceptionDetailsPageState extends ConsumerState { origin: reception?.origin, status: reception?.state, ), + SizedBox(height: 20), + ListView( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + primary: true, + children: [ + Text( + 'Produits', + style: AppTheme.of( + context, + ).bodyMedium.copyWith(fontWeight: FontWeight.bold), + ), + SizedBox(height: 10), + ...reception?.moveIdsWithoutPackage + ?.map( + (move) => Card( + color: AppTheme.of(context).primaryBackground, + child: ListTile( + title: Text( + move.productId?.displayName ?? '', + style: TextStyle(color: Colors.black), + ), + subtitle: Wrap( + spacing: 5, + children: [ + Chip( + backgroundColor: AppTheme.of( + context, + ).secondaryBackground, + label: Text( + "Qté demandée: ${move.productUomQty}", + ), + ), + Chip( + backgroundColor: AppTheme.of( + context, + ).secondaryBackground, + label: Text( + "Qté reçue: ${move.quantity}", + ), + ), + Chip( + backgroundColor: AppTheme.of( + context, + ).secondaryBackground, + label: Text( + "Ecart: ${move.ecart}", + style: TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + ), + ) + .toList() ?? + [], + ], + ), ], ), ), diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index c78cc6e..51ace5c 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -16,6 +16,16 @@ String? stringFromJson(dynamic json) { return null; } +double? doubleFromJson(dynamic json) { + if (json is double) return json; + return null; +} + +int? intFromJson(dynamic json) { + if (json is int) return json; + return null; +} + T? objectFromJson(dynamic json, T Function(Map) fromJson) { if (json is Map) return fromJson(json); return null;