e-taxes settings is single doctype now

This commit is contained in:
Ali 2026-03-03 16:17:07 +04:00
parent 7969b96d87
commit 639d580c19
10 changed files with 174 additions and 146 deletions

View File

@ -367,27 +367,8 @@ def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
@frappe.whitelist()
def get_default_settings():
"""Getting active E-Taxes settings"""
try:
settings_list = frappe.get_all('E-Taxes Settings',
filters={'is_active': 1},
limit=1)
if settings_list:
settings = frappe.get_doc('E-Taxes Settings', settings_list[0].name)
return {
'found': True,
'settings': settings.as_dict()
}
else:
return {
'found': False,
'message': 'No active E-Taxes settings found'
}
except Exception as e:
return {
'found': False,
'message': str(e)
}
settings = frappe.get_single('E-Taxes Settings')
return {'found': True, 'settings': settings.as_dict()}
@frappe.whitelist()
def normalize_azeri_text(text):
@ -599,7 +580,7 @@ def match_similar_items():
total_processed = len(items_to_process)
# Get fresh copy of settings document
doc = frappe.get_doc("E-Taxes Settings", settings.name)
doc = frappe.get_single('E-Taxes Settings')
# Process all items
for etaxes_item in items_to_process:
@ -668,16 +649,16 @@ def match_similar_items():
}
@frappe.whitelist()
def create_unmapped_items(settings_name):
def create_unmapped_items():
"""Creating items for unmapped elements from E-Taxes settings table with EQM Code support"""
# Записываем активность
record_etaxes_activity()
try:
import re
# Get settings
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
# Get unmapped items efficiently
unmapped_etaxes_items = []
@ -1606,7 +1587,7 @@ def match_similar_units():
total_processed = len(units_to_process)
# Get fresh copy of settings document
doc = frappe.get_doc("E-Taxes Settings", settings.name)
doc = frappe.get_single('E-Taxes Settings')
# Process all units
for etaxes_unit in units_to_process:
@ -1675,16 +1656,16 @@ def match_similar_units():
}
@frappe.whitelist()
def create_unmapped_units(settings_name):
def create_unmapped_units():
"""Creating units for unmapped elements from E-Taxes settings table"""
# Записываем активность
record_etaxes_activity()
try:
import re
# Get settings
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
# Get elements from table that have no mapping
unmapped_units = []
@ -2060,11 +2041,11 @@ def update_mapped_statuses(doc, method=None):
"E-Taxes Settings Error")
@frappe.whitelist()
def refresh_item_mappings_display(settings_name):
def refresh_item_mappings_display():
"""Операция-пустышка для обновления отображения item mappings"""
try:
# Получаем документ настроек
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
# Подготавливаем данные для обновления отображения на клиенте
updated_mappings = []
@ -2912,7 +2893,7 @@ def match_similar_customers():
total_processed = len(customers_to_process)
# Get fresh copy of settings document
doc = frappe.get_doc("E-Taxes Settings", settings.name)
doc = frappe.get_single('E-Taxes Settings')
# Process all customers
for etaxes_customer in customers_to_process:
@ -3039,7 +3020,7 @@ def match_similar_suppliers():
total_processed = len(suppliers_to_process)
# Get fresh copy of settings document
doc = frappe.get_doc("E-Taxes Settings", settings.name)
doc = frappe.get_single('E-Taxes Settings')
# Process all suppliers
for etaxes_supplier in suppliers_to_process:
@ -3109,18 +3090,18 @@ def match_similar_suppliers():
}
@frappe.whitelist()
def create_unmapped_customers(settings_name):
def create_unmapped_customers():
"""Creating customers for unmapped elements from E-Taxes settings table"""
# Записываем активность
record_etaxes_activity()
try:
# Get settings
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
# Get elements from table that have no mapping
unmapped_customers = []
for mapping_customer in settings_doc.customer_mappings:
# Check that customer has no mapping or it's empty
if not mapping_customer.erp_customer:
@ -3128,7 +3109,7 @@ def create_unmapped_customers(settings_name):
if frappe.db.exists('E-Taxes Customers', mapping_customer.etaxes_customer_name):
etaxes_customer = frappe.get_doc('E-Taxes Customers', mapping_customer.etaxes_customer_name)
unmapped_customers.append(etaxes_customer)
if len(unmapped_customers) == 0:
return {
"success": True,
@ -3136,10 +3117,10 @@ def create_unmapped_customers(settings_name):
"error_count": 0,
"message": "No unmapped customers in table to create"
}
created_count = 0
error_count = 0
# Collect individual settings for each element
mapping_settings = {}
for mapping in settings_doc.customer_mappings:
@ -3150,10 +3131,10 @@ def create_unmapped_customers(settings_name):
customer_settings['territory'] = mapping.territory
if mapping.payment_terms:
customer_settings['payment_terms'] = mapping.payment_terms
if customer_settings:
mapping_settings[mapping.etaxes_customer_name] = customer_settings
for etaxes_customer in unmapped_customers:
try:
# Check if such customer with this name already exists
@ -3232,14 +3213,14 @@ def create_unmapped_customers(settings_name):
}
@frappe.whitelist()
def create_unmapped_suppliers(settings_name):
def create_unmapped_suppliers():
"""Creating suppliers for unmapped elements from E-Taxes settings table"""
# Записываем активность
record_etaxes_activity()
try:
# Get settings
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
# Get elements from table that have no mapping
unmapped_suppliers = []
@ -3352,18 +3333,18 @@ def create_unmapped_suppliers(settings_name):
}
@frappe.whitelist()
def create_unmapped_customers(settings_name):
def create_unmapped_customers_v2():
"""Creating customers for unmapped elements from E-Taxes settings table"""
# Записываем активность
record_etaxes_activity()
try:
# Get settings
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
# Get elements from table that have no mapping
unmapped_customers = []
for mapping_customer in settings_doc.customer_mappings:
# Check that customer has no mapping or it's empty
if not mapping_customer.erp_customer:
@ -3371,7 +3352,7 @@ def create_unmapped_customers(settings_name):
if frappe.db.exists('E-Taxes Customers', mapping_customer.etaxes_customer_name):
etaxes_customer = frappe.get_doc('E-Taxes Customers', mapping_customer.etaxes_customer_name)
unmapped_customers.append(etaxes_customer)
if len(unmapped_customers) == 0:
return {
"success": True,
@ -3379,10 +3360,10 @@ def create_unmapped_customers(settings_name):
"error_count": 0,
"message": "No unmapped customers in table to create"
}
created_count = 0
error_count = 0
# Collect individual settings for each element
mapping_settings = {}
for mapping in settings_doc.customer_mappings:
@ -3393,10 +3374,10 @@ def create_unmapped_customers(settings_name):
customer_settings['territory'] = mapping.territory
if mapping.payment_terms:
customer_settings['payment_terms'] = mapping.payment_terms
if customer_settings:
mapping_settings[mapping.etaxes_customer_name] = customer_settings
for etaxes_customer in unmapped_customers:
try:
# ИСПРАВЛЕНИЕ: Обрезаем название клиента до 140 символов
@ -3484,15 +3465,7 @@ def create_unmapped_customers(settings_name):
@frappe.whitelist()
def get_active_settings():
"""Getting active E-Taxes settings"""
settings_list = frappe.get_all('E-Taxes Settings',
filters={'is_active': 1},
order_by='creation desc',
limit=1)
if not settings_list:
return None
return frappe.get_doc('E-Taxes Settings', settings_list[0].name)
return frappe.get_single('E-Taxes Settings')
@frappe.whitelist()
def normalize_string(text, consider_azeri=True):

