progress in invoices
This commit is contained in:
parent
7f6a78f642
commit
2591e9a7a9
|
|
@ -479,6 +479,7 @@ class AuthStore with ChangeNotifier {
|
||||||
Future<void> _ensureCurrencyForBusiness() async {
|
Future<void> _ensureCurrencyForBusiness() async {
|
||||||
final business = _currentBusiness;
|
final business = _currentBusiness;
|
||||||
if (business == null) return;
|
if (business == null) return;
|
||||||
|
|
||||||
// اگر ارزی انتخاب نشده، یا کد/شناسه فعلی جزو ارزهای کسبوکار نیست
|
// اگر ارزی انتخاب نشده، یا کد/شناسه فعلی جزو ارزهای کسبوکار نیست
|
||||||
final allowedCodes = business.currencies.map((c) => c.code).toSet();
|
final allowedCodes = business.currencies.map((c) => c.code).toSet();
|
||||||
final allowedIds = business.currencies.map((c) => c.id).toSet();
|
final allowedIds = business.currencies.map((c) => c.id).toSet();
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import '../../models/customer_model.dart';
|
||||||
import '../../models/person_model.dart';
|
import '../../models/person_model.dart';
|
||||||
import '../../widgets/invoice/line_items_table.dart';
|
import '../../widgets/invoice/line_items_table.dart';
|
||||||
import '../../utils/number_formatters.dart';
|
import '../../utils/number_formatters.dart';
|
||||||
|
import '../../services/currency_service.dart';
|
||||||
|
import '../../core/api_client.dart';
|
||||||
|
|
||||||
class NewInvoicePage extends StatefulWidget {
|
class NewInvoicePage extends StatefulWidget {
|
||||||
final int businessId;
|
final int businessId;
|
||||||
|
|
@ -62,8 +64,37 @@ class _NewInvoicePageState extends State<NewInvoicePage> with SingleTickerProvid
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = TabController(length: 4, vsync: this); // شروع با 4 تب
|
_tabController = TabController(length: 4, vsync: this); // شروع با 4 تب
|
||||||
|
// تنظیم نوع فاکتور پیشفرض
|
||||||
|
_selectedInvoiceType = InvoiceType.sales;
|
||||||
// تنظیم ارز پیشفرض از AuthStore
|
// تنظیم ارز پیشفرض از AuthStore
|
||||||
_selectedCurrencyId = widget.authStore.selectedCurrencyId;
|
_selectedCurrencyId = widget.authStore.selectedCurrencyId;
|
||||||
|
// اگر ارز انتخاب نشده، ارز پیشفرض را بارگذاری کن
|
||||||
|
if (_selectedCurrencyId == null) {
|
||||||
|
_loadDefaultCurrency();
|
||||||
|
}
|
||||||
|
// تنظیم تاریخهای پیشفرض
|
||||||
|
_invoiceDate = DateTime.now();
|
||||||
|
_dueDate = DateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadDefaultCurrency() async {
|
||||||
|
try {
|
||||||
|
final currencyService = CurrencyService(ApiClient());
|
||||||
|
final currencies = await currencyService.listBusinessCurrencies(businessId: widget.businessId);
|
||||||
|
if (currencies.isNotEmpty) {
|
||||||
|
// ارز پیشفرض را پیدا کن
|
||||||
|
final defaultCurrency = currencies.firstWhere(
|
||||||
|
(c) => c['is_default'] == true,
|
||||||
|
orElse: () => currencies.first,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
_selectedCurrencyId = defaultCurrency['id'] as int;
|
||||||
|
});
|
||||||
|
// ارز پیشفرض بارگذاری شد
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// خطا در بارگذاری ارز پیشفرض
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,25 @@ class _CurrencyPickerWidgetState extends State<CurrencyPickerWidget> {
|
||||||
List<Map<String, dynamic>> _currencies = [];
|
List<Map<String, dynamic>> _currencies = [];
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
String? _error;
|
String? _error;
|
||||||
|
int? _selectedValue;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_selectedValue = widget.selectedCurrencyId;
|
||||||
_loadCurrencies();
|
_loadCurrencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CurrencyPickerWidget oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.selectedCurrencyId != widget.selectedCurrencyId) {
|
||||||
|
setState(() {
|
||||||
|
_selectedValue = widget.selectedCurrencyId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadCurrencies() async {
|
Future<void> _loadCurrencies() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
|
@ -61,21 +73,33 @@ class _CurrencyPickerWidgetState extends State<CurrencyPickerWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_isLoading) {
|
if (_isLoading) {
|
||||||
return const SizedBox(
|
return SizedBox(
|
||||||
height: 56,
|
height: 56,
|
||||||
child: Center(
|
child: InputDecorator(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: widget.label ?? 'ارز',
|
||||||
|
hintText: widget.hintText ?? 'انتخاب ارز',
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_error != null) {
|
if (_error != null) {
|
||||||
return Container(
|
return SizedBox(
|
||||||
height: 56,
|
height: 56,
|
||||||
padding: const EdgeInsets.all(16),
|
child: InputDecorator(
|
||||||
decoration: BoxDecoration(
|
decoration: InputDecoration(
|
||||||
border: Border.all(color: Colors.red),
|
labelText: widget.label ?? 'ارز',
|
||||||
borderRadius: BorderRadius.circular(8),
|
hintText: widget.hintText ?? 'انتخاب ارز',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
enabled: false,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -83,7 +107,7 @@ class _CurrencyPickerWidgetState extends State<CurrencyPickerWidget> {
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'خطا در بارگذاری ارزها: $_error',
|
'خطا در بارگذاری ارزها',
|
||||||
style: const TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -93,32 +117,46 @@ class _CurrencyPickerWidgetState extends State<CurrencyPickerWidget> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currencies.isEmpty) {
|
if (_currencies.isEmpty) {
|
||||||
return Container(
|
return SizedBox(
|
||||||
height: 56,
|
height: 56,
|
||||||
padding: const EdgeInsets.all(16),
|
child: InputDecorator(
|
||||||
decoration: BoxDecoration(
|
decoration: InputDecoration(
|
||||||
border: Border.all(color: Colors.grey),
|
labelText: widget.label ?? 'ارز',
|
||||||
borderRadius: BorderRadius.circular(8),
|
hintText: widget.hintText ?? 'انتخاب ارز',
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
enabled: false,
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text('هیچ ارزی یافت نشد'),
|
child: Text('هیچ ارزی یافت نشد'),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DropdownButtonFormField<int>(
|
return SizedBox(
|
||||||
value: widget.selectedCurrencyId,
|
height: 56, // ارتفاع ثابت مثل سایر فیلدها
|
||||||
onChanged: widget.enabled ? widget.onChanged : null,
|
child: InputDecorator(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: widget.label ?? 'ارز',
|
labelText: widget.label ?? 'ارز',
|
||||||
hintText: widget.hintText ?? 'انتخاب ارز',
|
hintText: widget.hintText ?? 'انتخاب ارز',
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
enabled: widget.enabled,
|
enabled: widget.enabled,
|
||||||
),
|
),
|
||||||
|
child: DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton<int>(
|
||||||
|
value: _selectedValue,
|
||||||
|
isExpanded: true,
|
||||||
|
onChanged: widget.enabled ? (value) {
|
||||||
|
setState(() {
|
||||||
|
_selectedValue = value;
|
||||||
|
});
|
||||||
|
widget.onChanged(value);
|
||||||
|
} : null,
|
||||||
items: _currencies.map((currency) {
|
items: _currencies.map((currency) {
|
||||||
final isDefault = currency['is_default'] == true;
|
final isDefault = currency['is_default'] == true;
|
||||||
return DropdownMenuItem<int>(
|
return DropdownMenuItem<int>(
|
||||||
|
|
@ -153,12 +191,9 @@ class _CurrencyPickerWidgetState extends State<CurrencyPickerWidget> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
validator: (value) {
|
),
|
||||||
if (value == null) {
|
),
|
||||||
return 'انتخاب ارز الزامی است';
|
),
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,8 @@ class _InvoiceLineItemsTableState extends State<InvoiceLineItemsTable> {
|
||||||
if (!isTaxable) return 0;
|
if (!isTaxable) return 0;
|
||||||
|
|
||||||
final v = p['purchase_tax_rate'];
|
final v = p['purchase_tax_rate'];
|
||||||
if (v is num && v > 0) return v;
|
final rate = _toNum(v);
|
||||||
|
if (rate > 0) return rate;
|
||||||
// اگر محصول نرخ مالیات خرید نداشته باشد، از نرخ پیشفرض استفاده کن
|
// اگر محصول نرخ مالیات خرید نداشته باشد، از نرخ پیشفرض استفاده کن
|
||||||
return _getDefaultTaxRateForInvoiceType();
|
return _getDefaultTaxRateForInvoiceType();
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +175,8 @@ class _InvoiceLineItemsTableState extends State<InvoiceLineItemsTable> {
|
||||||
if (!isTaxable) return 0;
|
if (!isTaxable) return 0;
|
||||||
|
|
||||||
final v = p['sales_tax_rate'];
|
final v = p['sales_tax_rate'];
|
||||||
if (v is num && v > 0) return v;
|
final rate = _toNum(v);
|
||||||
|
if (rate > 0) return rate;
|
||||||
// اگر محصول نرخ مالیات فروش نداشته باشد، از نرخ پیشفرض استفاده کن
|
// اگر محصول نرخ مالیات فروش نداشته باشد، از نرخ پیشفرض استفاده کن
|
||||||
return _getDefaultTaxRateForInvoiceType();
|
return _getDefaultTaxRateForInvoiceType();
|
||||||
}
|
}
|
||||||
|
|
@ -353,8 +355,11 @@ class _InvoiceLineItemsTableState extends State<InvoiceLineItemsTable> {
|
||||||
_notify();
|
_notify();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final mainUnit = p['main_unit']?.toString();
|
final mainUnit = p['main_unit']?.toString();
|
||||||
final secondaryUnit = p['secondary_unit']?.toString();
|
final secondaryUnit = p['secondary_unit']?.toString();
|
||||||
|
final taxRate = _defaultTaxRateFromProduct(p);
|
||||||
|
|
||||||
final updated = item.copyWith(
|
final updated = item.copyWith(
|
||||||
productId: _toInt(p['id']),
|
productId: _toInt(p['id']),
|
||||||
productCode: p['code']?.toString(),
|
productCode: p['code']?.toString(),
|
||||||
|
|
@ -365,7 +370,7 @@ class _InvoiceLineItemsTableState extends State<InvoiceLineItemsTable> {
|
||||||
selectedUnit: mainUnit,
|
selectedUnit: mainUnit,
|
||||||
baseSalesPriceMainUnit: _toNum(p['base_sales_price']),
|
baseSalesPriceMainUnit: _toNum(p['base_sales_price']),
|
||||||
basePurchasePriceMainUnit: _toNum(p['base_purchase_price']),
|
basePurchasePriceMainUnit: _toNum(p['base_purchase_price']),
|
||||||
taxRate: _defaultTaxRateFromProduct(p),
|
taxRate: taxRate,
|
||||||
minOrderQty: _toInt(p['min_order_qty']),
|
minOrderQty: _toInt(p['min_order_qty']),
|
||||||
trackInventory: p['track_inventory'] == true,
|
trackInventory: p['track_inventory'] == true,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue