barcode_scanner/lib/pages/operation/reception/reception_details_page.dart
your-name ab4a56ed41 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.
2025-07-30 20:26:17 +03:00

171 lines
7.3 KiB
Dart

import 'package:e_scan/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart';
import 'package:e_scan/components/components.dart';
import 'package:e_scan/pages/operation/reception/reception_details_page_model.dart';
import 'package:e_scan/router/go_secure_router_builder.dart';
import 'package:e_scan/themes/app_theme.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
class ReceptionDetailsPage extends ConsumerStatefulWidget {
const ReceptionDetailsPage({super.key, required this.receptionId});
final int receptionId;
@override
ConsumerState<ReceptionDetailsPage> createState() =>
_ReceptionDetailsPageState();
}
class _ReceptionDetailsPageState extends ConsumerState<ReceptionDetailsPage> {
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
ref
.read(receptionDetailsPageModelProvider.notifier)
.getReceptionById(id: widget.receptionId);
});
}
@override
Widget build(BuildContext context) {
final state = ref.watch(receptionDetailsPageModelProvider);
final reception = state.reception;
return Scaffold(
backgroundColor: AppTheme.of(context).primaryBackground,
appBar: MainAppbarComponent(
leading: BackButton(color: AppTheme.of(context).white),
title: "Réceptions",
subTitle: "Opérations d'Entrepôt",
),
body: state.loading
? Center(child: LoadingProgressComponent())
: RefreshIndicator(
color: AppTheme.of(context).white,
backgroundColor: AppTheme.of(context).primary,
onRefresh: () async {
await ref
.read(receptionDetailsPageModelProvider.notifier)
.getReceptionById(id: widget.receptionId);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView(
children: [
const SizedBox(height: 16),
if (reception?.isDone == false) ...[
QuickActionComponent(
onTapScan: () {
final id = reception?.id;
if (id == null) return;
ReceptionScanRoute(receptionId: id)
.push(context)
.then(
(v) => ref
.read(
receptionDetailsPageModelProvider
.notifier,
)
.getReceptionById(id: widget.receptionId),
);
},
),
const SizedBox(height: 16),
],
Text(
'Détails réception',
style: AppTheme.of(
context,
).bodyMedium.copyWith(fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
StockPickingCard(
isDraft: reception?.isDraft == true,
isDone: reception?.isDone == true,
margin: EdgeInsets.symmetric(horizontal: 5),
reference: reception?.name ?? '',
from: reception?.locationId.target?.completeName,
to: reception?.locationDestId.target?.completeName,
contact: reception?.partnerId.target?.displayName,
origin: reception?.origin,
status: reception?.state,
),
SizedBox(height: 20),
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.target?.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() ??
[],
],
),
),
),
floatingActionButton: reception?.isDone == false
? FloatingActionButton(
backgroundColor: AppTheme.of(context).primary,
onPressed: () {
final id = reception?.id;
if (id == null) return;
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),
)
: null,
);
}
}