Added expense/income to je list and added load as draft check

This commit is contained in:
Ali 2026-01-20 20:20:44 +04:00
parent 0ef93e4b36
commit 9a3c705b4e
2 changed files with 70 additions and 32 deletions

View File

@ -798,7 +798,7 @@ VATETaxes.errors = {
},
// Показать сводку результатов
showSummary: function(totalOperations, processedCount, errorsCount) {
showSummary: function(totalOperations, processedCount, errorsCount, createAsDraft = 0) {
let title = '';
let indicator = '';
let message = '';
@ -807,6 +807,9 @@ VATETaxes.errors = {
title = __('Import Completed Successfully');
indicator = 'green';
message = __('All ') + totalOperations + __(' VAT operations were imported successfully.');
if (createAsDraft) {
message += ' ' + __('(created as Draft)');
}
frappe.msgprint({
title: title,
@ -889,6 +892,13 @@ VATETaxes.import = {
options: 'Company',
reqd: true,
default: frappe.defaults.get_user_default('Company')
},
{
fieldname: 'create_as_draft',
fieldtype: 'Check',
label: __('Create as Draft'),
default: 0,
description: __('If checked, Journal Entries will be created in Draft status instead of being submitted automatically')
}
],
primary_action_label: __('Search'),
@ -921,9 +931,10 @@ VATETaxes.import = {
const fromDate = values.creationDateFrom;
const toDate = values.creationDateTo;
const createAsDraft = values.create_as_draft || 0;
d.hide();
VATETaxes.import.loadOperations(fromDate, toDate, values.company);
VATETaxes.import.loadOperations(fromDate, toDate, values.company, [], 0, createAsDraft);
}
});
@ -932,7 +943,7 @@ VATETaxes.import = {
},
// Загрузить операции с фильтрацией дубликатов
loadOperations: function(fromDate, toDate, company, accumulatedOperations = [], offset = 0) {
loadOperations: function(fromDate, toDate, company, accumulatedOperations = [], offset = 0, createAsDraft = 0) {
VATETaxes.cancelLoading = false;
$(document).off('progress-cancel.vat_import');
@ -979,7 +990,7 @@ VATETaxes.import = {
if (hasMore && currentOperations.length > 0) {
const newOffset = offset + currentOperations.length;
VATETaxes.import.loadOperations(fromDate, toDate, company, allOperations, newOffset);
VATETaxes.import.loadOperations(fromDate, toDate, company, allOperations, newOffset, createAsDraft);
} else {
if (allOperations.length > 0) {
frappe.show_alert({
@ -987,7 +998,7 @@ VATETaxes.import = {
indicator: 'blue'
}, 3);
VATETaxes.import.filterDuplicates(allOperations, mainToken, company);
VATETaxes.import.filterDuplicates(allOperations, mainToken, company, createAsDraft);
} else {
frappe.msgprint({
title: __('Information'),
@ -1001,7 +1012,7 @@ VATETaxes.import = {
__('Your E-Taxes session has expired. Would you like to authenticate now?'),
function() {
VATETaxes.auth.startProcess(function() {
VATETaxes.import.loadOperations(fromDate, toDate, company);
VATETaxes.import.loadOperations(fromDate, toDate, company, [], 0, createAsDraft);
});
},
function() {
@ -1039,7 +1050,7 @@ VATETaxes.import = {
},
// Фильтрация дубликатов
filterDuplicates: function(allOperations, token, company) {
filterDuplicates: function(allOperations, token, company, createAsDraft = 0) {
frappe.call({
method: 'frappe.client.get_list',
args: {
@ -1074,7 +1085,7 @@ VATETaxes.import = {
}
if (filteredOperations.length > 0) {
VATETaxes.import.showOperationSelection(filteredOperations, token, company);
VATETaxes.import.showOperationSelection(filteredOperations, token, company, createAsDraft);
} else {
frappe.msgprint({
title: __('Information'),
@ -1094,7 +1105,7 @@ VATETaxes.import = {
},
error: function(err) {
console.error("Error fetching VAT operations:", err);
VATETaxes.import.showOperationSelection(allOperations, token, company);
VATETaxes.import.showOperationSelection(allOperations, token, company, createAsDraft);
frappe.show_alert({
message: __('Failed to check for duplicates. Showing all operations.'),
indicator: 'orange'
@ -1104,7 +1115,7 @@ VATETaxes.import = {
},
// Показать диалог выбора операций
showOperationSelection: function(operations, token, company) {
showOperationSelection: function(operations, token, company, createAsDraft = 0) {
let operationTable = '<div style="max-height: 500px; overflow-y: auto;"><table class="table table-bordered vat-operations-table" style="width: 100%; table-layout: fixed;">';
operationTable += '<thead><tr>' +
'<th style="width: 3%;"><input type="checkbox" class="select-all-operations"></th>' +
@ -1198,7 +1209,7 @@ VATETaxes.import = {
d.hide();
VATETaxes.cancelLoading = false;
VATETaxes.import.loadSelectedOperations(selectedOperations, token, company);
VATETaxes.import.loadSelectedOperations(selectedOperations, token, company, 0, 0, createAsDraft);
},
secondary_action_label: __('Cancel'),
secondary_action: function() {
@ -1226,7 +1237,7 @@ VATETaxes.import = {
},
// Загрузка выбранных операций
loadSelectedOperations: function(operations, token, company, processedCount = 0, currentIndex = 0) {
loadSelectedOperations: function(operations, token, company, processedCount = 0, currentIndex = 0, createAsDraft = 0) {
if (processedCount === 0 && currentIndex === 0) {
VATETaxes.loadingErrors = [];
window.totalOperationsToProcess = operations.length;
@ -1245,7 +1256,7 @@ VATETaxes.import = {
// Дополнительная микрозадержка для гарантии
setTimeout(function() {
VATETaxes.errors.showSummary(totalOperations, processedCount, errorsCount);
VATETaxes.errors.showSummary(totalOperations, processedCount, errorsCount, createAsDraft);
window.totalOperationsToProcess = null;
if (cur_list) {
@ -1260,7 +1271,7 @@ VATETaxes.import = {
if (VATETaxes.cancelLoading) {
operations = [];
frappe.hide_progress();
VATETaxes.import.loadSelectedOperations(operations, token, company, processedCount, currentIndex);
VATETaxes.import.loadSelectedOperations(operations, token, company, processedCount, currentIndex, createAsDraft);
return;
}
@ -1287,11 +1298,12 @@ VATETaxes.import = {
method: 'invoice_az.vat_api.import_vat_operations',
args: {
'operations_data': JSON.stringify([operation]),
'company': company
'company': company,
'create_as_draft': createAsDraft
},
callback: function(r) {
VATETaxes.import._handleImportResult(r, operation,
operations, token, company, processedCount, currentIndex, isLastOperation);
operations, token, company, processedCount, currentIndex, isLastOperation, createAsDraft);
},
error: function(xhr, status, error) {
const errorMessage = 'Network error during import: ' + (error || 'Unknown network error');
@ -1300,13 +1312,13 @@ VATETaxes.import = {
xhr_response: xhr.responseText
});
VATETaxes.import._continueOrFinish(operations, token, company, processedCount, currentIndex, isLastOperation);
VATETaxes.import._continueOrFinish(operations, token, company, processedCount, currentIndex, isLastOperation, createAsDraft);
}
});
},
// Обработка результата импорта
_handleImportResult: function(importR, operation, operations, token, company, processedCount, currentIndex, isLastOperation) {
_handleImportResult: function(importR, operation, operations, token, company, processedCount, currentIndex, isLastOperation, createAsDraft = 0) {
if (importR.message && importR.message.success) {
if (importR.message.imported > 0) {
// НЕ показываем индивидуальные алерты во время массового импорта
@ -1318,19 +1330,19 @@ VATETaxes.import = {
VATETaxes.errors.add(operation, error.error_type || 'Import Error', error.message, error);
}
VATETaxes.import._continueOrFinish(operations, token, company, processedCount, currentIndex, isLastOperation);
VATETaxes.import._continueOrFinish(operations, token, company, processedCount, currentIndex, isLastOperation, createAsDraft);
} else {
const errorMessage = importR.message ? importR.message.message : 'Unknown import error';
VATETaxes.errors.add(operation, 'Import Error', errorMessage, {
server_response: importR.message
});
VATETaxes.import._continueOrFinish(operations, token, company, processedCount, currentIndex, isLastOperation);
VATETaxes.import._continueOrFinish(operations, token, company, processedCount, currentIndex, isLastOperation, createAsDraft);
}
},
// Продолжить или завершить обработку
_continueOrFinish: function(operations, token, company, processedCount, currentIndex, isLastOperation) {
_continueOrFinish: function(operations, token, company, processedCount, currentIndex, isLastOperation, createAsDraft = 0) {
if (isLastOperation) {
$(document).off('progress-cancel.loading_operations');
@ -1344,7 +1356,7 @@ VATETaxes.import = {
// Дополнительная микрозадержка для гарантии
setTimeout(function() {
VATETaxes.errors.showSummary(totalToProcess, processedCount, errorsCount);
VATETaxes.errors.showSummary(totalToProcess, processedCount, errorsCount, createAsDraft);
if (cur_list) {
cur_list.refresh();
@ -1360,7 +1372,7 @@ VATETaxes.import = {
frappe.hide_progress();
}
VATETaxes.import.loadSelectedOperations(operations, token, company, processedCount, currentIndex);
VATETaxes.import.loadSelectedOperations(operations, token, company, processedCount, currentIndex, createAsDraft);
}, VATETaxes.PROGRESS_UPDATE_DELAY);
}
};
@ -1369,7 +1381,7 @@ VATETaxes.import = {
// Journal Entry List
frappe.listview_settings['Journal Entry'] = {
add_fields: ['posting_date', 'voucher_type', 'total_debit', 'total_credit', 'docstatus'],
add_fields: ['posting_date', 'voucher_type', 'total_debit', 'total_credit', 'docstatus', 'expense_income_type'],
get_indicator: function(doc) {
if (doc.docstatus == 1) {
@ -1381,6 +1393,17 @@ frappe.listview_settings['Journal Entry'] = {
}
},
formatters: {
expense_income_type: function(value) {
if (value === 'Income') {
return '<span class="indicator-pill green">' + value + '</span>';
} else if (value === 'Expense') {
return '<span class="indicator-pill red">' + value + '</span>';
}
return value || '';
}
},
onload: function(listview) {
listview.page.add_menu_item(__('Import VAT Operations from E-Taxes'), function() {
VATETaxes.import.showDialog();

View File

@ -349,13 +349,14 @@ def get_accounts_for_operation_type(operation_type, company, expense_income_type
return (None, None)
def create_journal_entry_from_vat_operation(operation_data, company):
def create_journal_entry_from_vat_operation(operation_data, company, create_as_draft=0):
"""
Создание Journal Entry из VAT операции
Args:
operation_data (dict): Данные операции из API
company (str): Название компании
create_as_draft (int): 1 для создания в Draft, 0 для auto-submit
Returns:
dict: Результат создания с именем JE или ошибкой
@ -453,6 +454,7 @@ def create_journal_entry_from_vat_operation(operation_data, company):
je.company = company
je.voucher_type = 'Journal Entry'
je.user_remark = f"VAT Operation: {operation_data.get('explanation', '')}"
je.expense_income_type = expense_income_type
# Строка 1: Дебет
debit_entry = {
@ -482,24 +484,30 @@ def create_journal_entry_from_vat_operation(operation_data, company):
je.append('accounts', credit_entry)
# Сохраняем и submit
# Сохраняем и submit (если не draft режим)
je.insert()
je.submit()
if not create_as_draft:
je.submit()
else:
frappe.logger().info(f"Journal Entry {je.name} created as Draft (not submitted)")
frappe.db.commit()
return {
'success': True,
'journal_entry': je.name,
'posting_date': str(posting_date)
'posting_date': str(posting_date),
'status': 'Draft' if create_as_draft else 'Submitted'
}
except Exception as e:
frappe.log_error(f"Error creating JE from VAT operation: {str(e)}\n{frappe.get_traceback()}",
error_context = "creating draft" if create_as_draft else "creating and submitting"
frappe.log_error(f"Error {error_context} JE from VAT operation: {str(e)}\n{frappe.get_traceback()}",
"VAT JE Creation Error")
return {
'success': False,
'message': f'Error creating Journal Entry: {str(e)}'
'message': f'Error {error_context} Journal Entry: {str(e)}'
}
@ -579,13 +587,14 @@ def create_vat_operation_record(operation_data, journal_entry_name=None, status=
@frappe.whitelist()
def import_vat_operations(operations_data, company=None):
def import_vat_operations(operations_data, company=None, create_as_draft=0):
"""
Импорт VAT операций и создание Journal Entries
Args:
operations_data (str/list): JSON строка или список операций
company (str): Название компании (опционально)
create_as_draft (int): 1 для создания в Draft, 0 для auto-submit
Returns:
dict: Результат импорта с статистикой
@ -597,6 +606,12 @@ def import_vat_operations(operations_data, company=None):
if isinstance(operations_data, str):
operations_data = json.loads(operations_data)
# Обработка create_as_draft параметра
try:
create_as_draft = int(create_as_draft)
except (ValueError, TypeError):
create_as_draft = 0
# Получаем компанию
if not company:
company = frappe.defaults.get_user_default('Company')
@ -628,7 +643,7 @@ def import_vat_operations(operations_data, company=None):
continue
# Создаем Journal Entry
je_result = create_journal_entry_from_vat_operation(operation, company)
je_result = create_journal_entry_from_vat_operation(operation, company, create_as_draft)
if je_result.get('success'):
# Создаем запись отслеживания