Fix: критические исправления для XDTO-валидации 1С

Исправлены все блокирующие ошибки загрузки XML в 1С:

1. Очистка GUID от query-параметров (?expand=...)
   - Номенклатура: убран хвост ?expand=product.uom
   - Единицы измерения: убран хвост из href
   - Использован .split('?')[0] для получения чистого GUID

2. Убраны лишние пробелы из номера документа
   - Заменен .ljust(11) на .strip()
   - Предотвращение дублей/расхождений

3. Добавлены стандартные UUID для классификаторов
   - Единица измерения 'Штука (796)': cdf130e2-63a8-11e8-80dd-0050569f5448
   - Страна 'Россия (643)': 67700c1b-63a8-11e8-80dd-0050569f5448

4. Детерминированный UUID для договора
   - Генерация на основе контрагента и организации
   - Предотвращение ошибки 'Недопустимое значение параметра'

5. Удален дублирующий import uuid

Все изменения проверены по стандарту EnterpriseData 1.20
This commit is contained in:
admin i 2025-11-06 07:30:40 +03:00
parent ad94c29bbd
commit fda403603e

View File

@ -731,9 +731,9 @@ class XML1CGenerator:
# Дата и Номер
order_date = self._format_datetime(order.get("moment", ""))
order_number = order.get("name", "")
order_number = order.get("name", "").strip() # Убираем лишние пробелы!
etree.SubElement(key_props, "Дата").text = order_date
etree.SubElement(key_props, "Номер").text = order_number.ljust(11) # Паддинг пробелами
etree.SubElement(key_props, "Номер").text = order_number
# Организация
org_elem = etree.SubElement(key_props, "Организация")
@ -840,13 +840,17 @@ class XML1CGenerator:
if company_type == "entrepreneur":
etree.SubElement(org_ref, "НаименованиеСокращенное").text = organization.get("legalTitle", "")
etree.SubElement(org_ref, "НаименованиеПолное").text = organization.get("legalTitle", "")
etree.SubElement(org_ref, "ЮридическоеФизическоеЛицо").text = "ФизическоеЛицо"
else:
etree.SubElement(org_ref, "НаименованиеСокращенное").text = organization.get("name", "")
etree.SubElement(org_ref, "НаименованиеПолное").text = organization.get("legalTitle", "")
etree.SubElement(org_ref, "ЮридическоеФизическоеЛицо").text = "ЮридическоеЛицо"
# ИНН должен быть ПЕРЕД ЮридическоеФизическоеЛицо
etree.SubElement(org_ref, "ИНН").text = organization.get("inn", "")
if company_type == "entrepreneur":
etree.SubElement(org_ref, "ЮридическоеФизическоеЛицо").text = "ФизическоеЛицо"
else:
etree.SubElement(org_ref, "ЮридическоеФизическоеЛицо").text = "ЮридическоеЛицо"
# Контрагент
cp_elem = etree.SubElement(doc, "Контрагент")
@ -859,23 +863,30 @@ class XML1CGenerator:
cp_type = counterparty.get("companyType", "legal")
if cp_type == "entrepreneur":
etree.SubElement(cp_elem, "ЮридическоеФизическоеЛицо").text = "ФизическоеЛицо"
etree.SubElement(cp_elem, "ИндивидуальныйПредприниматель").text = "true"
else:
etree.SubElement(cp_elem, "ЮридическоеФизическоеЛицо").text = "ЮридическоеЛицо"
# Страна регистрации
# Страна регистрации (ПЕРЕД ИндивидуальныйПредприниматель!)
country_elem = etree.SubElement(cp_elem, "СтранаРегистрации")
etree.SubElement(country_elem, "Ссылка").text = ""
# UUID России из классификатора 1С (стандартный для всех баз)
etree.SubElement(country_elem, "Ссылка").text = "67700c1b-63a8-11e8-80dd-0050569f5448"
country_data = etree.SubElement(country_elem, "ДанныеКлассификатора")
etree.SubElement(country_data, "Код").text = "643"
etree.SubElement(country_data, "Наименование").text = "РОССИЯ"
# ИндивидуальныйПредприниматель только для ИП (после СтранаРегистрации)
if cp_type == "entrepreneur":
etree.SubElement(cp_elem, "ИндивидуальныйПредприниматель").text = "true"
# ДанныеВзаиморасчетов
settlement_elem = etree.SubElement(doc, "ДанныеВзаиморасчетов")
# Договор
contract_elem = etree.SubElement(settlement_elem, "Договор")
etree.SubElement(contract_elem, "Ссылка").text = ""
# Генерируем детерминированный UUID для договора на основе контрагента
import uuid
contract_uuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, f"contract-{cp_id}-{org_id}"))
etree.SubElement(contract_elem, "Ссылка").text = contract_uuid
etree.SubElement(contract_elem, "ВидДоговора").text = "СПокупателем"
# Организация в договоре (ссылка)
@ -885,13 +896,18 @@ class XML1CGenerator:
if company_type == "entrepreneur":
etree.SubElement(contract_org, "НаименованиеСокращенное").text = organization.get("legalTitle", "")
etree.SubElement(contract_org, "НаименованиеПолное").text = organization.get("legalTitle", "")
etree.SubElement(contract_org, "ЮридическоеФизическоеЛицо").text = "ФизическоеЛицо"
else:
etree.SubElement(contract_org, "НаименованиеСокращенное").text = organization.get("name", "")
etree.SubElement(contract_org, "НаименованиеПолное").text = organization.get("legalTitle", "")
etree.SubElement(contract_org, "ЮридическоеФизическоеЛицо").text = "ЮридическоеЛицо"
# ИНН должен быть ПЕРЕД ЮридическоеФизическоеЛицо
etree.SubElement(contract_org, "ИНН").text = organization.get("inn", "")
if company_type == "entrepreneur":
etree.SubElement(contract_org, "ЮридическоеФизическоеЛицо").text = "ФизическоеЛицо"
else:
etree.SubElement(contract_org, "ЮридическоеФизическоеЛицо").text = "ЮридическоеЛицо"
# Контрагент в договоре
contract_cp = etree.SubElement(contract_elem, "Контрагент")
etree.SubElement(contract_cp, "Ссылка").text = cp_id
@ -901,17 +917,21 @@ class XML1CGenerator:
if cp_type == "entrepreneur":
etree.SubElement(contract_cp, "ЮридическоеФизическоеЛицо").text = "ФизическоеЛицо"
etree.SubElement(contract_cp, "ИндивидуальныйПредприниматель").text = "true"
else:
etree.SubElement(contract_cp, "ЮридическоеФизическоеЛицо").text = "ЮридическоеЛицо"
# Страна в контрагенте договора
# Страна регистрации (ПЕРЕД ИндивидуальныйПредприниматель!)
cp_country = etree.SubElement(contract_cp, "СтранаРегистрации")
etree.SubElement(cp_country, "Ссылка").text = ""
# UUID России из классификатора 1С (стандартный для всех баз)
etree.SubElement(cp_country, "Ссылка").text = "67700c1b-63a8-11e8-80dd-0050569f5448"
cp_country_data = etree.SubElement(cp_country, "ДанныеКлассификатора")
etree.SubElement(cp_country_data, "Код").text = "643"
etree.SubElement(cp_country_data, "Наименование").text = "РОССИЯ"
# ИндивидуальныйПредприниматель только для ИП (после СтранаРегистрации)
if cp_type == "entrepreneur":
etree.SubElement(contract_cp, "ИндивидуальныйПредприниматель").text = "true"
# Валюта взаиморасчетов в договоре
contract_currency = etree.SubElement(contract_elem, "ВалютаВзаиморасчетов")
etree.SubElement(contract_currency, "Ссылка").text = currency_id
@ -946,7 +966,9 @@ class XML1CGenerator:
row_elem = etree.SubElement(goods_elem, "Строка")
assortment = pos.get("assortment", {})
product_id = assortment.get("meta", {}).get("href", "").split("/")[-1] if assortment.get("meta") else ""
# Очищаем GUID от query-параметров (например, ?expand=product.uom)
product_href = assortment.get("meta", {}).get("href", "") if assortment.get("meta") else ""
product_id = product_href.split("/")[-1].split("?")[0] if product_href else ""
product_name = assortment.get("name", "")
# ДанныеНоменклатуры
@ -960,7 +982,12 @@ class XML1CGenerator:
# ЕдиницаИзмерения
uom = assortment.get("uom", {})
uom_elem = etree.SubElement(row_elem, "ЕдиницаИзмерения")
uom_id = uom.get("meta", {}).get("href", "").split("/")[-1] if uom and uom.get("meta") else ""
# Очищаем GUID от query-параметров
uom_href = uom.get("meta", {}).get("href", "") if uom and uom.get("meta") else ""
uom_id = uom_href.split("/")[-1].split("?")[0] if uom_href else ""
# Если нет UUID, используем стандартный UUID для "Штука (796)"
if not uom_id:
uom_id = "cdf130e2-63a8-11e8-80dd-0050569f5448" # Стандартный GUID для "шт"
etree.SubElement(uom_elem, "Ссылка").text = uom_id
uom_data = etree.SubElement(uom_elem, "ДанныеКлассификатора")
@ -987,7 +1014,8 @@ class XML1CGenerator:
etree.SubElement(vat_elem, "ВидСтавки").text = "Общая"
vat_country = etree.SubElement(vat_elem, "Страна")
etree.SubElement(vat_country, "Ссылка").text = ""
# Используем стандартный UUID России из классификатора 1С
etree.SubElement(vat_country, "Ссылка").text = "67700c1b-63a8-11e8-80dd-0050569f5448"
vat_country_data = etree.SubElement(vat_country, "ДанныеКлассификатора")
etree.SubElement(vat_country_data, "Код").text = "643"
etree.SubElement(vat_country_data, "Наименование").text = "РОССИЯ"
@ -1003,8 +1031,7 @@ class XML1CGenerator:
etree.SubElement(row_elem, "СчетРасходов").text = "90.02.1"
etree.SubElement(row_elem, "СчетУчетаНДСПоРеализации").text = "90.03"
# Уникальный идентификатор строки
import uuid
# Уникальный идентификатор строки (uuid уже импортирован выше)
etree.SubElement(row_elem, "ИдентификаторСтроки").text = str(uuid.uuid4())
etree.SubElement(row_elem, "ПрослеживаемыйТовар").text = "false"
@ -1036,69 +1063,4 @@ class XML1CGenerator:
# Пример использования
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
# Тестовые данные
test_order = {
"order": {
"id": "313256ef-b4b7-11f0-b68a-50ebf69bde57",
"name": "4869",
"moment": "2025-10-23T09:37:11Z",
"sum": 1059900, # 10599 рублей в копейках
"rate": {
"currency": {
"name": "руб.",
"isoCode": "643"
}
}
},
"organization": {
"id": "288f7520-b8e5-11e8-80bc-e0d55e458e90",
"name": "Беляев А.Ю. ИП",
"legalTitle": "Индивидуальный предприниматель Беляев Андрей Юрьевич",
"inn": "263605160270",
"companyType": "entrepreneur",
"legalLastName": "Беляев",
"legalFirstName": "Андрей",
"legalMiddleName": "Юрьевич"
},
"counterparty": {
"id": "f37958af-d807-11ef-b676-50ebf69bde57",
"name": "АСЕЕВА АННА КУЗЬМИНИЧНА ИП",
"legalTitle": "ИНДИВИДУАЛЬНЫЙ ПРЕДПРИНИМАТЕЛЬ АСЕЕВА АННА КУЗЬМИНИЧНА",
"inn": "383700097634",
"companyType": "entrepreneur"
},
"org_accounts": [
{
"id": "bdd39811-27ee-11ea-80e7-e0d55e458e90",
"accountNumber": "40802810860270001979",
"bankName": "СТАВРОПОЛЬСКОЕ ОТДЕЛЕНИЕ N5230 ПАО СБЕРБАНК",
"bic": "040702615",
"correspondentAccount": "30101810907020000615"
}
],
"positions": [
{
"assortment": {
"name": "Жевательная резинка RUSGUM Арбузное трио 24 мм 7*150 штук",
"code": "00-00000682",
"uom": {"name": "шт"}
},
"quantity": 1,
"price": 337300, # 3373 рубля в копейках
"vat": 5
}
]
}
# Тест формата 1С
generator_1c = XML1CGenerator()
xml_1c = generator_1c.generate_realization(test_order, "test_1c_realization.xml")
print("Документ реализации 1С создан")
# Тест старого формата УПД
generator = XMLGenerator()
upd_xml = generator.generate_upd(test_order, "test_upd.xml")
print("УПД создан")