View File

@ -114,9 +114,6 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
frappe.call({
method: 'invoice_az.api.create_unmapped_items',
args: {
'settings_name': frm.doc.name
},
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
@ -186,9 +183,6 @@ frappe.ui.form.on('E-Taxes Settings', {
frm.save().then(function() {
frappe.call({
method: 'invoice_az.api.refresh_item_mappings_display',
args: {
'settings_name': frm.doc.name
},
callback: function(refresh_r) {
if (refresh_r.message && refresh_r.message.success && refresh_r.message.updated_mappings) {
refresh_r.message.updated_mappings.forEach(function(mapping) {
@ -351,9 +345,6 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
frappe.call({
method: 'invoice_az.api.create_unmapped_customers',
args: {
'settings_name': frm.doc.name
},
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
@ -535,9 +526,6 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
frappe.call({
method: 'invoice_az.api.create_unmapped_suppliers',
args: {
'settings_name': frm.doc.name
},
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
@ -713,9 +701,6 @@ frappe.ui.form.on('E-Taxes Settings', {
function() {
frappe.call({
method: 'invoice_az.api.create_unmapped_units',
args: {
'settings_name': frm.doc.name
},
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({

View File

@ -1,15 +1,10 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:settings_name",
"creation": "2025-05-06 12:00:00",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"main_tab",
"settings_name",
"is_default",
"is_active",
"general_section",
"similarity_threshold",
"consider_azeri_chars",
@ -55,26 +50,6 @@
"fieldtype": "Tab Break",
"label": "Settings"
},
{
"fieldname": "settings_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Settings Name",
"reqd": 1,
"unique": 1
},
{
"default": "0",
"fieldname": "is_default",
"fieldtype": "Check",
"label": "Is Default"
},
{
"default": "1",
"fieldname": "is_active",
"fieldtype": "Check",
"label": "Is Active"
},
{
"fieldname": "general_section",
"fieldtype": "Section Break",
@ -102,7 +77,6 @@
{
"fieldname": "default_item_group",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Default Item Group",
"options": "Item Group",
"reqd": 1
@ -110,7 +84,6 @@
{
"fieldname": "default_uom",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Default Unit of Measure",
"options": "UOM",
"reqd": 1
@ -297,12 +270,12 @@
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2026-01-14 15:35:33.639586",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Settings",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{

View File

@ -7,16 +7,47 @@ from frappe.model.document import Document
class ETaxesSettings(Document):
def validate(self):
# Проверка на единственную активную настройку
if self.is_active:
active_settings = frappe.get_all('E-Taxes Settings',
filters={'is_active': 1, 'name': ['!=', self.name]})
for setting in active_settings:
frappe.db.set_value('E-Taxes Settings', setting.name, 'is_active', 0)
# Проверка уникальности VAT Account Mappings
self.validate_vat_account_mappings()
def on_update(self):
self._sync_registry_statuses()
def _sync_registry_statuses(self):
"""Reset registry record statuses when their rows are removed from mapping tables."""
self._reset_orphaned(
mapped_names={row.etaxes_customer_name for row in self.customer_mappings if row.etaxes_customer_name},
doctype="E-Taxes Customers",
name_field="etaxes_party_name",
reset_fields={"status": "New", "mapped_customer": None,
"customer_group": None, "territory": None, "payment_terms": None},
)
self._reset_orphaned(
mapped_names={row.etaxes_supplier_name for row in self.supplier_mappings if row.etaxes_supplier_name},
doctype="E-Taxes Suppliers",
name_field="etaxes_party_name",
reset_fields={"status": "New", "mapped_supplier": None,
"supplier_group": None, "payment_terms": None},
)
self._reset_orphaned(
mapped_names={row.etaxes_item_name for row in self.item_mappings if row.etaxes_item_name},
doctype="E-Taxes Item",
name_field="etaxes_item_name",
reset_fields={"status": "New", "mapped_item": None,
"item_group": None, "uom": None, "item_tax_template": None},
)
self._reset_orphaned(
mapped_names={row.etaxes_unit_name for row in self.unit_mappings if row.etaxes_unit_name},
doctype="E-Taxes Unit",
name_field="etaxes_unit_name",
reset_fields={"status": "New", "mapped_unit": None},
)
def _reset_orphaned(self, mapped_names, doctype, name_field, reset_fields):
orphaned = frappe.get_all(doctype, filters={"status": "Mapped"}, fields=["name", name_field])
for record in orphaned:
if record.get(name_field) not in mapped_names:
frappe.db.set_value(doctype, record.name, reset_fields, update_modified=False)
def validate_vat_account_mappings(self):
"""
Проверка уникальности комбинаций operation_type + expense_income + classification_code

View File

@ -0,0 +1 @@
invoice_az.patches.v1_0.migrate_etaxes_settings_to_single

View File

View File

View File

@ -0,0 +1,90 @@
"""
Migrate E-Taxes Settings from multi-record to Single doctype.
Before this patch the doctype had multiple records (with is_active/is_default flags).
After converting to issingle=1 the data must live in tabSingles and child tables
must have parent = "E-Taxes Settings".
"""
import frappe
def execute():
# Nothing to do if the old table doesn't exist (fresh install)
if not frappe.db.table_exists("tabE-Taxes Settings"):
return
# Find the best old record: prefer is_default=1, then is_active=1, then any
old_name = (
frappe.db.get_value("E-Taxes Settings", {"is_default": 1}, "name", ignore=True)
or frappe.db.get_value("E-Taxes Settings", {"is_active": 1}, "name", ignore=True)
or frappe.db.get_value("E-Taxes Settings", {}, "name", ignore=True)
)
if not old_name or old_name == "E-Taxes Settings":
# Already migrated or nothing to migrate
_migrate_child_tables(old_name)
return
# Read the old record as raw dict
old_row = frappe.db.get_value(
"E-Taxes Settings",
old_name,
[
"similarity_threshold",
"consider_azeri_chars",
"default_item_group",
"default_uom",
"default_item_tax_template",
"default_supplier_group",
"default_customer_group",
"default_payment_terms",
],
as_dict=True,
ignore=True,
)
if not old_row:
return
# Write scalar fields into the Single record
settings = frappe.get_single("E-Taxes Settings")
for field, value in old_row.items():
if value is not None:
setattr(settings, field, value)
# Migrate child tables: update parent to "E-Taxes Settings"
_migrate_child_tables(old_name)
# Save the single record (scalar fields only — child rows are already re-parented)
settings.flags.ignore_validate = True
settings.save(ignore_permissions=True)
frappe.db.commit()
def _migrate_child_tables(old_name):
"""Re-parent child table rows from the old record name to 'E-Taxes Settings'."""
if not old_name or old_name == "E-Taxes Settings":
return
child_tables = [
"tabE-Taxes Item Mapping",
"tabE-Taxes Customer Mappings",
"tabE-Taxes Supplier Mappings",
"tabE-Taxes Unit Mapping",
"tabE-Taxes VAT Account Mapping",
]
for table in child_tables:
if frappe.db.table_exists(table):
frappe.db.sql(
f"""
UPDATE `{table}`
SET parent = 'E-Taxes Settings',
parenttype = 'E-Taxes Settings'
WHERE parent = %s
AND parenttype = 'E-Taxes Settings'
""",
(old_name,),
)

View File

@ -8,15 +8,7 @@ import re
@frappe.whitelist()
def get_active_settings():
"""Getting active E-Taxes settings"""
settings_list = frappe.get_all('E-Taxes Settings',
filters={'is_active': 1},
order_by='creation desc',
limit=1)
if not settings_list:
return None
return frappe.get_doc('E-Taxes Settings', settings_list[0].name)
return frappe.get_single('E-Taxes Settings')
@frappe.whitelist()
def record_etaxes_activity(asan_login_name=None):

View File

@ -552,16 +552,7 @@ def get_accounts_for_operation_type(operation_type, company, expense_income_type
tuple: (debit_account, credit_account) или (None, None) если маппинг не найден
"""
try:
# Ищем дефолтные E-Taxes Settings
settings_name = frappe.db.get_value('E-Taxes Settings',
{'is_default': 1, 'is_active': 1},
'name')
if not settings_name:
# Если нет дефолтных, берем любые активные
settings_name = frappe.db.get_value('E-Taxes Settings',
{'is_active': 1},
'name')
settings_name = 'E-Taxes Settings'
if settings_name and operation_type:
# СТРОГОЕ СООТВЕТСТВИЕ: ищем в зависимости от наличия tax_code
@ -709,15 +700,7 @@ def create_journal_entry_from_vat_operation(operation_data, company, create_as_d
# Получаем настройки E-Taxes для маппингов
settings_doc = None
try:
settings_name = frappe.db.get_value('E-Taxes Settings',
{'is_default': 1, 'is_active': 1},
'name')
if not settings_name:
settings_name = frappe.db.get_value('E-Taxes Settings',
{'is_active': 1},
'name')
if settings_name:
settings_doc = frappe.get_doc('E-Taxes Settings', settings_name)
settings_doc = frappe.get_single('E-Taxes Settings')
except Exception as e:
frappe.logger().warning(f"[VAT] Could not load E-Taxes Settings: {str(e)}")