forked from hesabix/arc
867 lines
28 KiB
Dart
Executable file
867 lines
28 KiB
Dart
Executable file
import 'package:shamsi_date/shamsi_date.dart';
|
||
|
||
/// Converts JSON value to bool safely (handles int 0/1 and string 'true'/'false' from API).
|
||
bool _fromJsonBool(dynamic v, [bool defaultValue = false]) {
|
||
if (v == null) return defaultValue;
|
||
if (v is bool) return v;
|
||
if (v is int) return v != 0;
|
||
if (v is String) return v.toLowerCase() == 'true' || v == '1';
|
||
return defaultValue;
|
||
}
|
||
|
||
DateTime _parsePersonDateTime(dynamic value) {
|
||
if (value == null) return DateTime.now();
|
||
if (value is DateTime) return value;
|
||
if (value is int) {
|
||
return DateTime.fromMillisecondsSinceEpoch(value);
|
||
}
|
||
|
||
final raw = value.toString().trim();
|
||
if (raw.isEmpty) return DateTime.now();
|
||
|
||
// First try ISO-compatible date parsing.
|
||
try {
|
||
return DateTime.parse(raw);
|
||
} catch (_) {}
|
||
|
||
// Handle Jalali or Gregorian slash dates like 1405/04/18 17:08:49.
|
||
final parts = raw.split(' ');
|
||
final datePart = parts[0];
|
||
final timePart = parts.length > 1 ? parts[1] : '';
|
||
final dateSegments = datePart.split('/');
|
||
if (dateSegments.length == 3) {
|
||
final year = int.tryParse(dateSegments[0]);
|
||
final month = int.tryParse(dateSegments[1]);
|
||
final day = int.tryParse(dateSegments[2]);
|
||
if (year != null && month != null && day != null) {
|
||
int hour = 0;
|
||
int minute = 0;
|
||
int second = 0;
|
||
if (timePart.isNotEmpty) {
|
||
final timeSegments = timePart.split(':');
|
||
if (timeSegments.length >= 2) {
|
||
hour = int.tryParse(timeSegments[0]) ?? 0;
|
||
minute = int.tryParse(timeSegments[1]) ?? 0;
|
||
if (timeSegments.length >= 3) {
|
||
second = int.tryParse(timeSegments[2]) ?? 0;
|
||
}
|
||
}
|
||
}
|
||
try {
|
||
if (year >= 1200 && year <= 1600) {
|
||
final dt = Jalali(year, month, day).toDateTime();
|
||
return DateTime(dt.year, dt.month, dt.day, hour, minute, second);
|
||
}
|
||
return DateTime(year, month, day, hour, minute, second);
|
||
} catch (_) {
|
||
// fallthrough to default
|
||
}
|
||
}
|
||
}
|
||
|
||
return DateTime.now();
|
||
}
|
||
|
||
class PersonBankAccount {
|
||
final int? id;
|
||
final int personId;
|
||
final String bankName;
|
||
final String? accountNumber;
|
||
final String? cardNumber;
|
||
final String? shebaNumber;
|
||
final bool isActive;
|
||
final DateTime createdAt;
|
||
final DateTime updatedAt;
|
||
|
||
PersonBankAccount({
|
||
this.id,
|
||
required this.personId,
|
||
required this.bankName,
|
||
this.accountNumber,
|
||
this.cardNumber,
|
||
this.shebaNumber,
|
||
this.isActive = true,
|
||
required this.createdAt,
|
||
required this.updatedAt,
|
||
});
|
||
|
||
factory PersonBankAccount.fromJson(Map<String, dynamic> json) {
|
||
return PersonBankAccount(
|
||
id: json['id'],
|
||
personId: json['person_id'],
|
||
bankName: json['bank_name'],
|
||
accountNumber: json['account_number'],
|
||
cardNumber: json['card_number'],
|
||
shebaNumber: json['sheba_number'],
|
||
isActive: _fromJsonBool(json['is_active'], true),
|
||
createdAt: _parsePersonDateTime(json['created_at'] ?? json['created_at_raw']),
|
||
updatedAt: _parsePersonDateTime(json['updated_at'] ?? json['updated_at_raw']),
|
||
);
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return {
|
||
'id': id,
|
||
'person_id': personId,
|
||
'bank_name': bankName,
|
||
'account_number': accountNumber,
|
||
'card_number': cardNumber,
|
||
'sheba_number': shebaNumber,
|
||
'is_active': isActive,
|
||
'created_at': createdAt.toIso8601String(),
|
||
'updated_at': updatedAt.toIso8601String(),
|
||
};
|
||
}
|
||
|
||
PersonBankAccount copyWith({
|
||
int? id,
|
||
int? personId,
|
||
String? bankName,
|
||
String? accountNumber,
|
||
String? cardNumber,
|
||
String? shebaNumber,
|
||
bool? isActive,
|
||
DateTime? createdAt,
|
||
DateTime? updatedAt,
|
||
}) {
|
||
return PersonBankAccount(
|
||
id: id ?? this.id,
|
||
personId: personId ?? this.personId,
|
||
bankName: bankName ?? this.bankName,
|
||
accountNumber: accountNumber ?? this.accountNumber,
|
||
cardNumber: cardNumber ?? this.cardNumber,
|
||
shebaNumber: shebaNumber ?? this.shebaNumber,
|
||
isActive: isActive ?? this.isActive,
|
||
createdAt: createdAt ?? this.createdAt,
|
||
updatedAt: updatedAt ?? this.updatedAt,
|
||
);
|
||
}
|
||
}
|
||
|
||
class PersonSocialContact {
|
||
final int? id;
|
||
final int personId;
|
||
final String platformKey;
|
||
final String? customLabel;
|
||
final String value;
|
||
final int sortOrder;
|
||
final DateTime createdAt;
|
||
final DateTime updatedAt;
|
||
|
||
PersonSocialContact({
|
||
this.id,
|
||
required this.personId,
|
||
required this.platformKey,
|
||
this.customLabel,
|
||
required this.value,
|
||
this.sortOrder = 0,
|
||
required this.createdAt,
|
||
required this.updatedAt,
|
||
});
|
||
|
||
factory PersonSocialContact.fromJson(Map<String, dynamic> json) {
|
||
return PersonSocialContact(
|
||
id: json['id'] as int?,
|
||
personId: (json['person_id'] as int?) ?? 0,
|
||
platformKey: (json['platform_key'] as String?)?.trim() ?? 'telegram',
|
||
customLabel: json['custom_label'] as String?,
|
||
value: (json['value'] as String?) ?? '',
|
||
sortOrder: (json['sort_order'] as int?) ?? 0,
|
||
createdAt: _parsePersonDateTime(json['created_at'] ?? json['created_at_raw']),
|
||
updatedAt: _parsePersonDateTime(json['updated_at'] ?? json['updated_at_raw']),
|
||
);
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return {
|
||
'id': id,
|
||
'person_id': personId,
|
||
'platform_key': platformKey,
|
||
'custom_label': customLabel,
|
||
'value': value,
|
||
'sort_order': sortOrder,
|
||
'created_at': createdAt.toIso8601String(),
|
||
'updated_at': updatedAt.toIso8601String(),
|
||
};
|
||
}
|
||
|
||
/// بدنه API برای create/update
|
||
Map<String, dynamic> toApiWrite() {
|
||
return {
|
||
'platform_key': platformKey,
|
||
'custom_label': customLabel,
|
||
'value': value,
|
||
};
|
||
}
|
||
|
||
PersonSocialContact copyWith({
|
||
int? id,
|
||
int? personId,
|
||
String? platformKey,
|
||
String? customLabel,
|
||
String? value,
|
||
int? sortOrder,
|
||
DateTime? createdAt,
|
||
DateTime? updatedAt,
|
||
}) {
|
||
return PersonSocialContact(
|
||
id: id ?? this.id,
|
||
personId: personId ?? this.personId,
|
||
platformKey: platformKey ?? this.platformKey,
|
||
customLabel: customLabel ?? this.customLabel,
|
||
value: value ?? this.value,
|
||
sortOrder: sortOrder ?? this.sortOrder,
|
||
createdAt: createdAt ?? this.createdAt,
|
||
updatedAt: updatedAt ?? this.updatedAt,
|
||
);
|
||
}
|
||
}
|
||
|
||
enum PersonType {
|
||
customer('مشتری', 'Customer'),
|
||
marketer('بازاریاب', 'Marketer'),
|
||
employee('کارمند', 'Employee'),
|
||
supplier('تامینکننده', 'Supplier'),
|
||
partner('همکار', 'Partner'),
|
||
seller('فروشنده', 'Seller'),
|
||
shareholder('سهامدار', 'Shareholder');
|
||
|
||
const PersonType(this.persianName, this.englishName);
|
||
final String persianName;
|
||
final String englishName;
|
||
|
||
static PersonType fromString(String value) {
|
||
return PersonType.values.firstWhere(
|
||
(type) => type.persianName == value || type.englishName == value,
|
||
orElse: () => PersonType.customer,
|
||
);
|
||
}
|
||
}
|
||
|
||
class Person {
|
||
final int? id;
|
||
final int businessId;
|
||
final int? code;
|
||
final String aliasName;
|
||
final String? firstName;
|
||
final String? lastName;
|
||
final List<PersonType> personTypes;
|
||
final String? companyName;
|
||
final String? namePrefix;
|
||
final String legalEntityType;
|
||
final String? paymentId;
|
||
final String? nationalId;
|
||
final String? registrationNumber;
|
||
final String? economicId;
|
||
final String? country;
|
||
final String? province;
|
||
final String? city;
|
||
final String? address;
|
||
final String? postalCode;
|
||
final String? phone;
|
||
final String? mobile;
|
||
final String? mobile2;
|
||
final String? mobile3;
|
||
final String? fax;
|
||
final String? email;
|
||
final String? website;
|
||
final bool isActive;
|
||
final DateTime createdAt;
|
||
final DateTime updatedAt;
|
||
final List<PersonBankAccount> bankAccounts;
|
||
final List<PersonSocialContact> socialContacts;
|
||
final int? shareCount;
|
||
// پورسانت
|
||
final double? commissionSalePercent;
|
||
final double? commissionSalesReturnPercent;
|
||
final double? commissionSalesAmount;
|
||
final double? commissionSalesReturnAmount;
|
||
final bool commissionExcludeDiscounts;
|
||
final bool commissionExcludeAdditionsDeductions;
|
||
final bool commissionPostInInvoiceDocument;
|
||
|
||
// تراز و وضعیت مالی (مبالغ به ارز پایه؛ تراز = بستانکار − بدهکار)
|
||
final double? balance;
|
||
final String? status;
|
||
final double? totalDebit;
|
||
final double? totalCredit;
|
||
|
||
/// گروه اشخاص (دستهبندی)
|
||
final int? personGroupId;
|
||
final String? personGroupName;
|
||
|
||
/// کد ارزهای غیرپایه با گردش (کشفپذیری MC)
|
||
final List<String> foreignCurrencyCodes;
|
||
|
||
Person({
|
||
this.id,
|
||
required this.businessId,
|
||
this.code,
|
||
required this.aliasName,
|
||
this.firstName,
|
||
this.lastName,
|
||
required this.personTypes,
|
||
this.companyName,
|
||
this.namePrefix,
|
||
this.legalEntityType = 'natural',
|
||
this.paymentId,
|
||
this.nationalId,
|
||
this.registrationNumber,
|
||
this.economicId,
|
||
this.country,
|
||
this.province,
|
||
this.city,
|
||
this.address,
|
||
this.postalCode,
|
||
this.phone,
|
||
this.mobile,
|
||
this.mobile2,
|
||
this.mobile3,
|
||
this.fax,
|
||
this.email,
|
||
this.website,
|
||
this.isActive = true,
|
||
required this.createdAt,
|
||
required this.updatedAt,
|
||
this.bankAccounts = const [],
|
||
this.socialContacts = const [],
|
||
this.shareCount,
|
||
this.commissionSalePercent,
|
||
this.commissionSalesReturnPercent,
|
||
this.commissionSalesAmount,
|
||
this.commissionSalesReturnAmount,
|
||
this.commissionExcludeDiscounts = false,
|
||
this.commissionExcludeAdditionsDeductions = false,
|
||
this.commissionPostInInvoiceDocument = false,
|
||
this.balance,
|
||
this.status,
|
||
this.totalDebit,
|
||
this.totalCredit,
|
||
this.personGroupId,
|
||
this.personGroupName,
|
||
this.foreignCurrencyCodes = const [],
|
||
});
|
||
|
||
factory Person.fromJson(Map<String, dynamic> json) {
|
||
final List<PersonType> types = (json['person_types'] as List?)
|
||
?.map((e) => PersonType.fromString(e.toString()))
|
||
.toList() ??
|
||
[];
|
||
return Person(
|
||
id: json['id'],
|
||
businessId: json['business_id'],
|
||
code: json['code'],
|
||
aliasName: json['alias_name'],
|
||
firstName: json['first_name'],
|
||
lastName: json['last_name'],
|
||
personTypes: types,
|
||
companyName: json['company_name'],
|
||
namePrefix: json['name_prefix'] as String?,
|
||
legalEntityType: (json['legal_entity_type'] as String?)?.trim().toLowerCase() == 'legal'
|
||
? 'legal'
|
||
: 'natural',
|
||
paymentId: json['payment_id'],
|
||
nationalId: json['national_id'],
|
||
registrationNumber: json['registration_number'],
|
||
economicId: json['economic_id'],
|
||
country: json['country'],
|
||
province: json['province'],
|
||
city: json['city'],
|
||
address: json['address'],
|
||
postalCode: json['postal_code'],
|
||
phone: json['phone'],
|
||
mobile: json['mobile'],
|
||
mobile2: json['mobile_2'] as String?,
|
||
mobile3: json['mobile_3'] as String?,
|
||
fax: json['fax'],
|
||
email: json['email'],
|
||
website: json['website'],
|
||
isActive: _fromJsonBool(json['is_active'], true),
|
||
createdAt: _parsePersonDateTime(json['created_at'] ?? json['created_at_raw']),
|
||
updatedAt: _parsePersonDateTime(json['updated_at'] ?? json['updated_at_raw']),
|
||
bankAccounts: (json['bank_accounts'] as List<dynamic>?)
|
||
?.map((ba) => PersonBankAccount.fromJson(ba))
|
||
.toList() ?? [],
|
||
socialContacts: (json['social_contacts'] as List<dynamic>?)
|
||
?.map((e) => PersonSocialContact.fromJson(e as Map<String, dynamic>))
|
||
.toList() ??
|
||
[],
|
||
shareCount: json['share_count'],
|
||
commissionSalePercent: (json['commission_sale_percent'] as num?)?.toDouble(),
|
||
commissionSalesReturnPercent: (json['commission_sales_return_percent'] as num?)?.toDouble(),
|
||
commissionSalesAmount: (json['commission_sales_amount'] as num?)?.toDouble(),
|
||
commissionSalesReturnAmount: (json['commission_sales_return_amount'] as num?)?.toDouble(),
|
||
commissionExcludeDiscounts: _fromJsonBool(json['commission_exclude_discounts'], false),
|
||
commissionExcludeAdditionsDeductions: _fromJsonBool(json['commission_exclude_additions_deductions'], false),
|
||
commissionPostInInvoiceDocument: _fromJsonBool(json['commission_post_in_invoice_document'], false),
|
||
balance: (json['balance'] as num?)?.toDouble(),
|
||
status: json['status'] as String?,
|
||
totalDebit: (json['total_debit'] as num?)?.toDouble(),
|
||
totalCredit: (json['total_credit'] as num?)?.toDouble(),
|
||
personGroupId: json['person_group_id'] as int?,
|
||
personGroupName: json['person_group_name'] as String?,
|
||
foreignCurrencyCodes: (json['foreign_currency_codes'] as List?)
|
||
?.map((e) => e.toString())
|
||
.where((e) => e.trim().isNotEmpty)
|
||
.toList() ??
|
||
const [],
|
||
);
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return {
|
||
'id': id,
|
||
'business_id': businessId,
|
||
'code': code,
|
||
'alias_name': aliasName,
|
||
'first_name': firstName,
|
||
'last_name': lastName,
|
||
'person_types': personTypes.map((t) => t.persianName).toList(),
|
||
'company_name': companyName,
|
||
'name_prefix': namePrefix,
|
||
'legal_entity_type': legalEntityType,
|
||
'payment_id': paymentId,
|
||
'national_id': nationalId,
|
||
'registration_number': registrationNumber,
|
||
'economic_id': economicId,
|
||
'country': country,
|
||
'province': province,
|
||
'city': city,
|
||
'address': address,
|
||
'postal_code': postalCode,
|
||
'phone': phone,
|
||
'mobile': mobile,
|
||
'mobile_2': mobile2,
|
||
'mobile_3': mobile3,
|
||
'fax': fax,
|
||
'email': email,
|
||
'website': website,
|
||
'is_active': isActive,
|
||
'created_at': createdAt.toIso8601String(),
|
||
'updated_at': updatedAt.toIso8601String(),
|
||
'bank_accounts': bankAccounts.map((ba) => ba.toJson()).toList(),
|
||
'social_contacts': socialContacts.map((s) => s.toJson()).toList(),
|
||
'share_count': shareCount,
|
||
'commission_sale_percent': commissionSalePercent,
|
||
'commission_sales_return_percent': commissionSalesReturnPercent,
|
||
'commission_sales_amount': commissionSalesAmount,
|
||
'commission_sales_return_amount': commissionSalesReturnAmount,
|
||
'commission_exclude_discounts': commissionExcludeDiscounts,
|
||
'commission_exclude_additions_deductions': commissionExcludeAdditionsDeductions,
|
||
'commission_post_in_invoice_document': commissionPostInInvoiceDocument,
|
||
'balance': balance,
|
||
'status': status,
|
||
'total_debit': totalDebit,
|
||
'total_credit': totalCredit,
|
||
'person_group_id': personGroupId,
|
||
'person_group_name': personGroupName,
|
||
'foreign_currency_codes': foreignCurrencyCodes,
|
||
};
|
||
}
|
||
|
||
Person copyWith({
|
||
int? id,
|
||
int? businessId,
|
||
String? aliasName,
|
||
String? firstName,
|
||
String? lastName,
|
||
List<PersonType>? personTypes,
|
||
String? companyName,
|
||
String? namePrefix,
|
||
String? legalEntityType,
|
||
String? paymentId,
|
||
String? nationalId,
|
||
String? registrationNumber,
|
||
String? economicId,
|
||
String? country,
|
||
String? province,
|
||
String? city,
|
||
String? address,
|
||
String? postalCode,
|
||
String? phone,
|
||
String? mobile,
|
||
String? mobile2,
|
||
String? mobile3,
|
||
String? fax,
|
||
String? email,
|
||
String? website,
|
||
bool? isActive,
|
||
DateTime? createdAt,
|
||
DateTime? updatedAt,
|
||
List<PersonBankAccount>? bankAccounts,
|
||
List<PersonSocialContact>? socialContacts,
|
||
int? personGroupId,
|
||
String? personGroupName,
|
||
List<String>? foreignCurrencyCodes,
|
||
}) {
|
||
return Person(
|
||
id: id ?? this.id,
|
||
businessId: businessId ?? this.businessId,
|
||
aliasName: aliasName ?? this.aliasName,
|
||
firstName: firstName ?? this.firstName,
|
||
lastName: lastName ?? this.lastName,
|
||
personTypes: personTypes ?? this.personTypes,
|
||
companyName: companyName ?? this.companyName,
|
||
namePrefix: namePrefix ?? this.namePrefix,
|
||
legalEntityType: legalEntityType ?? this.legalEntityType,
|
||
paymentId: paymentId ?? this.paymentId,
|
||
nationalId: nationalId ?? this.nationalId,
|
||
registrationNumber: registrationNumber ?? this.registrationNumber,
|
||
economicId: economicId ?? this.economicId,
|
||
country: country ?? this.country,
|
||
province: province ?? this.province,
|
||
city: city ?? this.city,
|
||
address: address ?? this.address,
|
||
postalCode: postalCode ?? this.postalCode,
|
||
phone: phone ?? this.phone,
|
||
mobile: mobile ?? this.mobile,
|
||
mobile2: mobile2 ?? this.mobile2,
|
||
mobile3: mobile3 ?? this.mobile3,
|
||
fax: fax ?? this.fax,
|
||
email: email ?? this.email,
|
||
website: website ?? this.website,
|
||
isActive: isActive ?? this.isActive,
|
||
createdAt: createdAt ?? this.createdAt,
|
||
updatedAt: updatedAt ?? this.updatedAt,
|
||
bankAccounts: bankAccounts ?? this.bankAccounts,
|
||
socialContacts: socialContacts ?? this.socialContacts,
|
||
shareCount: shareCount,
|
||
commissionSalePercent: commissionSalePercent,
|
||
commissionSalesReturnPercent: commissionSalesReturnPercent,
|
||
commissionSalesAmount: commissionSalesAmount,
|
||
commissionSalesReturnAmount: commissionSalesReturnAmount,
|
||
commissionExcludeDiscounts: commissionExcludeDiscounts,
|
||
commissionExcludeAdditionsDeductions: commissionExcludeAdditionsDeductions,
|
||
commissionPostInInvoiceDocument: commissionPostInInvoiceDocument,
|
||
balance: balance,
|
||
status: status,
|
||
totalDebit: totalDebit,
|
||
totalCredit: totalCredit,
|
||
personGroupId: personGroupId ?? this.personGroupId,
|
||
personGroupName: personGroupName ?? this.personGroupName,
|
||
foreignCurrencyCodes: foreignCurrencyCodes ?? this.foreignCurrencyCodes,
|
||
);
|
||
}
|
||
|
||
String get fullName {
|
||
if (firstName != null && lastName != null) {
|
||
return '$firstName $lastName';
|
||
} else if (firstName != null) {
|
||
return firstName!;
|
||
} else if (lastName != null) {
|
||
return lastName!;
|
||
}
|
||
return aliasName;
|
||
}
|
||
|
||
String get displayName {
|
||
return fullName.isNotEmpty ? fullName : aliasName;
|
||
}
|
||
}
|
||
|
||
/// مانده افتتاحیه شخص — فقط در سند تراز افتتاحیه ذخیره میشود.
|
||
class PersonOpeningBalanceInput {
|
||
final double amount;
|
||
final String balanceType; // debit | credit
|
||
final int? fiscalYearId;
|
||
final bool clear;
|
||
|
||
const PersonOpeningBalanceInput({
|
||
this.amount = 0,
|
||
required this.balanceType,
|
||
this.fiscalYearId,
|
||
this.clear = false,
|
||
});
|
||
|
||
Map<String, dynamic> toJson() => {
|
||
if (clear) 'clear': true,
|
||
if (!clear) 'amount': amount,
|
||
'balance_type': balanceType,
|
||
if (fiscalYearId != null) 'fiscal_year_id': fiscalYearId,
|
||
};
|
||
}
|
||
|
||
class PersonCreateRequest {
|
||
final String aliasName;
|
||
final int? code;
|
||
final String? firstName;
|
||
final String? lastName;
|
||
final List<PersonType> personTypes;
|
||
final String? companyName;
|
||
final String? namePrefix;
|
||
final String legalEntityType;
|
||
final String? paymentId;
|
||
final String? nationalId;
|
||
final String? registrationNumber;
|
||
final String? economicId;
|
||
final String? country;
|
||
final String? province;
|
||
final String? city;
|
||
final String? address;
|
||
final String? postalCode;
|
||
final String? phone;
|
||
final String? mobile;
|
||
final String? mobile2;
|
||
final String? mobile3;
|
||
final String? fax;
|
||
final String? email;
|
||
final String? website;
|
||
final List<PersonBankAccount> bankAccounts;
|
||
final List<PersonSocialContact> socialContacts;
|
||
final int? shareCount;
|
||
final double? commissionSalePercent;
|
||
final double? commissionSalesReturnPercent;
|
||
final double? commissionSalesAmount;
|
||
final double? commissionSalesReturnAmount;
|
||
final bool? commissionExcludeDiscounts;
|
||
final bool? commissionExcludeAdditionsDeductions;
|
||
final bool? commissionPostInInvoiceDocument;
|
||
final int? personGroupId;
|
||
final PersonOpeningBalanceInput? openingBalance;
|
||
|
||
PersonCreateRequest({
|
||
required this.aliasName,
|
||
this.code,
|
||
this.firstName,
|
||
this.lastName,
|
||
this.personTypes = const [],
|
||
this.companyName,
|
||
this.namePrefix,
|
||
this.legalEntityType = 'natural',
|
||
this.paymentId,
|
||
this.nationalId,
|
||
this.registrationNumber,
|
||
this.economicId,
|
||
this.country,
|
||
this.province,
|
||
this.city,
|
||
this.address,
|
||
this.postalCode,
|
||
this.phone,
|
||
this.mobile,
|
||
this.mobile2,
|
||
this.mobile3,
|
||
this.fax,
|
||
this.email,
|
||
this.website,
|
||
this.bankAccounts = const [],
|
||
this.socialContacts = const [],
|
||
this.shareCount,
|
||
this.commissionSalePercent,
|
||
this.commissionSalesReturnPercent,
|
||
this.commissionSalesAmount,
|
||
this.commissionSalesReturnAmount,
|
||
this.commissionExcludeDiscounts,
|
||
this.commissionExcludeAdditionsDeductions,
|
||
this.commissionPostInInvoiceDocument,
|
||
this.personGroupId,
|
||
this.openingBalance,
|
||
});
|
||
|
||
Map<String, dynamic> toJson() {
|
||
List<Map<String, dynamic>> validSocialRows() {
|
||
return socialContacts
|
||
.where((s) {
|
||
final v = s.value.trim();
|
||
if (v.isEmpty) return false;
|
||
if (s.platformKey == 'other' && (s.customLabel == null || s.customLabel!.trim().isEmpty)) {
|
||
return false;
|
||
}
|
||
return s.platformKey.isNotEmpty;
|
||
})
|
||
.map((s) => s.toApiWrite())
|
||
.toList();
|
||
}
|
||
|
||
final sc = validSocialRows();
|
||
return {
|
||
'alias_name': aliasName,
|
||
if (code != null) 'code': code,
|
||
'first_name': firstName,
|
||
'last_name': lastName,
|
||
if (personTypes.isNotEmpty) 'person_types': personTypes.map((t) => t.persianName).toList(),
|
||
'company_name': companyName,
|
||
if (namePrefix != null && namePrefix!.trim().isNotEmpty) 'name_prefix': namePrefix!.trim(),
|
||
'legal_entity_type': legalEntityType,
|
||
'payment_id': paymentId,
|
||
'national_id': nationalId,
|
||
'registration_number': registrationNumber,
|
||
'economic_id': economicId,
|
||
'country': country,
|
||
'province': province,
|
||
'city': city,
|
||
'address': address,
|
||
'postal_code': postalCode,
|
||
'phone': phone,
|
||
'mobile': mobile,
|
||
'mobile_2': mobile2,
|
||
'mobile_3': mobile3,
|
||
'fax': fax,
|
||
'email': email,
|
||
'website': website,
|
||
// Only send fields expected by backend for create
|
||
'bank_accounts': bankAccounts
|
||
.where((ba) => (ba.bankName).trim().isNotEmpty)
|
||
.map((ba) => {
|
||
'bank_name': ba.bankName,
|
||
'account_number': ba.accountNumber,
|
||
'card_number': ba.cardNumber,
|
||
'sheba_number': ba.shebaNumber,
|
||
})
|
||
.toList(),
|
||
'social_contacts': sc,
|
||
if (shareCount != null) 'share_count': shareCount,
|
||
if (commissionSalePercent != null) 'commission_sale_percent': commissionSalePercent,
|
||
if (commissionSalesReturnPercent != null) 'commission_sales_return_percent': commissionSalesReturnPercent,
|
||
if (commissionSalesAmount != null) 'commission_sales_amount': commissionSalesAmount,
|
||
if (commissionSalesReturnAmount != null) 'commission_sales_return_amount': commissionSalesReturnAmount,
|
||
if (commissionExcludeDiscounts != null) 'commission_exclude_discounts': commissionExcludeDiscounts,
|
||
if (commissionExcludeAdditionsDeductions != null) 'commission_exclude_additions_deductions': commissionExcludeAdditionsDeductions,
|
||
if (commissionPostInInvoiceDocument != null) 'commission_post_in_invoice_document': commissionPostInInvoiceDocument,
|
||
if (personGroupId != null) 'person_group_id': personGroupId,
|
||
if (openingBalance != null) 'opening_balance': openingBalance!.toJson(),
|
||
};
|
||
}
|
||
}
|
||
|
||
class PersonUpdateRequest {
|
||
final int? code;
|
||
final String? aliasName;
|
||
final String? firstName;
|
||
final String? lastName;
|
||
final List<PersonType>? personTypes;
|
||
final String? companyName;
|
||
final String? namePrefix;
|
||
final String? legalEntityType;
|
||
final String? paymentId;
|
||
final String? nationalId;
|
||
final String? registrationNumber;
|
||
final String? economicId;
|
||
final String? country;
|
||
final String? province;
|
||
final String? city;
|
||
final String? address;
|
||
final String? postalCode;
|
||
final String? phone;
|
||
final String? mobile;
|
||
final String? mobile2;
|
||
final String? mobile3;
|
||
final String? fax;
|
||
final String? email;
|
||
final String? website;
|
||
final bool? isActive;
|
||
final int? shareCount;
|
||
final double? commissionSalePercent;
|
||
final double? commissionSalesReturnPercent;
|
||
final double? commissionSalesAmount;
|
||
final double? commissionSalesReturnAmount;
|
||
final bool? commissionExcludeDiscounts;
|
||
final bool? commissionExcludeAdditionsDeductions;
|
||
final bool? commissionPostInInvoiceDocument;
|
||
final int? personGroupId;
|
||
/// اگر ارسال شود، لیست راههای ارتباطی بهطور کامل جایگزین میشود.
|
||
final List<Map<String, dynamic>>? socialContacts;
|
||
/// اگر ارسال شود، لیست حسابهای بانکی شخص جایگزین کامل میشود.
|
||
final List<PersonBankAccount>? bankAccounts;
|
||
final PersonOpeningBalanceInput? openingBalance;
|
||
|
||
PersonUpdateRequest({
|
||
this.code,
|
||
this.aliasName,
|
||
this.firstName,
|
||
this.lastName,
|
||
this.personTypes,
|
||
this.companyName,
|
||
this.namePrefix,
|
||
this.legalEntityType,
|
||
this.paymentId,
|
||
this.nationalId,
|
||
this.registrationNumber,
|
||
this.economicId,
|
||
this.country,
|
||
this.province,
|
||
this.city,
|
||
this.address,
|
||
this.postalCode,
|
||
this.phone,
|
||
this.mobile,
|
||
this.mobile2,
|
||
this.mobile3,
|
||
this.fax,
|
||
this.email,
|
||
this.website,
|
||
this.isActive,
|
||
this.shareCount,
|
||
this.commissionSalePercent,
|
||
this.commissionSalesReturnPercent,
|
||
this.commissionSalesAmount,
|
||
this.commissionSalesReturnAmount,
|
||
this.commissionExcludeDiscounts,
|
||
this.commissionExcludeAdditionsDeductions,
|
||
this.commissionPostInInvoiceDocument,
|
||
this.personGroupId,
|
||
this.socialContacts,
|
||
this.bankAccounts,
|
||
this.openingBalance,
|
||
});
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return <String, dynamic>{
|
||
'code': code,
|
||
if (aliasName != null) 'alias_name': aliasName,
|
||
'first_name': firstName,
|
||
'last_name': lastName,
|
||
if (personTypes != null)
|
||
'person_types': personTypes!.map((t) => t.persianName).toList(),
|
||
'company_name': companyName,
|
||
'name_prefix': () {
|
||
final p = namePrefix;
|
||
if (p == null) return null;
|
||
final trimmed = p.trim();
|
||
return trimmed.isEmpty ? null : trimmed;
|
||
}(),
|
||
'legal_entity_type': legalEntityType,
|
||
'payment_id': paymentId,
|
||
'national_id': nationalId,
|
||
'registration_number': registrationNumber,
|
||
'economic_id': economicId,
|
||
'country': country,
|
||
'province': province,
|
||
'city': city,
|
||
'address': address,
|
||
'postal_code': postalCode,
|
||
'phone': phone,
|
||
'mobile': mobile,
|
||
'mobile_2': mobile2,
|
||
'mobile_3': mobile3,
|
||
'fax': fax,
|
||
'email': email,
|
||
'website': website,
|
||
if (isActive != null) 'is_active': isActive,
|
||
'share_count': shareCount,
|
||
'commission_sale_percent': commissionSalePercent,
|
||
'commission_sales_return_percent': commissionSalesReturnPercent,
|
||
'commission_sales_amount': commissionSalesAmount,
|
||
'commission_sales_return_amount': commissionSalesReturnAmount,
|
||
if (commissionExcludeDiscounts != null)
|
||
'commission_exclude_discounts': commissionExcludeDiscounts,
|
||
if (commissionExcludeAdditionsDeductions != null)
|
||
'commission_exclude_additions_deductions': commissionExcludeAdditionsDeductions,
|
||
if (commissionPostInInvoiceDocument != null)
|
||
'commission_post_in_invoice_document': commissionPostInInvoiceDocument,
|
||
'person_group_id': personGroupId,
|
||
if (socialContacts != null) 'social_contacts': socialContacts,
|
||
if (bankAccounts != null)
|
||
'bank_accounts': bankAccounts!
|
||
.where((ba) => ba.bankName.trim().isNotEmpty)
|
||
.map((ba) => {
|
||
'bank_name': ba.bankName,
|
||
'account_number': ba.accountNumber,
|
||
'card_number': ba.cardNumber,
|
||
'sheba_number': ba.shebaNumber,
|
||
})
|
||
.toList(),
|
||
if (openingBalance != null) 'opening_balance': openingBalance!.toJson(),
|
||
};
|
||
}
|
||
}
|