feat: Adds draft status for stock pickings
Introduces an `isDraft` property to `StockPickingRecordEntity` to mark receptions with local modifications. Automatically sets a reception to draft status when a product's quantity is incremented via scanning. Displays a 'Brouillon' chip on reception cards to provide visual feedback for draft operations. Ensures reception details are refreshed after scanning to reflect the updated draft status and provides immediate error feedback when no product is found during a scan.
This commit is contained in:
parent
46b93d7fa4
commit
ab4a56ed41
@ -5,6 +5,7 @@ import 'package:objectbox/objectbox.dart';
|
||||
class StockPickingRecordEntity {
|
||||
StockPickingRecordEntity({
|
||||
this.id = 0,
|
||||
this.isDraft = false,
|
||||
this.priority,
|
||||
this.name,
|
||||
this.pickingTypeCode,
|
||||
@ -28,7 +29,7 @@ class StockPickingRecordEntity {
|
||||
});
|
||||
@Id(assignable: true)
|
||||
int id;
|
||||
|
||||
bool isDraft;
|
||||
String? priority;
|
||||
String? name;
|
||||
String? pickingTypeCode;
|
||||
|
@ -156,7 +156,7 @@
|
||||
},
|
||||
{
|
||||
"id": "7:7263194599189060077",
|
||||
"lastPropertyId": "26:6083080926308657518",
|
||||
"lastPropertyId": "27:7762235054004255701",
|
||||
"name": "StockPickingRecordEntity",
|
||||
"properties": [
|
||||
{
|
||||
@ -239,6 +239,11 @@
|
||||
"flags": 520,
|
||||
"indexId": "9:5054232387620309172",
|
||||
"relationTarget": "StockPickingTypeEntity"
|
||||
},
|
||||
{
|
||||
"id": "27:7762235054004255701",
|
||||
"name": "isDraft",
|
||||
"type": 1
|
||||
}
|
||||
],
|
||||
"relations": []
|
||||
|
@ -193,7 +193,7 @@ final _entities = <obx_int.ModelEntity>[
|
||||
obx_int.ModelEntity(
|
||||
id: const obx_int.IdUid(7, 7263194599189060077),
|
||||
name: 'StockPickingRecordEntity',
|
||||
lastPropertyId: const obx_int.IdUid(26, 6083080926308657518),
|
||||
lastPropertyId: const obx_int.IdUid(27, 7762235054004255701),
|
||||
flags: 0,
|
||||
properties: <obx_int.ModelProperty>[
|
||||
obx_int.ModelProperty(
|
||||
@ -284,6 +284,12 @@ final _entities = <obx_int.ModelEntity>[
|
||||
indexId: const obx_int.IdUid(9, 5054232387620309172),
|
||||
relationTarget: 'StockPickingTypeEntity',
|
||||
),
|
||||
obx_int.ModelProperty(
|
||||
id: const obx_int.IdUid(27, 7762235054004255701),
|
||||
name: 'isDraft',
|
||||
type: 1,
|
||||
flags: 0,
|
||||
),
|
||||
],
|
||||
relations: <obx_int.ModelRelation>[],
|
||||
backlinks: <obx_int.ModelBacklink>[
|
||||
@ -707,7 +713,7 @@ obx_int.ModelDefinition getObjectBoxModel() {
|
||||
final originOffset = object.origin == null
|
||||
? null
|
||||
: fbb.writeString(object.origin!);
|
||||
fbb.startTable(27);
|
||||
fbb.startTable(28);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, priorityOffset);
|
||||
fbb.addOffset(2, nameOffset);
|
||||
@ -721,6 +727,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.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
@ -733,6 +740,12 @@ obx_int.ModelDefinition getObjectBoxModel() {
|
||||
4,
|
||||
0,
|
||||
);
|
||||
final isDraftParam = const fb.BoolReader().vTableGet(
|
||||
buffer,
|
||||
rootOffset,
|
||||
56,
|
||||
false,
|
||||
);
|
||||
final priorityParam = const fb.StringReader(
|
||||
asciiOptimization: true,
|
||||
).vTableGetNullable(buffer, rootOffset, 6);
|
||||
@ -760,6 +773,7 @@ obx_int.ModelDefinition getObjectBoxModel() {
|
||||
);
|
||||
final object = StockPickingRecordEntity(
|
||||
id: idParam,
|
||||
isDraft: isDraftParam,
|
||||
priority: priorityParam,
|
||||
name: nameParam,
|
||||
pickingTypeCode: pickingTypeCodeParam,
|
||||
@ -1057,6 +1071,11 @@ class StockPickingRecordEntity_ {
|
||||
_entities[6].properties[12],
|
||||
);
|
||||
|
||||
/// See [StockPickingRecordEntity.isDraft].
|
||||
static final isDraft = obx.QueryBooleanProperty<StockPickingRecordEntity>(
|
||||
_entities[6].properties[13],
|
||||
);
|
||||
|
||||
/// see [StockPickingRecordEntity.moveLineIdsWithoutPackage]
|
||||
static final moveLineIdsWithoutPackage =
|
||||
obx.QueryBacklinkToMany<
|
||||
|
@ -11,9 +11,11 @@ class StockPickingCard extends StatelessWidget {
|
||||
required this.origin,
|
||||
required this.status,
|
||||
required this.isDone,
|
||||
this.isDraft = false,
|
||||
this.margin,
|
||||
});
|
||||
final bool isDone;
|
||||
final bool isDraft;
|
||||
final String? reference;
|
||||
final String? from;
|
||||
final String? to;
|
||||
@ -36,17 +38,41 @@ class StockPickingCard extends StatelessWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.receipt_long, color: Colors.blue),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
reference ?? 'Référence inconnue',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.receipt_long, color: Colors.blue),
|
||||
Expanded(
|
||||
child: Text(
|
||||
reference ?? 'Référence inconnue',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isDraft)
|
||||
Chip(
|
||||
backgroundColor: Colors.red,
|
||||
label: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Icon(Icons.cloud_off_outlined, color: Colors.white),
|
||||
Text(
|
||||
'Brouillon',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
@ -57,7 +57,16 @@ class _ReceptionDetailsPageState extends ConsumerState<ReceptionDetailsPage> {
|
||||
onTapScan: () {
|
||||
final id = reception?.id;
|
||||
if (id == null) return;
|
||||
ReceptionScanRoute(receptionId: id).push(context);
|
||||
ReceptionScanRoute(receptionId: id)
|
||||
.push(context)
|
||||
.then(
|
||||
(v) => ref
|
||||
.read(
|
||||
receptionDetailsPageModelProvider
|
||||
.notifier,
|
||||
)
|
||||
.getReceptionById(id: widget.receptionId),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -70,6 +79,7 @@ class _ReceptionDetailsPageState extends ConsumerState<ReceptionDetailsPage> {
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
StockPickingCard(
|
||||
isDraft: reception?.isDraft == true,
|
||||
isDone: reception?.isDone == true,
|
||||
margin: EdgeInsets.symmetric(horizontal: 5),
|
||||
reference: reception?.name ?? '',
|
||||
@ -144,7 +154,13 @@ class _ReceptionDetailsPageState extends ConsumerState<ReceptionDetailsPage> {
|
||||
onPressed: () {
|
||||
final id = reception?.id;
|
||||
if (id == null) return;
|
||||
ReceptionScanRoute(receptionId: id).push(context);
|
||||
ReceptionScanRoute(receptionId: id)
|
||||
.push(context)
|
||||
.then(
|
||||
(v) => ref
|
||||
.read(receptionDetailsPageModelProvider.notifier)
|
||||
.getReceptionById(id: widget.receptionId),
|
||||
);
|
||||
},
|
||||
child: Icon(Icons.qr_code, color: AppTheme.of(context).white),
|
||||
)
|
||||
|
@ -79,6 +79,7 @@ class _ReceptionPageState extends ConsumerState<ReceptionPage> {
|
||||
).push(context);
|
||||
},
|
||||
child: StockPickingCard(
|
||||
isDraft: reception.isDraft == true,
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 5,
|
||||
vertical: 5,
|
||||
|
@ -99,12 +99,17 @@ class _ReceptionScanPageState extends ConsumerState<ReceptionScanPage>
|
||||
|
||||
if (qrcodeValue == null) {
|
||||
debugPrint("Qrcode non valide");
|
||||
Toast.showError('Aucun produit trouvé.');
|
||||
return;
|
||||
}
|
||||
// find product in local database
|
||||
final isProductExist = model.isProductExist(barcode: qrcodeValue);
|
||||
if (isProductExist) {
|
||||
final product = model.getProduct(barcode: qrcodeValue);
|
||||
model.incrementMoveLineQuantity(
|
||||
barcode: qrcodeValue,
|
||||
receptionId: widget.receptionId,
|
||||
);
|
||||
//show dialog
|
||||
await showDialog(
|
||||
barrierDismissible: false,
|
||||
@ -146,6 +151,7 @@ class _ReceptionScanPageState extends ConsumerState<ReceptionScanPage>
|
||||
},
|
||||
);
|
||||
} else {
|
||||
Toast.showError('Aucun produit trouvé.');
|
||||
debugPrint('Aucun produit trouvé.');
|
||||
}
|
||||
}
|
||||
@ -160,7 +166,10 @@ class _ReceptionScanPageState extends ConsumerState<ReceptionScanPage>
|
||||
final isProductExist = model.isProductExist(barcode: qrcodeValue);
|
||||
if (isProductExist) {
|
||||
final product = model.getProduct(barcode: qrcodeValue);
|
||||
model.incrementMoveLineQuantity(barcode: qrcodeValue);
|
||||
model.incrementMoveLineQuantity(
|
||||
barcode: qrcodeValue,
|
||||
receptionId: widget.receptionId,
|
||||
);
|
||||
//show dialog
|
||||
await showDialog(
|
||||
barrierDismissible: false,
|
||||
|
@ -4,7 +4,6 @@ import 'package:e_scan/backend/objectbox/objectbox_manager.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
|
||||
part 'reception_scan_page_model.freezed.dart';
|
||||
|
||||
@ -49,9 +48,14 @@ class ReceptionScanPageModel
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
void incrementMoveLineQuantity({required String barcode}) {
|
||||
void incrementMoveLineQuantity({
|
||||
required String barcode,
|
||||
required int receptionId,
|
||||
}) {
|
||||
final moveLineBox = objectboxManager.store
|
||||
.box<MoveLineWithoutPackageEntity>();
|
||||
final stockPickingRecordBox = objectboxManager.store
|
||||
.box<StockPickingRecordEntity>();
|
||||
final moveBox = objectboxManager.store.box<MoveWithoutPackageEntity>();
|
||||
final productBox = objectboxManager.store.box<ProductEntity>();
|
||||
final productEntity = productBox
|
||||
@ -59,6 +63,7 @@ class ReceptionScanPageModel
|
||||
.build()
|
||||
.findFirst();
|
||||
final productId = productEntity?.id;
|
||||
final stockPickingRecord = stockPickingRecordBox.get(receptionId);
|
||||
if (productId != null) {
|
||||
final moveLineEntity = moveLineBox
|
||||
.query(MoveLineWithoutPackageEntity_.productId.equals(productId))
|
||||
@ -68,21 +73,18 @@ class ReceptionScanPageModel
|
||||
.query(MoveWithoutPackageEntity_.productId.equals(productId))
|
||||
.build()
|
||||
.findFirst();
|
||||
if (moveLineEntity != null && moveEntity != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future handleBarcode({required BarcodeCapture barcodeCapture}) async {
|
||||
try {
|
||||
final stockPickingRecords = objectboxManager.store
|
||||
.box<StockPickingRecordEntity>();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
Loading…
x
Reference in New Issue
Block a user