2025-10-27 22:17:45 +03:30
|
|
|
import 'package:dio/dio.dart';
|
2025-10-21 11:30:01 +03:30
|
|
|
import 'package:hesabix_ui/core/api_client.dart';
|
2025-10-27 22:17:45 +03:30
|
|
|
import 'package:hesabix_ui/models/expense_income_document.dart';
|
2025-10-21 11:30:01 +03:30
|
|
|
|
2025-10-27 22:17:45 +03:30
|
|
|
/// سرویس CRUD اسناد هزینه/درآمد
|
2025-10-21 11:30:01 +03:30
|
|
|
class ExpenseIncomeService {
|
2025-10-27 22:17:45 +03:30
|
|
|
final ApiClient _apiClient;
|
2025-10-21 11:30:01 +03:30
|
|
|
|
2025-10-27 22:17:45 +03:30
|
|
|
ExpenseIncomeService(this._apiClient);
|
|
|
|
|
|
|
|
|
|
/// ایجاد سند هزینه/درآمد جدید
|
|
|
|
|
Future<ExpenseIncomeDocument> create({
|
2025-10-21 11:30:01 +03:30
|
|
|
required int businessId,
|
2025-10-27 22:17:45 +03:30
|
|
|
required String documentType,
|
2025-10-21 11:30:01 +03:30
|
|
|
required DateTime documentDate,
|
|
|
|
|
required int currencyId,
|
2025-10-27 22:17:45 +03:30
|
|
|
required List<ItemLineData> itemLines,
|
|
|
|
|
required List<CounterpartyLineData> counterpartyLines,
|
2025-10-21 11:30:01 +03:30
|
|
|
String? description,
|
2025-10-27 22:17:45 +03:30
|
|
|
Map<String, dynamic>? extraInfo,
|
2025-10-21 11:30:01 +03:30
|
|
|
}) async {
|
2025-10-27 22:17:45 +03:30
|
|
|
try {
|
|
|
|
|
// تبدیل itemLines به فرمت API
|
|
|
|
|
final itemLinesData = itemLines.map((line) => {
|
|
|
|
|
'account_id': line.accountId,
|
|
|
|
|
'amount': line.amount,
|
|
|
|
|
if (line.description != null && line.description!.isNotEmpty)
|
|
|
|
|
'description': line.description,
|
|
|
|
|
}).toList();
|
|
|
|
|
|
|
|
|
|
// تبدیل counterpartyLines به فرمت API
|
|
|
|
|
final counterpartyLinesData = counterpartyLines.map((line) {
|
|
|
|
|
final data = {
|
|
|
|
|
'transaction_type': line.transactionType.value,
|
|
|
|
|
'amount': line.amount,
|
|
|
|
|
'transaction_date': line.transactionDate.toIso8601String(),
|
|
|
|
|
if (line.description != null && line.description!.isNotEmpty)
|
|
|
|
|
'description': line.description,
|
|
|
|
|
if (line.commission != null && line.commission! > 0)
|
|
|
|
|
'commission': line.commission,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// اضافه کردن فیلدهای خاص بر اساس نوع تراکنش
|
|
|
|
|
switch (line.transactionType) {
|
|
|
|
|
case TransactionType.bank:
|
|
|
|
|
if (line.bankAccountId != null) {
|
|
|
|
|
data['bank_account_id'] = line.bankAccountId;
|
|
|
|
|
data['bank_account_name'] = line.bankAccountName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.cashRegister:
|
|
|
|
|
if (line.cashRegisterId != null) {
|
|
|
|
|
data['cash_register_id'] = line.cashRegisterId;
|
|
|
|
|
data['cash_register_name'] = line.cashRegisterName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.pettyCash:
|
|
|
|
|
if (line.pettyCashId != null) {
|
|
|
|
|
data['petty_cash_id'] = line.pettyCashId;
|
|
|
|
|
data['petty_cash_name'] = line.pettyCashName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.check:
|
|
|
|
|
if (line.checkId != null) {
|
|
|
|
|
data['check_id'] = line.checkId;
|
|
|
|
|
data['check_number'] = line.checkNumber;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.person:
|
|
|
|
|
if (line.personId != null) {
|
|
|
|
|
data['person_id'] = line.personId;
|
|
|
|
|
data['person_name'] = line.personName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}).toList();
|
|
|
|
|
|
|
|
|
|
final requestData = {
|
|
|
|
|
'document_type': documentType,
|
|
|
|
|
'document_date': documentDate.toIso8601String(),
|
|
|
|
|
'currency_id': currencyId,
|
|
|
|
|
if (description != null && description.isNotEmpty) 'description': description,
|
|
|
|
|
'item_lines': itemLinesData,
|
|
|
|
|
'counterparty_lines': counterpartyLinesData,
|
|
|
|
|
if (extraInfo != null) 'extra_info': extraInfo,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
final response = await _apiClient.post(
|
|
|
|
|
'/businesses/$businessId/expense-income/create',
|
|
|
|
|
data: requestData,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
final data = response.data['data'];
|
|
|
|
|
return ExpenseIncomeDocument.fromJson(data);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw _handleError(e);
|
|
|
|
|
}
|
2025-10-21 11:30:01 +03:30
|
|
|
}
|
|
|
|
|
|
2025-10-27 22:17:45 +03:30
|
|
|
/// ویرایش سند هزینه/درآمد
|
|
|
|
|
Future<ExpenseIncomeDocument> update({
|
|
|
|
|
required int documentId,
|
|
|
|
|
required DateTime documentDate,
|
|
|
|
|
required int currencyId,
|
|
|
|
|
required List<ItemLineData> itemLines,
|
|
|
|
|
required List<CounterpartyLineData> counterpartyLines,
|
|
|
|
|
String? description,
|
|
|
|
|
Map<String, dynamic>? extraInfo,
|
2025-10-21 11:30:01 +03:30
|
|
|
}) async {
|
2025-10-27 22:17:45 +03:30
|
|
|
try {
|
|
|
|
|
// تبدیل itemLines به فرمت API
|
|
|
|
|
final itemLinesData = itemLines.map((line) => {
|
|
|
|
|
'account_id': line.accountId,
|
|
|
|
|
'amount': line.amount,
|
|
|
|
|
if (line.description != null && line.description!.isNotEmpty)
|
|
|
|
|
'description': line.description,
|
|
|
|
|
}).toList();
|
|
|
|
|
|
|
|
|
|
// تبدیل counterpartyLines به فرمت API
|
|
|
|
|
final counterpartyLinesData = counterpartyLines.map((line) {
|
|
|
|
|
final data = {
|
|
|
|
|
'transaction_type': line.transactionType.value,
|
|
|
|
|
'amount': line.amount,
|
|
|
|
|
'transaction_date': line.transactionDate.toIso8601String(),
|
|
|
|
|
if (line.description != null && line.description!.isNotEmpty)
|
|
|
|
|
'description': line.description,
|
|
|
|
|
if (line.commission != null && line.commission! > 0)
|
|
|
|
|
'commission': line.commission,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// اضافه کردن فیلدهای خاص بر اساس نوع تراکنش
|
|
|
|
|
switch (line.transactionType) {
|
|
|
|
|
case TransactionType.bank:
|
|
|
|
|
if (line.bankAccountId != null) {
|
|
|
|
|
data['bank_account_id'] = line.bankAccountId;
|
|
|
|
|
data['bank_account_name'] = line.bankAccountName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.cashRegister:
|
|
|
|
|
if (line.cashRegisterId != null) {
|
|
|
|
|
data['cash_register_id'] = line.cashRegisterId;
|
|
|
|
|
data['cash_register_name'] = line.cashRegisterName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.pettyCash:
|
|
|
|
|
if (line.pettyCashId != null) {
|
|
|
|
|
data['petty_cash_id'] = line.pettyCashId;
|
|
|
|
|
data['petty_cash_name'] = line.pettyCashName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.check:
|
|
|
|
|
if (line.checkId != null) {
|
|
|
|
|
data['check_id'] = line.checkId;
|
|
|
|
|
data['check_number'] = line.checkNumber;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TransactionType.person:
|
|
|
|
|
if (line.personId != null) {
|
|
|
|
|
data['person_id'] = line.personId;
|
|
|
|
|
data['person_name'] = line.personName;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}).toList();
|
|
|
|
|
|
|
|
|
|
final requestData = {
|
|
|
|
|
'document_date': documentDate.toIso8601String(),
|
|
|
|
|
'currency_id': currencyId,
|
|
|
|
|
if (description != null && description.isNotEmpty) 'description': description,
|
|
|
|
|
'item_lines': itemLinesData,
|
|
|
|
|
'counterparty_lines': counterpartyLinesData,
|
|
|
|
|
if (extraInfo != null) 'extra_info': extraInfo,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
final response = await _apiClient.put(
|
|
|
|
|
'/expense-income/$documentId',
|
|
|
|
|
data: requestData,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
final data = response.data['data'];
|
|
|
|
|
return ExpenseIncomeDocument.fromJson(data);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw _handleError(e);
|
|
|
|
|
}
|
2025-10-21 11:30:01 +03:30
|
|
|
}
|
|
|
|
|
|
2025-10-27 22:17:45 +03:30
|
|
|
/// دریافت فایل PDF یک سند
|
|
|
|
|
Future<List<int>> generatePdf(int documentId) async {
|
|
|
|
|
try {
|
|
|
|
|
return await _apiClient.downloadPdf('/expense-income/$documentId/pdf');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw _handleError(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-21 11:30:01 +03:30
|
|
|
|
2025-10-27 22:17:45 +03:30
|
|
|
Exception _handleError(dynamic error) {
|
|
|
|
|
if (error is DioException) {
|
|
|
|
|
final response = error.response;
|
|
|
|
|
if (response != null) {
|
|
|
|
|
final data = response.data;
|
|
|
|
|
if (data is Map<String, dynamic>) {
|
|
|
|
|
final message = data['message'] ?? data['detail'] ?? error.message;
|
|
|
|
|
return Exception(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Exception(error.message ?? 'خطا در ارتباط با سرور');
|
|
|
|
|
}
|
|
|
|
|
return Exception(error.toString());
|
|
|
|
|
}
|
|
|
|
|
}
|