Added bulk edit, all english, and every doctype of invoice az is read-only

This commit is contained in:
Ali 2025-05-16 12:30:56 +04:00
parent 905cfeac0c
commit 994917ffbc
3 changed files with 680 additions and 527 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
frappe.ui.form.on('Purchase Order', {
refresh: function(frm) {
// Проверяем, установлена ли галочка is_taxes_doc
@ -7,7 +6,7 @@ frappe.ui.form.on('Purchase Order', {
frm.set_read_only(true);
// Альтернативно, можно добавить визуальное предупреждение
frm.page.set_indicator(__('Налоговый документ'), 'blue');
frm.page.set_indicator(__('Tax Document'), 'blue');
}
},
@ -42,7 +41,7 @@ frappe.listview_settings['Purchase Order'] = {
// Добавляем кнопку в верхнее меню списка
onload: function(listview) {
// Добавляем кнопку для импорта из E-Taxes
listview.page.add_menu_item(__('Импорт из E-Taxes'), function() {
listview.page.add_menu_item(__('Import from E-Taxes'), function() {
show_etaxes_import_dialog_for_list();
});
}
@ -58,7 +57,7 @@ var cancel_loading = false;
function show_loading_dialog(message, show_progress = false) {
if (!loading_dialog) {
loading_dialog = new frappe.ui.Dialog({
title: __('Загрузка'),
title: __('Loading'),
fields: [
{
fieldname: 'status_html',
@ -100,8 +99,8 @@ function show_loading_dialog(message, show_progress = false) {
}
</style>
<div class="mt-3" id="status_message">
<h4>${message || __('Пожалуйста, подождите...')}</h4>
<p>${__('Операция может занять некоторое время')}</p>
<h4>${message || __('Please wait...')}</h4>
<p>${__('The operation may take some time')}</p>
</div>
<!-- Блок для отображения кода верификации -->
<div id="verification_code_container" style="display:none; margin-top: 15px;">
@ -121,18 +120,18 @@ function show_loading_dialog(message, show_progress = false) {
`
}
],
primary_action_label: __('Отмена'),
primary_action_label: __('Cancel'),
primary_action: function() {
cancel_loading = true;
loading_dialog.$wrapper.find('.primary-action').prop('disabled', true);
loading_dialog.$wrapper.find('.primary-action').html(__('Отмена в процессе...'));
$('#status_message p').text(__('Отмена загрузки. Уже загруженные инвойсы сохранятся.'));
loading_dialog.$wrapper.find('.primary-action').html(__('Cancelling...'));
$('#status_message p').text(__('Cancelling the loading. Already loaded invoices will be saved.'));
}
});
} else {
// Обновляем сообщение
$('#status_message h4').text(message || __('Пожалуйста, подождите...'));
$('#status_message p').text(__('Операция может занять некоторое время'));
$('#status_message h4').text(message || __('Please wait...'));
$('#status_message p').text(__('The operation may take some time'));
}
// Показываем или скрываем прогресс-бар
@ -156,7 +155,7 @@ function update_progress(current, total) {
if (loading_dialog) {
var percent = Math.round((current / total) * 100);
$('#progress_bar_fill').css('width', percent + '%');
$('#progress_text').text(current + ' из ' + total + ' (' + percent + '%)');
$('#progress_text').text(current + ' of ' + total + ' (' + percent + '%)');
}
}
@ -667,34 +666,34 @@ function show_etaxes_import_dialog(frm) {
check_token_before_api_call(function() {
// Создаем диалог для выбора периода и склада
var d = new frappe.ui.Dialog({
title: __('Импорт счета-фактуры из E-Taxes'),
title: __('Import invoice from E-Taxes'),
fields: [
{
fieldname: 'date_range_section',
fieldtype: 'Section Break',
label: __('Период')
label: __('Period')
},
{
fieldname: 'creationDateFrom',
fieldtype: 'Date',
label: __('Дата с'),
label: __('Date from'),
default: moment().subtract(1, 'month').format('YYYY-MM-DD')
},
{
fieldname: 'creationDateTo',
fieldtype: 'Date',
label: __('Дата по'),
label: __('Date to'),
default: moment().format('YYYY-MM-DD')
},
{
fieldname: 'settings_section',
fieldtype: 'Section Break',
label: __('Настройки')
label: __('Settings')
},
{
fieldname: 'warehouse',
fieldtype: 'Link',
label: __('Склад по умолчанию'),
label: __('Default warehouse'),
options: 'Warehouse',
reqd: true,
get_query: function() {
@ -707,7 +706,7 @@ function show_etaxes_import_dialog(frm) {
}
}
],
primary_action_label: __('Поиск'),
primary_action_label: __('Search'),
primary_action: function() {
var values = d.get_values();
@ -718,16 +717,16 @@ function show_etaxes_import_dialog(frm) {
if (!warehouse) {
frappe.msgprint({
title: __('Внимание'),
title: __('Warning'),
indicator: 'orange',
message: __('Пожалуйста, выберите склад по умолчанию')
message: __('Please select a default warehouse')
});
return;
}
// Закрываем диалог
d.hide();
show_loading_dialog(__('Загрузка счетов-фактур...'));
show_loading_dialog(__('Loading invoices...'));
// Загружаем счета-фактуры
load_etaxes_invoices(frm, fromDate, toDate, warehouse);
@ -744,34 +743,34 @@ function show_etaxes_import_dialog_for_list() {
check_token_before_api_call(function() {
// Создаем диалог для выбора периода и склада
var d = new frappe.ui.Dialog({
title: __('Импорт счета-фактуры из E-Taxes'),
title: __('Import invoice from E-Taxes'),
fields: [
{
fieldname: 'date_range_section',
fieldtype: 'Section Break',
label: __('Период')
label: __('Period')
},
{
fieldname: 'creationDateFrom',
fieldtype: 'Date',
label: __('Дата с'),
label: __('Date from'),
default: moment().subtract(1, 'month').format('YYYY-MM-DD')
},
{
fieldname: 'creationDateTo',
fieldtype: 'Date',
label: __('Дата по'),
label: __('Date to'),
default: moment().format('YYYY-MM-DD')
},
{
fieldname: 'settings_section',
fieldtype: 'Section Break',
label: __('Настройки')
label: __('Settings')
},
{
fieldname: 'warehouse',
fieldtype: 'Link',
label: __('Склад по умолчанию'),
label: __('Default warehouse'),
options: 'Warehouse',
reqd: true,
get_query: function() {
@ -784,7 +783,7 @@ function show_etaxes_import_dialog_for_list() {
}
}
],
primary_action_label: __('Поиск'),
primary_action_label: __('Search'),
primary_action: function() {
var values = d.get_values();
@ -795,16 +794,16 @@ function show_etaxes_import_dialog_for_list() {
if (!warehouse) {
frappe.msgprint({
title: __('Внимание'),
title: __('Warning'),
indicator: 'orange',
message: __('Пожалуйста, выберите склад по умолчанию')
message: __('Please select a default warehouse')
});
return;
}
// Закрываем диалог
d.hide();
show_loading_dialog(__('Загрузка счетов-фактур...'));
show_loading_dialog(__('Loading invoices...'));
// Загружаем счета-фактуры
load_etaxes_invoices(null, fromDate, toDate, warehouse);
@ -836,9 +835,9 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
if (!main_token) {
hide_loading_dialog();
frappe.msgprint({
title: __('Ошибка авторизации'),
title: __('Authentication Error'),
indicator: 'red',
message: __('Не найден токен авторизации. Пожалуйста, завершите процесс авторизации в настройках E-Taxes.')
message: __('Authentication token not found. Please complete the authentication process in E-Taxes settings.')
});
return;
}
@ -866,8 +865,8 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
if (invoices.length > 0) {
// Показываем диалог загрузки для проверки дубликатов
update_loading_message(
__('Проверка на дубликаты...'),
__('Фильтрация уже импортированных счетов-фактур')
__('Checking for duplicates...'),
__('Filtering already imported invoices')
);
// Получаем список уже импортированных инвойсов из E-Taxes Purchase
@ -922,19 +921,19 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
show_invoice_selection_dialog(frm, filteredInvoices, main_token, warehouse);
} else {
frappe.msgprint({
title: __('Информация'),
title: __('Information'),
indicator: 'blue',
message: __('За указанный период не найдено новых счетов-фактур (все ' +
invoices.length + ' уже импортированы)')
message: __('No new invoices found for the specified period (all ' +
invoices.length + ' are already imported)')
});
}
} catch (e) {
console.error("Error processing purchase data:", e);
hide_loading_dialog();
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: __('Произошла ошибка при обработке данных: ') + e.message
message: __('An error occurred while processing data: ') + e.message
});
}
},
@ -946,7 +945,7 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
show_invoice_selection_dialog(frm, invoices, main_token, warehouse);
frappe.show_alert({
message: __('Не удалось выполнить проверку дубликатов. Показаны все инвойсы.'),
message: __('Failed to check for duplicates. Showing all invoices.'),
indicator: 'orange'
}, 5);
}
@ -956,9 +955,9 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
hide_loading_dialog();
frappe.msgprint({
title: __('Информация'),
title: __('Information'),
indicator: 'blue',
message: __('За указанный период не найдено счетов-фактур')
message: __('No invoices found for the specified period')
});
}
} else if (r.message && r.message.error === 'unauthorized') {
@ -987,9 +986,9 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
hide_loading_dialog();
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка загрузки счетов-фактур')
message: r.message ? r.message.message : __('Error loading invoices')
});
}
},
@ -997,18 +996,18 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
console.error("Error fetching invoices:", error);
hide_loading_dialog();
frappe.msgprint({
title: __('Ошибка сети'),
title: __('Network Error'),
indicator: 'red',
message: __('Ошибка при получении данных с сервера: ') + (error || 'Unknown error')
message: __('Error fetching data from server: ') + (error || 'Unknown error')
});
}
});
} else {
hide_loading_dialog();
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: __('Не найдены настройки авторизации E-Taxes')
message: __('E-Taxes authentication settings not found')
});
}
},
@ -1016,9 +1015,9 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse) {
console.error("Error fetching Asan login settings:", error);
hide_loading_dialog();
frappe.msgprint({
title: __('Ошибка сети'),
title: __('Network Error'),
indicator: 'red',
message: __('Ошибка при получении настроек авторизации: ') + (error || 'Unknown error')
message: __('Error fetching authentication settings: ') + (error || 'Unknown error')
});
}
});
@ -1030,10 +1029,10 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
var invoice_table = '<div style="max-height: 500px; overflow-y: auto;"><table class="table table-bordered etaxes-invoices-table">';
invoice_table += '<thead><tr>' +
'<th style="width: 30px;"><input type="checkbox" class="select-all-invoices"></th>' +
'<th>' + __('Номер') + '</th>' +
'<th>' + __('Дата') + '</th>' +
'<th>' + __('Поставщик') + '</th>' +
'<th>' + __('Сумма') + '</th>' +
'<th>' + __('Number') + '</th>' +
'<th>' + __('Date') + '</th>' +
'<th>' + __('Supplier') + '</th>' +
'<th>' + __('Amount') + '</th>' +
'</tr></thead><tbody>';
invoices.forEach(function(invoice) {
@ -1063,12 +1062,12 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
// Информация о количестве найденных инвойсов
let info_message = '<div class="alert alert-info">' +
__('Найдено новых счетов-фактур: ') + invoices.length +
__('New invoices found: ') + invoices.length +
'</div>';
// Создаем диалог
var d = new frappe.ui.Dialog({
title: __('Выбор счета-фактуры'),
title: __('Select invoice'),
fields: [
{
fieldname: 'info_html',
@ -1084,10 +1083,10 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
fieldname: 'warehouse_display',
fieldtype: 'HTML',
options: '<div class="row"><div class="col-xs-12"><div class="alert alert-info">' +
__('Выбранный склад: ') + '<strong>' + warehouse + '</strong></div></div></div>'
__('Selected warehouse: ') + '<strong>' + warehouse + '</strong></div></div></div>'
}
],
primary_action_label: __('Загрузить выбранные'),
primary_action_label: __('Load selected'),
primary_action: function() {
// Получаем выбранные инвойсы
var selected_invoice_ids = [];
@ -1097,9 +1096,9 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
if (selected_invoice_ids.length === 0) {
frappe.msgprint({
title: __('Внимание'),
title: __('Warning'),
indicator: 'orange',
message: __('Не выбрано ни одного счета-фактуры')
message: __('No invoices selected')
});
return;
}
@ -1109,13 +1108,13 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
// Загружаем выбранные инвойсы последовательно
// Показываем диалог с прогрессом
show_loading_dialog(__('Загрузка инвойсов...'), true);
show_loading_dialog(__('Loading invoices...'), true);
update_progress(0, selected_invoice_ids.length);
// Загружаем выбранные инвойсы последовательно
load_selected_invoices(frm, selected_invoice_ids, token, warehouse, 0);
},
secondary_action_label: __('Отмена'),
secondary_action_label: __('Cancel'),
secondary_action: function() {
d.hide();
}
@ -1137,8 +1136,8 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
// Если был запрос на отмену, показываем соответствующее сообщение
if (cancel_loading) {
set_loading_success(
__('Загрузка отменена'),
__('Загружено ' + processed_count + ' инвойсов из ' + (processed_count + invoice_ids.length)),
__('Loading cancelled'),
__('Loaded ' + processed_count + ' invoices out of ' + (processed_count + invoice_ids.length)),
function() {
if (frm) {
frm.reload_doc();
@ -1150,8 +1149,8 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
} else {
// Все успешно загружено
set_loading_success(
__('Загрузка завершена'),
__('Загружено ' + processed_count + ' инвойсов'),
__('Loading completed'),
__('Loaded ' + processed_count + ' invoices'),
function() {
if (frm) {
frm.reload_doc();
@ -1172,8 +1171,8 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
// Обновляем текст загрузки
update_loading_message(
__('Загрузка инвойса (' + (processed_count + 1) + ' из ' + (processed_count + invoice_ids.length + 1) + ')'),
__('Получение данных счета-фактуры...')
__('Loading invoice (' + (processed_count + 1) + ' of ' + (processed_count + invoice_ids.length + 1) + ')'),
__('Getting invoice data...')
);
// Получаем детали счета-фактуры
@ -1199,8 +1198,8 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
let total = r.message.totalAmount || r.message.amount || 0;
update_loading_message(
__('Загрузка инвойса (' + (processed_count + 1) + ' из ' + (processed_count + invoice_ids.length + 1) + ')'),
__('Импорт данных инвойса: ') + serialNumber
__('Loading invoice (' + (processed_count + 1) + ' of ' + (processed_count + invoice_ids.length + 1) + ')'),
__('Importing invoice data: ') + serialNumber
);
// Импортируем данные инвойса с указанием даты и склада
@ -1222,7 +1221,7 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
if (import_r.message && import_r.message.success) {
// Показываем уведомление об успешном импорте
frappe.show_alert({
message: __('Инвойс успешно импортирован: ') + serialNumber,
message: __('Invoice successfully imported: ') + serialNumber,
indicator: 'green'
}, 3);
@ -1268,7 +1267,7 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
} else {
// Обрабатываем ошибку создания E-Taxes Purchase
frappe.show_alert({
message: __('Ошибка при создании E-Taxes Purchase'),
message: __('Error creating E-Taxes Purchase'),
indicator: 'red'
}, 3);
@ -1295,13 +1294,13 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
// Останавливаем загрузку, так как требуется вмешательство пользователя
frappe.msgprint({
title: __('Импорт остановлен'),
title: __('Import stopped'),
indicator: 'red',
message: __('Для продолжения необходимо создать недостающие сопоставления')
message: __('To continue, you need to create missing mappings')
});
} else {
frappe.show_alert({
message: __('Ошибка импорта: ') + serialNumber,
message: __('Import error: ') + serialNumber,
indicator: 'red'
}, 3);
@ -1327,7 +1326,7 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
callback: function(login_r) {
if (login_r.message && login_r.message.found && login_r.message.main_token) {
// Показываем диалог с прогрессом снова
show_loading_dialog(__('Возобновление загрузки инвойсов...'), true);
show_loading_dialog(__('Resuming invoice loading...'), true);
update_progress(processed_count, processed_count + invoice_ids.length + 1);
// Возвращаем текущий инвойс в начало списка
@ -1337,9 +1336,9 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
load_selected_invoices(frm, invoice_ids, login_r.message.main_token, warehouse, processed_count);
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: __('Не удалось получить токен после авторизации')
message: __('Failed to get token after authentication')
});
}
}
@ -1356,7 +1355,7 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
);
} else {
frappe.show_alert({
message: __('Ошибка загрузки данных инвойса'),
message: __('Error loading invoice data'),
indicator: 'red'
}, 3);
@ -1373,12 +1372,12 @@ function load_selected_invoices(frm, invoice_ids, token, warehouse, processed_co
function show_unmatched_items_dialog(invoice_data, unmatched_items) {
// Создаем таблицу с несопоставленными товарами
var items_table = '<div class="text-center mb-3">' +
'<p>' + __('Для завершения импорта необходимо создать сопоставления для следующих товаров:') + '</p>' +
'<p>' + __('To complete the import, you need to create mappings for the following items:') + '</p>' +
'</div>' +
'<div style="max-height: 300px; overflow-y: auto;"><table class="table table-bordered">';
items_table += '<thead><tr>' +
'<th>' + __('Название') + '</th>' +
'<th>' + __('Код') + '</th>' +
'<th>' + __('Name') + '</th>' +
'<th>' + __('Code') + '</th>' +
'</tr></thead><tbody>';
unmatched_items.forEach(function(item) {
@ -1392,7 +1391,7 @@ function show_unmatched_items_dialog(invoice_data, unmatched_items) {
// Создаем диалог
var d = new frappe.ui.Dialog({
title: __('Несопоставленные товары'),
title: __('Unmapped items'),
fields: [
{
fieldname: 'items_html',
@ -1400,14 +1399,14 @@ function show_unmatched_items_dialog(invoice_data, unmatched_items) {
options: items_table
}
],
primary_action_label: __('Перейти к настройкам'),
primary_action_label: __('Go to settings'),
primary_action: function() {
d.hide();
// Переходим к настройкам E-Taxes
frappe.set_route('Form', 'E-Taxes Settings');
},
secondary_action_label: __('Отмена'),
secondary_action_label: __('Cancel'),
secondary_action: function() {
d.hide();
}
@ -1420,22 +1419,22 @@ function show_unmatched_items_dialog(invoice_data, unmatched_items) {
function show_unmatched_parties_dialog(invoice_data, unmatched_parties) {
// Создаем таблицу с несопоставленными контрагентами
var parties_table = '<div class="text-center mb-3">' +
'<p>' + __('Для завершения импорта необходимо создать сопоставления для следующих контрагентов:') + '</p>' +
'<p>' + __('To complete the import, you need to create mappings for the following partners:') + '</p>' +
'</div>' +
'<div style="max-height: 300px; overflow-y: auto;"><table class="table table-bordered">';
parties_table += '<thead><tr>' +
'<th>' + __('Название') + '</th>' +
'<th>' + __('ИНН/VOEN') + '</th>' +
'<th>' + __('Тип') + '</th>' +
'<th>' + __('Name') + '</th>' +
'<th>' + __('TIN/VOEN') + '</th>' +
'<th>' + __('Type') + '</th>' +
'</tr></thead><tbody>';
unmatched_parties.forEach(function(party) {
// Преобразуем тип контрагента для отображения
let partyTypeDisplay = '';
if (party.type === 'Sender') {
partyTypeDisplay = 'Поставщик (Sender)';
partyTypeDisplay = 'Supplier (Sender)';
} else if (party.type === 'Receiver') {
partyTypeDisplay = 'Клиент (Receiver)';
partyTypeDisplay = 'Customer (Receiver)';
} else {
partyTypeDisplay = party.type || '';
}
@ -1451,7 +1450,7 @@ function show_unmatched_parties_dialog(invoice_data, unmatched_parties) {
// Создаем диалог
var d = new frappe.ui.Dialog({
title: __('Несопоставленные контрагенты'),
title: __('Unmapped partners'),
fields: [
{
fieldname: 'parties_html',
@ -1459,14 +1458,14 @@ function show_unmatched_parties_dialog(invoice_data, unmatched_parties) {
options: parties_table
}
],
primary_action_label: __('Перейти к настройкам'),
primary_action_label: __('Go to settings'),
primary_action: function() {
d.hide();
// Переходим к настройкам E-Taxes
frappe.set_route('Form', 'E-Taxes Settings');
},
secondary_action_label: __('Отмена'),
secondary_action_label: __('Cancel'),
secondary_action: function() {
d.hide();
}

View File

@ -4,11 +4,23 @@ frappe.form.link_formatters['E-Taxes Item'] = function(value, doc) {
frappe.ui.form.on('E-Taxes Settings', {
refresh: function(frm) {
frm.fields_dict['item_mappings'].grid.add_custom_button(__('Bulk Edit'), function() {
bulk_edit_table(frm, 'item_mappings');
});
// Добавляем кнопку "Bulk Edit" для табличной части party_mappings
frm.fields_dict['party_mappings'].grid.add_custom_button(__('Bulk Edit'), function() {
bulk_edit_table(frm, 'party_mappings');
});
// Кнопка для сопоставления товаров по похожему названию
frm.add_custom_button(__('Сопоставить товары по похожему названию'), function() {
frm.add_custom_button(__('Match items by similar name'), function() {
if(frm.is_dirty()) {
frappe.confirm(
__('Документ содержит несохраненные изменения. Сохранить перед выполнением операции?'),
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
@ -18,7 +30,7 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Операция отменена. Сначала сохраните документ.'),
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
}, 5);
}
@ -30,10 +42,10 @@ frappe.ui.form.on('E-Taxes Settings', {
function match_similar_items() {
frappe.confirm(
__('Эта операция автоматически сопоставит несопоставленные товары с похожими названиями. Продолжить?'),
__('This operation will automatically match unmapped items with similar names. Continue?'),
function() {
frappe.show_alert({
message: __('Выполняется сопоставление товаров...'),
message: __('Matching items...'),
indicator: 'blue'
});
@ -42,16 +54,16 @@ frappe.ui.form.on('E-Taxes Settings', {
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
message: __('Сопоставлено {0} из {1} товаров',
message: __('Matched {0} of {1} items',
[r.message.matched_count, r.message.total_processed]),
indicator: 'green'
}, 5);
frm.reload_doc();
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка сопоставления товаров')
message: r.message ? r.message.message : __('Error matching items')
});
}
}
@ -59,13 +71,13 @@ frappe.ui.form.on('E-Taxes Settings', {
}
);
}
}, __('Товары'));
}, __('Items'));
// Кнопка для создания товаров-соответствий
frm.add_custom_button(__('Создать товары-соответствия'), function() {
frm.add_custom_button(__('Create matching items'), function() {
if(frm.is_dirty()) {
frappe.confirm(
__('Документ содержит несохраненные изменения. Сохранить перед выполнением операции?'),
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
@ -75,7 +87,7 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Операция отменена. Сначала сохраните документ.'),
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
}, 5);
}
@ -87,11 +99,11 @@ frappe.ui.form.on('E-Taxes Settings', {
function create_unmapped_items() {
frappe.confirm(
__('<div style="color: red; font-weight: bold;">ВНИМАНИЕ! Это действие создаст новые товары в системе для всех несопоставленных элементов!</div><p>Это действие необратимо. Вы уверены?</p>'),
__('<div style="color: red; font-weight: bold;">WARNING! This action will create new items in the system for all unmapped elements!</div><p>This action is irreversible. Are you sure?</p>'),
function() {
// Дополнительное подтверждение
frappe.confirm(
__('Вы действительно уверены, что хотите создать новые товары? Это нельзя будет отменить.'),
__('Are you really sure you want to create new items? This cannot be undone.'),
function() {
frappe.call({
method: 'invoice_az.api.create_unmapped_items',
@ -101,15 +113,15 @@ frappe.ui.form.on('E-Taxes Settings', {
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
message: __('Создано {0} новых товаров', [r.message.created_count]),
message: __('Created {0} new items', [r.message.created_count]),
indicator: 'green'
}, 5);
frm.reload_doc();
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка создания товаров')
message: r.message ? r.message.message : __('Error creating items')
});
}
}
@ -119,13 +131,13 @@ frappe.ui.form.on('E-Taxes Settings', {
}
);
}
}, __('Товары'));
}, __('Items'));
// Кнопка для добавления несопоставленных товаров в таблицу
frm.add_custom_button(__('Добавить несопоставленные товары'), function() {
frm.add_custom_button(__('Add unmapped items'), function() {
if(frm.is_dirty()) {
frappe.confirm(
__('Документ содержит несохраненные изменения. Сохранить перед выполнением операции?'),
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
@ -135,7 +147,7 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Операция отменена. Сначала сохраните документ.'),
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
}, 5);
}
@ -170,32 +182,32 @@ frappe.ui.form.on('E-Taxes Settings', {
});
frm.refresh_field('item_mappings');
frappe.show_alert({
message: __('Добавлено {0} несопоставленных товаров', [r.message.items.length]),
message: __('Added {0} unmapped items', [r.message.items.length]),
indicator: 'green'
}, 5);
} else {
frappe.show_alert({
message: __('Нет несопоставленных товаров'),
message: __('No unmapped items'),
indicator: 'blue'
}, 5);
}
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка получения товаров')
message: r.message ? r.message.message : __('Error retrieving items')
});
}
}
});
}
}, __('Товары'));
}, __('Items'));
// Аналогичные кнопки для контрагентов
frm.add_custom_button(__('Сопоставить контрагентов по похожему названию'), function() {
frm.add_custom_button(__('Match partners by similar name'), function() {
if(frm.is_dirty()) {
frappe.confirm(
__('Документ содержит несохраненные изменения. Сохранить перед выполнением операции?'),
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
@ -205,7 +217,7 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Операция отменена. Сначала сохраните документ.'),
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
}, 5);
}
@ -217,10 +229,10 @@ frappe.ui.form.on('E-Taxes Settings', {
function match_similar_parties() {
frappe.confirm(
__('Эта операция автоматически сопоставит несопоставленных контрагентов с похожими названиями. Продолжить?'),
__('This operation will automatically match unmapped partners with similar names. Continue?'),
function() {
frappe.show_alert({
message: __('Выполняется сопоставление контрагентов...'),
message: __('Matching partners...'),
indicator: 'blue'
});
@ -229,16 +241,16 @@ frappe.ui.form.on('E-Taxes Settings', {
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
message: __('Сопоставлено {0} из {1} контрагентов',
message: __('Matched {0} of {1} partners',
[r.message.matched_count, r.message.total_processed]),
indicator: 'green'
}, 5);
frm.reload_doc();
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка сопоставления контрагентов')
message: r.message ? r.message.message : __('Error matching partners')
});
}
}
@ -246,12 +258,12 @@ frappe.ui.form.on('E-Taxes Settings', {
}
);
}
}, __('Контрагенты'));
}, __('Partners'));
frm.add_custom_button(__('Создать контрагенты-соответствия'), function() {
frm.add_custom_button(__('Create matching partners'), function() {
if(frm.is_dirty()) {
frappe.confirm(
__('Документ содержит несохраненные изменения. Сохранить перед выполнением операции?'),
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
@ -261,7 +273,7 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Операция отменена. Сначала сохраните документ.'),
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
}, 5);
}
@ -273,11 +285,11 @@ frappe.ui.form.on('E-Taxes Settings', {
function create_unmapped_parties() {
frappe.confirm(
__('<div style="color: red; font-weight: bold;">ВНИМАНИЕ! Это действие создаст новых контрагентов в системе для всех несопоставленных элементов!</div><p>Это действие необратимо. Вы уверены?</p>'),
__('<div style="color: red; font-weight: bold;">WARNING! This action will create new partners in the system for all unmapped elements!</div><p>This action is irreversible. Are you sure?</p>'),
function() {
// Дополнительное подтверждение
frappe.confirm(
__('Вы действительно уверены, что хотите создать новых контрагентов? Это нельзя будет отменить.'),
__('Are you really sure you want to create new partners? This cannot be undone.'),
function() {
frappe.call({
method: 'invoice_az.api.create_unmapped_parties',
@ -287,15 +299,15 @@ frappe.ui.form.on('E-Taxes Settings', {
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
message: __('Создано {0} новых контрагентов', [r.message.created_count]),
message: __('Created {0} new partners', [r.message.created_count]),
indicator: 'green'
}, 5);
frm.reload_doc();
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка создания контрагентов')
message: r.message ? r.message.message : __('Error creating partners')
});
}
}
@ -305,12 +317,12 @@ frappe.ui.form.on('E-Taxes Settings', {
}
);
}
}, __('Контрагенты'));
}, __('Partners'));
frm.add_custom_button(__('Добавить несопоставленных контрагентов'), function() {
frm.add_custom_button(__('Add unmapped partners'), function() {
if(frm.is_dirty()) {
frappe.confirm(
__('Документ содержит несохраненные изменения. Сохранить перед выполнением операции?'),
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
@ -320,7 +332,7 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Операция отменена. Сначала сохраните документ.'),
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
}, 5);
}
@ -366,25 +378,166 @@ frappe.ui.form.on('E-Taxes Settings', {
});
frm.refresh_field('party_mappings');
frappe.show_alert({
message: __('Добавлено {0} несопоставленных контрагентов', [r.message.parties.length]),
message: __('Added {0} unmapped partners', [r.message.parties.length]),
indicator: 'green'
}, 5);
} else {
frappe.show_alert({
message: __('Нет несопоставленных контрагентов'),
message: __('No unmapped partners'),
indicator: 'blue'
}, 5);
}
} else {
frappe.msgprint({
title: __('Ошибка'),
title: __('Error'),
indicator: 'red',
message: r.message ? r.message.message : __('Ошибка получения контрагентов')
message: r.message ? r.message.message : __('Error retrieving partners')
});
}
}
});
}
}, __('Контрагенты'));
}, __('Partners'));
}
});
function bulk_edit_table(frm, table_field) {
// Получаем выбранные строки
const selected_rows = frm.fields_dict[table_field].grid.get_selected_children();
if (selected_rows.length === 0) {
frappe.msgprint(__("No rows selected. Please select rows to edit."));
return;
}
// Получаем метаданные дочернего doctype
const child_doctype = selected_rows[0].doctype;
const meta = frappe.get_meta(child_doctype);
// Формируем список полей для редактирования
const field_options = [];
const field_map = {};
meta.fields.forEach(df => {
// Исключаем служебные поля и поля только для чтения
if (!['Section Break', 'Column Break', 'Tab Break', 'HTML', 'Table', 'Button'].includes(df.fieldtype) &&
!df.read_only && !df.hidden) {
field_options.push({
label: __(df.label || frappe.model.unscrub(df.fieldname)),
value: df.fieldname
});
field_map[df.fieldname] = df;
}
});
// Создаем диалог
const d = new frappe.ui.Dialog({
title: __("Bulk Edit Selected Rows"),
fields: [
{
label: __("Field to Edit"),
fieldname: "field_to_edit",
fieldtype: "Select",
options: field_options,
reqd: 1
},
{
fieldname: "value_section",
fieldtype: "Section Break",
label: __("New Value"),
collapsible: 0,
hidden: 1
},
{
fieldname: "value_container",
fieldtype: "HTML",
options: '<div id="value_field_container"></div>'
}
],
primary_action_label: __("Apply"),
primary_action: function() {
// Получаем выбранное поле и значение
const field_name = d.get_value("field_to_edit");
// Если не выбрано поле
if (!field_name || !d.new_value_field) {
frappe.msgprint(__("Please select a field and enter a value."));
return;
}
// Получаем новое значение из динамического поля
const new_value = d.new_value_field.get_value();
// Проверяем, что значение введено
if (new_value === undefined || new_value === null || new_value === "") {
frappe.msgprint(__("Please enter a new value."));
return;
}
// Применяем изменения
frappe.show_alert({
message: __("Updating rows..."),
indicator: 'blue'
});
let count = 0;
const total = selected_rows.length;
const promises = [];
selected_rows.forEach(row => {
promises.push(
frappe.model.set_value(row.doctype, row.name, field_name, new_value)
.then(() => {
count++;
})
);
});
Promise.all(promises).then(() => {
frm.refresh_field(table_field);
frappe.show_alert({
message: __("Updated {0} rows", [count]),
indicator: 'green'
}, 5);
d.hide();
});
}
});
// Обработчик изменения поля
d.fields_dict.field_to_edit.$input.on('change', function() {
const field_name = d.get_value("field_to_edit");
const field_def = field_map[field_name];
if (!field_def) return;
// Показываем секцию для нового значения
d.set_df_property("value_section", "hidden", 0);
// Удаляем предыдущее поле, если оно было
if (d.new_value_field) {
d.new_value_field.$wrapper.remove();
delete d.new_value_field;
}
// Создаем новое поле нужного типа
const value_container = d.fields_dict.value_container.$wrapper.find("#value_field_container");
d.new_value_field = frappe.ui.form.make_control({
df: {
label: __("New Value for {0}", [__(field_def.label || frappe.model.unscrub(field_def.fieldname))]),
fieldname: "new_value",
fieldtype: field_def.fieldtype,
options: field_def.options || "",
reqd: 1
},
parent: value_container,
render_input: true
});
d.new_value_field.refresh();
});
d.show();
}