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:
parent
ad94c29bbd
commit
fda403603e
@ -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("УПД создан")
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user