This commit is contained in:
Phuoc Nguyen
2025-10-28 16:48:31 +07:00
parent 5cfc56f40d
commit 4b35d236df
11 changed files with 390 additions and 102 deletions

View File

@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/di/providers.dart';
import '../../../users/domain/entities/user_entity.dart';
import '../../data/models/create_product_warehouse_request.dart';
import '../../domain/entities/product_stage_entity.dart';
/// Product detail page
@@ -13,6 +14,7 @@ class ProductDetailPage extends ConsumerStatefulWidget {
final int productId;
final String warehouseName;
final int? stageId;
final String operationType;
const ProductDetailPage({
super.key,
@@ -20,6 +22,7 @@ class ProductDetailPage extends ConsumerStatefulWidget {
required this.productId,
required this.warehouseName,
this.stageId,
this.operationType = 'import',
});
@override
@@ -107,17 +110,23 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
final error = productDetailState.error;
final selectedIndex = productDetailState.selectedStageIndex;
// Get product name from stages if available
final productName = stages.isNotEmpty ? stages.first.productName : 'Product';
// Capitalize first letter of operation type
final operationTitle = widget.operationType == 'import' ? 'Import' : 'Export';
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Product Stages',
operationTitle,
style: textTheme.titleMedium,
),
Text(
widget.warehouseName,
productName,
style: textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
@@ -467,17 +476,32 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
),
]),
// Add button
SizedBox(
width: double.infinity,
child: FilledButton.icon(
onPressed: () => _addNewQuantities(stageToShow),
icon: const Icon(Icons.add),
label: const Text('Add Quantities'),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
// Action buttons
Row(
spacing: 12,
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () => _printQuantities(stageToShow),
icon: const Icon(Icons.print),
label: const Text('Print'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
),
),
Expanded(
child: FilledButton.icon(
onPressed: () => _addNewQuantities(stageToShow),
icon: const Icon(Icons.save),
label: const Text('Save'),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
),
],
),
],
),
@@ -489,7 +513,17 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
);
}
void _addNewQuantities(ProductStageEntity stage) {
void _printQuantities(ProductStageEntity stage) {
// TODO: Implement print functionality
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Print functionality coming soon'),
duration: Duration(seconds: 2),
),
);
}
Future<void> _addNewQuantities(ProductStageEntity stage) async {
// Parse the values from text fields
final passedQuantity = int.tryParse(_passedQuantityController.text) ?? 0;
final passedWeight = double.tryParse(_passedWeightController.text) ?? 0.0;
@@ -508,31 +542,115 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
return;
}
// TODO: Implement API call to add new quantities
// For now, just show a success message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Added: Passed Q=$passedQuantity, W=$passedWeight | Issued Q=$issuedQuantity, W=$issuedWeight',
// Validate that both users are selected
if (_selectedEmployee == null || _selectedWarehouseUser == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please select both Employee and Warehouse User'),
backgroundColor: Colors.orange,
),
backgroundColor: Colors.green,
duration: const Duration(seconds: 2),
);
return;
}
// Show loading dialog
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(
child: CircularProgressIndicator(),
),
);
// Log the values for debugging
debugPrint('Adding new quantities for stage ${stage.productStageId}:');
debugPrint(' Warehouse User: ${_selectedWarehouseUser?.fullName ?? "Not selected"}');
debugPrint(' Warehouse User ID: ${_selectedWarehouseUser?.id}');
debugPrint(' Employee: ${_selectedEmployee?.fullName ?? "Not selected"}');
debugPrint(' Employee ID: ${_selectedEmployee?.id}');
debugPrint(' Passed Quantity: $passedQuantity');
debugPrint(' Passed Weight: $passedWeight');
debugPrint(' Issued Quantity: $issuedQuantity');
debugPrint(' Issued Weight: $issuedWeight');
try {
// Determine actionTypeId based on operation type
// 4 = Import, 5 = Export
final typeId = widget.operationType == 'import' ? 4 : 5;
final actionTypeId = widget.operationType == 'import' ? 1 : 2;
// Clear the text fields after successful add
_clearControllers();
// Create request with all required fields
final request = CreateProductWarehouseRequest(
typeId: typeId, // Import type
productId: stage.productId,
stageId: stage.stageId ?? 0,
orderId: null,
recordDate: DateTime.now().toIso8601String(),
passedQuantityWeight: passedWeight,
passedQuantity: passedQuantity,
issuedQuantityWeight: issuedWeight,
issuedQuantity: issuedQuantity,
responsibleUserId: _selectedEmployee!.id,
description: '',
productName: stage.productName,
productCode: stage.productCode,
stockPassedQuantityWeight: stage.passedQuantityWeight,
stockPassedQuantity: stage.passedQuantity,
stockIssuedQuantity: stage.issuedQuantity,
stockIssuedQuantityWeight: stage.issuedQuantityWeight,
receiverUserId: _selectedWarehouseUser!.id,
actionTypeId: actionTypeId,
wareHouseId: widget.warehouseId,
productStageId: stage.productStageId ?? 0,
isConfirm: true,
);
// Call the repository to create product warehouse entry
final repository = ref.read(productsRepositoryProvider);
final result = await repository.createProductWarehouse(request);
// Dismiss loading dialog
if (mounted) Navigator.of(context).pop();
result.fold(
(failure) {
// Show error message
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to add quantities: ${failure.message}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
},
(_) {
// Success - show success message
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Quantities added successfully!'),
backgroundColor: Colors.green,
duration: const Duration(seconds: 2),
),
);
// Clear the text fields after successful add
_clearControllers();
// Refresh the product detail to show updated quantities
ref.read(productDetailProvider(_providerKey).notifier).refreshProductDetail(
widget.warehouseId,
widget.productId,
);
}
},
);
} catch (e) {
// Dismiss loading dialog
if (mounted) Navigator.of(context).pop();
// Show error message
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.toString()}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
}
}
Widget _buildStageHeader(ProductStageEntity stage, ThemeData theme) {

View File

@@ -214,6 +214,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
warehouseId: widget.warehouseId,
productId: productId,
warehouseName: widget.warehouseName,
operationType: widget.operationType,
stageId: stageId,
);
}
@@ -466,6 +467,7 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
warehouseId: widget.warehouseId,
productId: product.id,
warehouseName: widget.warehouseName,
operationType: widget.operationType,
);
},
);