diff --git a/jey_erp/custom/create_asset_categories.py b/jey_erp/custom/create_asset_categories.py
index ffcc89d..9bed3ae 100644
--- a/jey_erp/custom/create_asset_categories.py
+++ b/jey_erp/custom/create_asset_categories.py
@@ -1,6 +1,16 @@
import frappe
+def _logger():
+ """Файловый логгер для процесса создания категорий активов.
+
+ Используем frappe.logger() для info/debug-сообщений (пишутся в
+ logs/asset_categories.log), а frappe.log_error() оставляем только
+ для реальных ошибок (записи в Error Log DocType).
+ """
+ return frappe.logger("asset_categories", allow_site=True, file_count=20)
+
+
def get_system_asset_categories():
"""
Возвращает список системных категорий активов
@@ -79,25 +89,24 @@ def create_asset_categories_on_company_update(doc, method):
# Проверяем первую категорию из списка
if frappe.db.exists("Asset Category", {"asset_category_name": system_categories[0]}):
# Категории уже созданы (они создаются для всех компаний сразу)
- frappe.log_error(
- f"Asset categories already exist, skipping creation\nCompany: {company_name}\nFirst category found: {system_categories[0]}",
- "AC Skip Existing"
+ _logger().info(
+ f"Asset categories already exist, skipping creation. "
+ f"Company: {company_name}, first category found: {system_categories[0]}"
)
return
# Логируем начало создания
- frappe.log_error(
- f"Creating asset categories automatically\nTriggered by: Company.on_update\nCompany: {company_name}",
- "AC Auto Create"
+ _logger().info(
+ f"Creating asset categories automatically (Company.on_update). Company: {company_name}"
)
try:
# Создаем категории
_create_categories_for_company(company_name)
- frappe.log_error(
- f"Asset categories created successfully\nCompany: {company_name}\nCategories: {len(system_categories)}",
- "AC Created"
+ _logger().info(
+ f"Asset categories created successfully. "
+ f"Company: {company_name}, categories: {len(system_categories)}"
)
except Exception as e:
import traceback
@@ -116,10 +125,7 @@ def create_asset_categories_for_company(company_name):
Args:
company_name: Название компании
"""
- frappe.log_error(
- f"Function started\nCompany: {company_name}",
- "AC Create Start"
- )
+ _logger().info(f"create_asset_categories_for_company started. Company: {company_name}")
try:
# Проверяем, что компания существует
@@ -132,10 +138,7 @@ def create_asset_categories_for_company(company_name):
)
return
- frappe.log_error(
- f"Company exists in database\nCompany: {company_name}",
- "AC Company OK"
- )
+ _logger().debug(f"Company exists in database. Company: {company_name}")
# Проверка идемпотентности: если категории уже есть, не создаем
system_categories = get_system_asset_categories()
@@ -144,28 +147,27 @@ def create_asset_categories_for_company(company_name):
filters={"asset_category_name": ["in", system_categories]}
)
- frappe.log_error(
- f"Idempotency check\nSystem categories: {len(system_categories)}\nExisting categories: {existing_categories}\nCategories list: {system_categories}",
- "AC Check Existing"
+ _logger().debug(
+ f"Idempotency check. System categories: {len(system_categories)}, "
+ f"existing: {existing_categories}"
)
if existing_categories > 0:
- frappe.log_error(
- f"Categories already exist, skipping creation\nFound: {existing_categories} of {len(system_categories)}",
- "AC Already Exist"
+ _logger().info(
+ f"Categories already exist, skipping creation. "
+ f"Found: {existing_categories} of {len(system_categories)}"
)
return
# Создаем категории
- frappe.log_error(
- f"Starting category creation\nCompany: {company_name}\nCategories to create: {len(system_categories)}",
- "AC Creating"
+ _logger().info(
+ f"Starting category creation. Company: {company_name}, "
+ f"categories to create: {len(system_categories)}"
)
_create_categories_for_company(company_name)
- frappe.log_error(
- f"Asset categories creation completed successfully\nCompany: {company_name}",
- "AC Complete"
+ _logger().info(
+ f"Asset categories creation completed successfully. Company: {company_name}"
)
except Exception as e:
@@ -185,10 +187,7 @@ def _create_categories_for_company(company_name):
asset_categories = get_system_asset_categories()
# Находим нужные счета
- frappe.log_error(
- f"Searching for required accounts\nCompany: {company_name}\nAccounts needed: 3",
- "AC Search Accounts"
- )
+ _logger().debug(f"Searching for required accounts. Company: {company_name}, needed: 3")
try:
accumulated_depreciation = get_account_by_name(
@@ -204,9 +203,10 @@ def _create_categories_for_company(company_name):
company_name
)
- frappe.log_error(
- f"All required accounts found\nCompany: {company_name}\n\nAccounts:\n1. Accumulated Depreciation: {accumulated_depreciation}\n2. Depreciation Expense: {depreciation_expense}\n3. Fixed Asset: {fixed_asset}",
- "AC Accounts OK"
+ _logger().debug(
+ f"All required accounts found. Company: {company_name}, "
+ f"fixed_asset: {fixed_asset}, accumulated_depreciation: {accumulated_depreciation}, "
+ f"depreciation_expense: {depreciation_expense}"
)
except Exception as e:
@@ -250,14 +250,22 @@ def _create_categories_for_company(company_name):
f"AC Cat ERROR {idx}"
)
- frappe.db.commit()
+ # NB: commit намеренно НЕ вызываем. В активном пути (Company.on_update)
+ # транзакцию коммитит сам фреймворк; ручной commit внутри хука разрывает
+ # атомарность сохранения Company. В фоновой задаче (frappe.enqueue) и в
+ # патчах commit тоже выполняется обёрткой после успешного завершения.
# Финальный отчет
status = "SUCCESS" if created_count > 0 else ("SKIP" if skipped_count > 0 else "ERROR")
- frappe.log_error(
- f"Category creation finished\nCompany: {company_name}\nCreated: {created_count}\nSkipped: {skipped_count}\nErrors: {len(errors)}\n\nError details:\n{chr(10).join(errors) if errors else 'No errors'}",
- f"AC {status}"
+ summary = (
+ f"Category creation finished [{status}]. Company: {company_name}, "
+ f"created: {created_count}, skipped: {skipped_count}, errors: {len(errors)}"
)
+ if errors:
+ # Реальные ошибки создания категорий — в Error Log, чтобы были заметны.
+ frappe.log_error(summary + "\n\n" + "\n".join(errors), f"AC {status}")
+ else:
+ _logger().info(summary)
def prevent_delete_system_asset_categories(doc, method):
diff --git a/jey_erp/custom_fields.py b/jey_erp/custom_fields.py
index 894b979..ca57459 100644
--- a/jey_erp/custom_fields.py
+++ b/jey_erp/custom_fields.py
@@ -1054,6 +1054,29 @@ def create_custom_fields():
hidden=1,
read_only=1,
no_copy=1
+ ),
+ dict(
+ fieldname='etaxes_classification_code',
+ label='Təsnifat Kodu',
+ fieldtype='Link',
+ options='Classification code',
+ insert_after='loaded_from_etaxes',
+ read_only=1,
+ no_copy=1,
+ translatable=0,
+ description='Təsnifat kodu (taxCodeInfo.code) imported from E-Taxes'
+ ),
+ dict(
+ fieldname='etaxes_explanation',
+ label='Explanation',
+ fieldtype='Small Text',
+ insert_after='etaxes_classification_code',
+ in_list_view=1,
+ columns=3,
+ read_only=1,
+ no_copy=1,
+ translatable=0,
+ description='Explanation imported from E-Taxes VAT operation'
)
],
"Bank": [
diff --git a/jey_erp/public/js/currency_exchange_list.js b/jey_erp/public/js/currency_exchange_list.js
index 2a86720..93b4d35 100644
--- a/jey_erp/public/js/currency_exchange_list.js
+++ b/jey_erp/public/js/currency_exchange_list.js
@@ -50,10 +50,31 @@ function _show_import_dialog(available_currencies) {
d.hide();
+ // Защита от гонки realtime: 'complete' вызывает hide_progress() (обнуляет
+ // frappe.cur_progress). Опоздавший 'progress' иначе пересоздаёт модал, который
+ // уже никто не закрывает. Флаг делает обработчик прогресса no-op после finish.
+ let importFinished = false;
+ // Надёжное закрытие прогресс-бара: frappe.hide_progress() в этом окружении
+ // (jey_theme/Bootstrap) не всегда убирает модал — он зависает. Принудительно.
+ const closeProgress = function () {
+ if (frappe.cur_progress) {
+ try {
+ frappe.cur_progress.$wrapper.modal("hide");
+ frappe.cur_progress.$wrapper.remove();
+ } catch (e) {}
+ frappe.cur_progress = null;
+ }
+ if ($(".modal:visible").length === 0) {
+ $(".modal-backdrop").remove();
+ $("body").removeClass("modal-open");
+ }
+ };
+
frappe.realtime.off("cbar_import_progress");
frappe.realtime.off("cbar_import_complete");
frappe.realtime.on("cbar_import_progress", function (data) {
+ if (importFinished) return;
frappe.show_progress(
__("Importing CBAR Rates"),
data.current,
@@ -63,9 +84,10 @@ function _show_import_dialog(available_currencies) {
});
frappe.realtime.on("cbar_import_complete", function (data) {
+ importFinished = true;
frappe.realtime.off("cbar_import_progress");
frappe.realtime.off("cbar_import_complete");
- frappe.hide_progress();
+ closeProgress();
const { created, updated, skipped_dates } = data;
let msg = `${__("Import complete")}
${__("Created")}: ${created}
${__("Updated")}: ${updated}`;
@@ -89,7 +111,7 @@ function _show_import_dialog(available_currencies) {
error: function () {
frappe.realtime.off("cbar_import_progress");
frappe.realtime.off("cbar_import_complete");
- frappe.hide_progress();
+ closeProgress();
}
});
}
diff --git a/jey_erp/translation_markers.py b/jey_erp/translation_markers.py
index e0f6b19..3d5051a 100644
--- a/jey_erp/translation_markers.py
+++ b/jey_erp/translation_markers.py
@@ -77,6 +77,8 @@ _lt("Employer Name")
_lt("Employer Position")
_lt("Enter the tax-exempt area value")
_lt("Expense/Income")
+_lt("Explanation")
+_lt("Explanation imported from E-Taxes VAT operation")
_lt("FIN")
_lt("First Name")
_lt("For agricultural: hectares. For industrial: square meters.")
@@ -178,6 +180,8 @@ _lt("Taxable Assets Information")
_lt("Taxation system")
_lt("Taxpayer Activity Group")
_lt("Type of act for e-taxes")
+_lt("Təsnifat Kodu")
+_lt("Təsnifat kodu (taxCodeInfo.code) imported from E-Taxes")
_lt("Used as the Expense Account when this service item is selected in a Landed Cost Voucher.")
_lt("Uçot metodu (Accounting Method)")
_lt("VAT 0% with amount")