2025-09-27 21:19:00 +03:30
|
|
|
|
import 'package:shamsi_date/shamsi_date.dart';
|
2025-09-20 01:17:27 +03:30
|
|
|
|
enum BusinessType {
|
|
|
|
|
|
company('شرکت'),
|
|
|
|
|
|
shop('مغازه'),
|
|
|
|
|
|
store('فروشگاه'),
|
|
|
|
|
|
union('اتحادیه'),
|
|
|
|
|
|
club('باشگاه'),
|
|
|
|
|
|
institute('موسسه'),
|
|
|
|
|
|
individual('شخصی');
|
|
|
|
|
|
|
|
|
|
|
|
const BusinessType(this.displayName);
|
|
|
|
|
|
final String displayName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum BusinessField {
|
|
|
|
|
|
manufacturing('تولیدی'),
|
|
|
|
|
|
commercial('بازرگانی'),
|
|
|
|
|
|
service('خدماتی'),
|
|
|
|
|
|
other('سایر');
|
|
|
|
|
|
|
|
|
|
|
|
const BusinessField(this.displayName);
|
|
|
|
|
|
final String displayName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class BusinessData {
|
|
|
|
|
|
// مرحله 1: اطلاعات پایه
|
|
|
|
|
|
String name;
|
|
|
|
|
|
BusinessType? businessType;
|
|
|
|
|
|
BusinessField? businessField;
|
|
|
|
|
|
|
|
|
|
|
|
// مرحله 2: اطلاعات تماس
|
|
|
|
|
|
String? address;
|
|
|
|
|
|
String? phone;
|
|
|
|
|
|
String? mobile;
|
|
|
|
|
|
String? postalCode;
|
|
|
|
|
|
|
|
|
|
|
|
// مرحله 3: اطلاعات قانونی
|
|
|
|
|
|
String? nationalId;
|
|
|
|
|
|
String? registrationNumber;
|
|
|
|
|
|
String? economicId;
|
|
|
|
|
|
|
|
|
|
|
|
// مرحله 4: اطلاعات جغرافیایی
|
|
|
|
|
|
String? country;
|
|
|
|
|
|
String? province;
|
|
|
|
|
|
String? city;
|
|
|
|
|
|
|
2025-09-27 21:19:00 +03:30
|
|
|
|
// مرحله 5: سال(های) مالی
|
|
|
|
|
|
List<FiscalYearData> fiscalYears;
|
|
|
|
|
|
|
2025-09-28 23:06:53 +03:30
|
|
|
|
// ارزها
|
|
|
|
|
|
int? defaultCurrencyId;
|
|
|
|
|
|
List<int> currencyIds;
|
|
|
|
|
|
|
2025-09-20 01:17:27 +03:30
|
|
|
|
BusinessData({
|
|
|
|
|
|
this.name = '',
|
|
|
|
|
|
this.businessType,
|
|
|
|
|
|
this.businessField,
|
|
|
|
|
|
this.address,
|
|
|
|
|
|
this.phone,
|
|
|
|
|
|
this.mobile,
|
|
|
|
|
|
this.postalCode,
|
|
|
|
|
|
this.nationalId,
|
|
|
|
|
|
this.registrationNumber,
|
|
|
|
|
|
this.economicId,
|
|
|
|
|
|
this.country,
|
|
|
|
|
|
this.province,
|
|
|
|
|
|
this.city,
|
2025-09-27 21:19:00 +03:30
|
|
|
|
List<FiscalYearData>? fiscalYears,
|
2025-09-28 23:06:53 +03:30
|
|
|
|
this.defaultCurrencyId,
|
|
|
|
|
|
List<int>? currencyIds,
|
|
|
|
|
|
}) : fiscalYears = fiscalYears ?? <FiscalYearData>[],
|
|
|
|
|
|
currencyIds = currencyIds ?? <int>[];
|
2025-09-20 01:17:27 +03:30
|
|
|
|
|
|
|
|
|
|
// تبدیل به Map برای ارسال به API
|
|
|
|
|
|
Map<String, dynamic> toJson() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
'name': name,
|
2025-09-27 21:19:00 +03:30
|
|
|
|
// بکاند انتظار مقادیر فارسی enum را دارد
|
|
|
|
|
|
'business_type': businessType?.displayName,
|
|
|
|
|
|
'business_field': businessField?.displayName,
|
2025-09-20 01:17:27 +03:30
|
|
|
|
'address': address,
|
|
|
|
|
|
'phone': phone,
|
|
|
|
|
|
'mobile': mobile,
|
|
|
|
|
|
'postal_code': postalCode,
|
|
|
|
|
|
'national_id': nationalId,
|
|
|
|
|
|
'registration_number': registrationNumber,
|
|
|
|
|
|
'economic_id': economicId,
|
|
|
|
|
|
'country': country,
|
|
|
|
|
|
'province': province,
|
|
|
|
|
|
'city': city,
|
2025-09-27 21:19:00 +03:30
|
|
|
|
'fiscal_years': fiscalYears.map((e) => e.toJson()).toList(),
|
2025-09-28 23:06:53 +03:30
|
|
|
|
'default_currency_id': defaultCurrencyId,
|
|
|
|
|
|
'currency_ids': _buildCurrencyIdsPayload(),
|
2025-09-20 01:17:27 +03:30
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-28 23:06:53 +03:30
|
|
|
|
List<int>? _buildCurrencyIdsPayload() {
|
|
|
|
|
|
if (defaultCurrencyId == null && currencyIds.isEmpty) return null;
|
|
|
|
|
|
final setIds = <int>{...currencyIds};
|
|
|
|
|
|
if (defaultCurrencyId != null) setIds.add(defaultCurrencyId!);
|
|
|
|
|
|
return setIds.toList();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-20 01:17:27 +03:30
|
|
|
|
// کپی کردن با تغییرات
|
|
|
|
|
|
BusinessData copyWith({
|
|
|
|
|
|
String? name,
|
|
|
|
|
|
BusinessType? businessType,
|
|
|
|
|
|
BusinessField? businessField,
|
|
|
|
|
|
String? address,
|
|
|
|
|
|
String? phone,
|
|
|
|
|
|
String? mobile,
|
|
|
|
|
|
String? postalCode,
|
|
|
|
|
|
String? nationalId,
|
|
|
|
|
|
String? registrationNumber,
|
|
|
|
|
|
String? economicId,
|
|
|
|
|
|
String? country,
|
|
|
|
|
|
String? province,
|
|
|
|
|
|
String? city,
|
2025-09-27 21:19:00 +03:30
|
|
|
|
List<FiscalYearData>? fiscalYears,
|
2025-09-28 23:06:53 +03:30
|
|
|
|
int? defaultCurrencyId,
|
|
|
|
|
|
List<int>? currencyIds,
|
2025-09-20 01:17:27 +03:30
|
|
|
|
}) {
|
|
|
|
|
|
return BusinessData(
|
|
|
|
|
|
name: name ?? this.name,
|
|
|
|
|
|
businessType: businessType ?? this.businessType,
|
|
|
|
|
|
businessField: businessField ?? this.businessField,
|
|
|
|
|
|
address: address ?? this.address,
|
|
|
|
|
|
phone: phone ?? this.phone,
|
|
|
|
|
|
mobile: mobile ?? this.mobile,
|
|
|
|
|
|
postalCode: postalCode ?? this.postalCode,
|
|
|
|
|
|
nationalId: nationalId ?? this.nationalId,
|
|
|
|
|
|
registrationNumber: registrationNumber ?? this.registrationNumber,
|
|
|
|
|
|
economicId: economicId ?? this.economicId,
|
|
|
|
|
|
country: country ?? this.country,
|
|
|
|
|
|
province: province ?? this.province,
|
|
|
|
|
|
city: city ?? this.city,
|
2025-09-27 21:19:00 +03:30
|
|
|
|
fiscalYears: fiscalYears ?? this.fiscalYears,
|
2025-09-28 23:06:53 +03:30
|
|
|
|
defaultCurrencyId: defaultCurrencyId ?? this.defaultCurrencyId,
|
|
|
|
|
|
currencyIds: currencyIds ?? this.currencyIds,
|
2025-09-20 01:17:27 +03:30
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// بررسی اعتبار مرحله 1
|
|
|
|
|
|
bool isStep1Valid() {
|
|
|
|
|
|
return name.isNotEmpty && businessType != null && businessField != null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// بررسی اعتبار مرحله 2 (اختیاری)
|
|
|
|
|
|
bool isStep2Valid() {
|
|
|
|
|
|
// اعتبارسنجی موبایل اگر وارد شده باشد
|
|
|
|
|
|
if (mobile != null && mobile!.isNotEmpty) {
|
|
|
|
|
|
if (!_isValidMobile(mobile!)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// اعتبارسنجی تلفن ثابت اگر وارد شده باشد
|
|
|
|
|
|
if (phone != null && phone!.isNotEmpty) {
|
|
|
|
|
|
if (!_isValidPhone(phone!)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// بررسی اعتبار مرحله 3 (اختیاری)
|
|
|
|
|
|
bool isStep3Valid() {
|
|
|
|
|
|
// اعتبارسنجی کد ملی اگر وارد شده باشد
|
|
|
|
|
|
if (nationalId != null && nationalId!.isNotEmpty) {
|
|
|
|
|
|
if (!_isValidNationalId(nationalId!)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-27 21:19:00 +03:30
|
|
|
|
// بررسی اعتبار مرحله 4 (اطلاعات جغرافیایی - اختیاری)
|
2025-09-20 01:17:27 +03:30
|
|
|
|
bool isStep4Valid() {
|
2025-09-27 21:19:00 +03:30
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// بررسی اعتبار مرحله 5 (سال مالی - اجباری)
|
|
|
|
|
|
bool isFiscalStepValid() {
|
|
|
|
|
|
if (fiscalYears.isEmpty) return false;
|
|
|
|
|
|
final fy = fiscalYears.first;
|
|
|
|
|
|
if (fy.title.trim().isEmpty || fy.startDate == null || fy.endDate == null) return false;
|
|
|
|
|
|
if (fy.startDate!.isAfter(fy.endDate!)) return false;
|
|
|
|
|
|
return true;
|
2025-09-20 01:17:27 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// بررسی اعتبار کل فرم
|
|
|
|
|
|
bool isFormValid() {
|
2025-09-27 21:19:00 +03:30
|
|
|
|
return isStep1Valid() && isStep2Valid() && isStep3Valid() && isStep4Valid() && isFiscalStepValid();
|
2025-09-20 01:17:27 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// اعتبارسنجی شماره موبایل ایرانی
|
|
|
|
|
|
bool _isValidMobile(String mobile) {
|
|
|
|
|
|
// حذف فاصلهها و کاراکترهای اضافی
|
|
|
|
|
|
String cleanMobile = mobile.replaceAll(RegExp(r'[\s\-\(\)]'), '');
|
|
|
|
|
|
|
|
|
|
|
|
// بررسی فرمتهای مختلف موبایل ایرانی
|
2025-09-28 23:06:53 +03:30
|
|
|
|
RegExp mobileRegex = RegExp(r'^(\+98|0)?9\d{9} |