Web approach uniformity done
This commit is contained in:
@@ -130,13 +130,42 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
|
||||
}
|
||||
|
||||
Future<void> _pickInvoiceFile() async {
|
||||
final picker = ImagePicker();
|
||||
final picked = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 80,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() => _invoiceFile = picked);
|
||||
try {
|
||||
final picker = ImagePicker();
|
||||
final picked = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 80,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
_invoiceFile = picked;
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
final bytes = await picked.readAsBytes();
|
||||
final formData = FormData.fromMap({
|
||||
'file': MultipartFile.fromBytes(
|
||||
bytes,
|
||||
filename: picked.name.isNotEmpty ? picked.name : 'invoice_${DateTime.now().millisecondsSinceEpoch}.jpg',
|
||||
),
|
||||
'type': 'SALES_INVOICE',
|
||||
});
|
||||
|
||||
final uploadResp = await DioClient().dio.post(
|
||||
'/upload',
|
||||
data: formData,
|
||||
);
|
||||
|
||||
if (uploadResp.statusCode == 200) {
|
||||
setState(() {
|
||||
_invoiceUrl = uploadResp.data['url'];
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error uploading sales invoice attachment: $e');
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,11 +309,12 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
|
||||
try {
|
||||
String? finalInvoiceUrl = _invoiceUrl;
|
||||
|
||||
if (_invoiceFile != null) {
|
||||
if (_invoiceFile != null && finalInvoiceUrl == null) {
|
||||
final bytes = await _invoiceFile!.readAsBytes();
|
||||
final formData = FormData.fromMap({
|
||||
'file': await MultipartFile.fromFile(
|
||||
_invoiceFile!.path,
|
||||
filename: _invoiceFile!.name,
|
||||
'file': MultipartFile.fromBytes(
|
||||
bytes,
|
||||
filename: _invoiceFile!.name.isNotEmpty ? _invoiceFile!.name : 'invoice.jpg',
|
||||
),
|
||||
'type': 'SALES_INVOICE',
|
||||
});
|
||||
@@ -366,12 +396,15 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
|
||||
}
|
||||
|
||||
final invoiceDiscount = double.tryParse(_discountCtrl.text) ?? 0.0;
|
||||
totalDiscount = invoiceDiscount;
|
||||
grandTotal = (subtotal + totalMaking - invoiceDiscount).clamp(0.0, double.infinity) + totalTax;
|
||||
totalDiscount = double.parse(invoiceDiscount.toStringAsFixed(2));
|
||||
final rawGrandTotal = (subtotal + totalMaking - invoiceDiscount).clamp(0.0, double.infinity) + totalTax;
|
||||
grandTotal = double.parse(rawGrandTotal.toStringAsFixed(2));
|
||||
|
||||
final amountPaid = double.tryParse(_amountPaidCtrl.text) ?? (_isAmountPaidEdited ? 0.0 : grandTotal);
|
||||
final validAmountPaid = amountPaid > grandTotal ? grandTotal : amountPaid;
|
||||
final balanceDue = grandTotal - validAmountPaid;
|
||||
final validAmountPaid = double.parse((amountPaid > grandTotal ? grandTotal : amountPaid).toStringAsFixed(2));
|
||||
final balanceDue = double.parse((grandTotal - validAmountPaid).clamp(0.0, double.infinity).toStringAsFixed(2));
|
||||
final isFullyPaid = balanceDue.abs() < 0.01 || validAmountPaid >= grandTotal - 0.01;
|
||||
final determinedStatus = isFullyPaid ? 'PAID' : (validAmountPaid > 0.01 ? 'PARTIAL' : 'DRAFT');
|
||||
|
||||
final invoice = Invoice(
|
||||
id: widget.existingInvoice?.id,
|
||||
@@ -379,18 +412,20 @@ class _InvoiceBuilderScreenState extends ConsumerState<InvoiceBuilderScreen> {
|
||||
invoiceNumber: _invoiceNumberCtrl.text.trim(),
|
||||
issueDate: _invoiceDate,
|
||||
dueDate: _dueDate,
|
||||
subtotal: subtotal,
|
||||
taxTotal: totalTax,
|
||||
cgstTotal: totalCgst,
|
||||
sgstTotal: totalSgst,
|
||||
igstTotal: totalIgst,
|
||||
subtotal: double.parse(subtotal.toStringAsFixed(2)),
|
||||
taxTotal: double.parse(totalTax.toStringAsFixed(2)),
|
||||
cgstTotal: double.parse(totalCgst.toStringAsFixed(2)),
|
||||
sgstTotal: double.parse(totalSgst.toStringAsFixed(2)),
|
||||
igstTotal: double.parse(totalIgst.toStringAsFixed(2)),
|
||||
discountTotal: totalDiscount,
|
||||
totalAmount: grandTotal,
|
||||
amountPaid: validAmountPaid,
|
||||
paymentMethod: validAmountPaid > 0 ? _paymentMethod : null,
|
||||
paymentWalletId: validAmountPaid > 0 ? (_selectedWalletId ?? ref.read(walletProvider).value?.firstOrNull?.id) : null,
|
||||
nextPaymentDate: balanceDue > 0 ? (_nextPaymentDate ?? DateTime.now().add(const Duration(days: 30))) : null,
|
||||
status: widget.existingInvoice?.status ?? (validAmountPaid >= grandTotal ? 'PAID' : (validAmountPaid > 0 ? 'PARTIAL' : 'DRAFT')),
|
||||
nextPaymentDate: balanceDue > 0.01 ? (_nextPaymentDate ?? DateTime.now().add(const Duration(days: 30))) : null,
|
||||
status: widget.existingInvoice != null
|
||||
? (isFullyPaid ? 'PAID' : (validAmountPaid > 0.01 ? 'PARTIAL' : widget.existingInvoice!.status))
|
||||
: determinedStatus,
|
||||
notes: _notesCtrl.text.trim().isEmpty ? null : _notesCtrl.text.trim(),
|
||||
invoiceUrl: finalInvoiceUrl,
|
||||
isEmi: _isEmi,
|
||||
|
||||
Reference in New Issue
Block a user