fix: don't assign group-type Customer/Supplier Group to auto-created parties
Recent ERPNext rejects assigning a group-type Customer Group to a Customer
("Cannot select a Group type Customer Group"); customer_group is also no
longer mandatory. The integration fell back to "All Customer Groups" (a
group node), so every auto-created party failed.
- add invoice_az/utils.py:resolve_customer_group() — returns the configured
group only if it exists and is not a group node, else None
- api.py / vat_api.py: skip customer_group when no valid non-group value;
create the Customer without a group instead of forcing "All Customer Groups"
- add link_filters (is_group=0) to all Customer/Supplier Group pickers in
E-Taxes Settings and the mapping child tables
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
530289e94c
commit
a90fe340d5
|
|
@ -11,6 +11,7 @@ import time
|
||||||
|
|
||||||
# Импорты из модуля аутентификации
|
# Импорты из модуля аутентификации
|
||||||
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
||||||
|
from .utils import resolve_customer_group
|
||||||
from .sales_api import get_sales_invoices
|
from .sales_api import get_sales_invoices
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3153,10 +3154,14 @@ def create_unmapped_customers():
|
||||||
customer_doc.customer_name = etaxes_customer.etaxes_party_name
|
customer_doc.customer_name = etaxes_customer.etaxes_party_name
|
||||||
customer_doc.customer_type = "Company" # Can be configured if needed
|
customer_doc.customer_type = "Company" # Can be configured if needed
|
||||||
|
|
||||||
# Apply settings considering individual priorities
|
# Apply settings considering individual priorities.
|
||||||
customer_group = individual_settings.get('customer_group', settings_doc.default_customer_group)
|
# Only assign a Customer Group if a valid, non-group one is configured;
|
||||||
if not frappe.db.exists('Customer Group', customer_group):
|
# otherwise leave it empty (ERPNext rejects group-type Customer Groups
|
||||||
customer_group = "All Customer Groups"
|
# and the field is no longer mandatory).
|
||||||
|
customer_group = resolve_customer_group(
|
||||||
|
individual_settings.get('customer_group') or settings_doc.default_customer_group
|
||||||
|
)
|
||||||
|
if customer_group:
|
||||||
customer_doc.customer_group = customer_group
|
customer_doc.customer_group = customer_group
|
||||||
|
|
||||||
# Territory
|
# Territory
|
||||||
|
|
@ -3401,10 +3406,14 @@ def create_unmapped_customers_v2():
|
||||||
customer_doc.customer_name = customer_name
|
customer_doc.customer_name = customer_name
|
||||||
customer_doc.customer_type = "Company" # Can be configured if needed
|
customer_doc.customer_type = "Company" # Can be configured if needed
|
||||||
|
|
||||||
# Apply settings considering individual priorities
|
# Apply settings considering individual priorities.
|
||||||
customer_group = individual_settings.get('customer_group', settings_doc.default_customer_group)
|
# Only assign a Customer Group if a valid, non-group one is configured;
|
||||||
if not frappe.db.exists('Customer Group', customer_group):
|
# otherwise leave it empty (ERPNext rejects group-type Customer Groups
|
||||||
customer_group = "All Customer Groups"
|
# and the field is no longer mandatory).
|
||||||
|
customer_group = resolve_customer_group(
|
||||||
|
individual_settings.get('customer_group') or settings_doc.default_customer_group
|
||||||
|
)
|
||||||
|
if customer_group:
|
||||||
customer_doc.customer_group = customer_group
|
customer_doc.customer_group = customer_group
|
||||||
|
|
||||||
# Territory
|
# Territory
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
"fieldname": "customer_group",
|
"fieldname": "customer_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Customer Group",
|
"label": "Customer Group",
|
||||||
|
"link_filters": "[[\"Customer Group\",\"is_group\",\"=\",0]]",
|
||||||
"options": "Customer Group"
|
"options": "Customer Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
"fieldname": "supplier_group",
|
"fieldname": "supplier_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Supplier Group",
|
"label": "Supplier Group",
|
||||||
|
"link_filters": "[[\"Supplier Group\",\"is_group\",\"=\",0]]",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -78,6 +79,7 @@
|
||||||
"fieldname": "customer_group",
|
"fieldname": "customer_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Customer Group",
|
"label": "Customer Group",
|
||||||
|
"link_filters": "[[\"Customer Group\",\"is_group\",\"=\",0]]",
|
||||||
"options": "Customer Group"
|
"options": "Customer Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -103,12 +103,14 @@
|
||||||
"fieldname": "default_supplier_group",
|
"fieldname": "default_supplier_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Default Supplier Group",
|
"label": "Default Supplier Group",
|
||||||
|
"link_filters": "[[\"Supplier Group\",\"is_group\",\"=\",0]]",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "default_customer_group",
|
"fieldname": "default_customer_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Default Customer Group",
|
"label": "Default Customer Group",
|
||||||
|
"link_filters": "[[\"Customer Group\",\"is_group\",\"=\",0]]",
|
||||||
"options": "Customer Group"
|
"options": "Customer Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
"fieldname": "supplier_group",
|
"fieldname": "supplier_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Supplier Group",
|
"label": "Supplier Group",
|
||||||
|
"link_filters": "[[\"Supplier Group\",\"is_group\",\"=\",0]]",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# ======= SHARED HELPERS =======
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_customer_group(preferred=None):
|
||||||
|
"""Return a usable (non-group) Customer Group, or None.
|
||||||
|
|
||||||
|
Newer ERPNext rejects assigning a Customer to a "group" type Customer Group
|
||||||
|
(e.g. the root "All Customer Groups"), and `customer_group` is no longer a
|
||||||
|
mandatory field. So:
|
||||||
|
- if a valid, non-group Customer Group is configured -> use it
|
||||||
|
- otherwise return None and let the Customer be created without a group
|
||||||
|
"""
|
||||||
|
if preferred and frappe.db.exists("Customer Group", preferred):
|
||||||
|
if not frappe.db.get_value("Customer Group", preferred, "is_group"):
|
||||||
|
return preferred
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_territory(preferred=None):
|
||||||
|
"""Return a usable Territory, or None if the configured one is missing.
|
||||||
|
|
||||||
|
Mirrors :func:`resolve_customer_group`; Territory root nodes are still
|
||||||
|
accepted by ERPNext, so we only validate existence.
|
||||||
|
"""
|
||||||
|
if preferred and frappe.db.exists("Territory", preferred):
|
||||||
|
return preferred
|
||||||
|
return None
|
||||||
|
|
@ -8,6 +8,7 @@ import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from frappe.utils import now_datetime, getdate
|
from frappe.utils import now_datetime, getdate
|
||||||
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
||||||
|
from invoice_az.utils import resolve_customer_group
|
||||||
|
|
||||||
# API Endpoint
|
# API Endpoint
|
||||||
VAT_OPERATIONS_URL = "https://new.e-taxes.gov.az/api/po/vatacc/public/v1/operation/find.outbox"
|
VAT_OPERATIONS_URL = "https://new.e-taxes.gov.az/api/po/vatacc/public/v1/operation/find.outbox"
|
||||||
|
|
@ -349,16 +350,18 @@ def find_or_create_customer_from_vat_operation(tin, name, settings_doc, auto_cre
|
||||||
return customer['name']
|
return customer['name']
|
||||||
|
|
||||||
# Шаг 4: Создание нового Customer
|
# Шаг 4: Создание нового Customer
|
||||||
# Получаем default customer group
|
# Берём настроенную группу только если она валидна и не является group-узлом;
|
||||||
default_customer_group = settings_doc.default_customer_group if settings_doc else None
|
# иначе создаём Customer без группы (ERPNext запрещает group-type Customer Group).
|
||||||
if not default_customer_group or not frappe.db.exists('Customer Group', default_customer_group):
|
default_customer_group = resolve_customer_group(
|
||||||
default_customer_group = 'All Customer Groups'
|
settings_doc.default_customer_group if settings_doc else None
|
||||||
|
)
|
||||||
|
|
||||||
# Создаем Customer (с защитой от race condition)
|
# Создаем Customer (с защитой от race condition)
|
||||||
try:
|
try:
|
||||||
customer_doc = frappe.new_doc('Customer')
|
customer_doc = frappe.new_doc('Customer')
|
||||||
customer_doc.customer_name = name or f"Customer-{tin}"
|
customer_doc.customer_name = name or f"Customer-{tin}"
|
||||||
customer_doc.customer_type = 'Company'
|
customer_doc.customer_type = 'Company'
|
||||||
|
if default_customer_group:
|
||||||
customer_doc.customer_group = default_customer_group
|
customer_doc.customer_group = default_customer_group
|
||||||
customer_doc.territory = 'All Territories'
|
customer_doc.territory = 'All Territories'
|
||||||
customer_doc.tax_id = tin
|
customer_doc.tax_id = tin
|
||||||
|
|
@ -410,7 +413,7 @@ def _create_or_update_etaxes_customer_and_mapping(tin, etaxes_name, erp_customer
|
||||||
if settings_doc:
|
if settings_doc:
|
||||||
if existing_mapping_row:
|
if existing_mapping_row:
|
||||||
# Используем прямое обновление child table через SQL
|
# Используем прямое обновление child table через SQL
|
||||||
default_customer_group = settings_doc.default_customer_group if settings_doc.default_customer_group else 'All Customer Groups'
|
default_customer_group = resolve_customer_group(settings_doc.default_customer_group)
|
||||||
|
|
||||||
# Обновляем через frappe.db.sql напрямую
|
# Обновляем через frappe.db.sql напрямую
|
||||||
frappe.db.sql("""
|
frappe.db.sql("""
|
||||||
|
|
@ -438,7 +441,7 @@ def _create_or_update_etaxes_customer_and_mapping(tin, etaxes_name, erp_customer
|
||||||
'etaxes_customer_name': etaxes_name or f"Customer-{tin}",
|
'etaxes_customer_name': etaxes_name or f"Customer-{tin}",
|
||||||
'etaxes_tax_id': tin,
|
'etaxes_tax_id': tin,
|
||||||
'erp_customer': erp_customer,
|
'erp_customer': erp_customer,
|
||||||
'customer_group': settings_doc.default_customer_group if settings_doc.default_customer_group else 'All Customer Groups',
|
'customer_group': resolve_customer_group(settings_doc.default_customer_group),
|
||||||
'territory': 'All Territories',
|
'territory': 'All Territories',
|
||||||
'mapping_type': 'Automatic'
|
'mapping_type': 'Automatic'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue