Compare commits

..

12 Commits

14 changed files with 4661 additions and 2335 deletions

View File

@ -1,88 +0,0 @@
{
"meta": {
"source": "Таблица основных типов ISO-контейнеров (внешние/внутренние размеры, дверной проём, масса, объём)",
"usage": "Сверка п.21 FCL (проходимость груза в дверной проём 20DV, 40DV, 40HC) и оценка payload/объёма."
},
"types": {
"20DV": {
"label": "20' Standard (Dry Cube) — соответствует 20DV в критериях",
"external_mm": { "length": 6058, "width": 2438, "height": 2591 },
"internal_mm": { "length": 5905, "width": 2350, "height": 2381 },
"door_opening_mm": { "width": 2336, "height": 2291 },
"max_gross_kg": 30480,
"tare_kg": 2370,
"payload_kg": 28110,
"volume_m3": 33.2
},
"20HC": {
"label": "20' High Cube",
"external_mm": { "length": 6058, "width": 2438, "height": 2896 },
"internal_mm": { "length": 5905, "width": 2350, "height": 2693 },
"door_opening_mm": { "width": 2340, "height": 2597 },
"max_gross_kg": 30480,
"tare_kg": 2340,
"payload_kg": 28140,
"volume_m3": 37.5
},
"20HC_PW": {
"label": "20' High Cube Pallet Wide",
"external_mm": { "length": 6058, "width": 2484, "height": 2896 },
"internal_mm": { "length": 5898, "width": 2426, "height": 2698 },
"door_opening_mm": { "width": 2374, "height": 2585 },
"max_gross_kg": 30480,
"tare_kg": 2580,
"payload_kg": 27900,
"volume_m3": 38.6
},
"40DV": {
"label": "40' Standard (Dry Van) — соответствует 40DV в критериях",
"external_mm": { "length": 12192, "width": 2438, "height": 2591 },
"internal_mm": { "length": 12039, "width": 2350, "height": 2372 },
"door_opening_mm": { "width": 2336, "height": 2291 },
"max_gross_kg": 30480,
"tare_kg": 4000,
"payload_kg": 26480,
"volume_m3": 67.8
},
"40HC": {
"label": "40' High Cube — соответствует 40HC в критериях",
"external_mm": { "length": 12192, "width": 2438, "height": 2896 },
"internal_mm": { "length": 12039, "width": 2350, "height": 2693 },
"door_opening_mm": { "width": 2340, "height": 2597 },
"max_gross_kg": 32500,
"tare_kg": 4200,
"payload_kg": 28300,
"volume_m3": 76.5
},
"40HC_PW": {
"label": "40' High Cube Pallet Wide",
"external_mm": { "length": 12192, "width": 2500, "height": 2896 },
"internal_mm": { "length": 12039, "width": 2432, "height": 2693 },
"door_opening_mm": { "width": 2432, "height": 2597 },
"max_gross_kg": 35000,
"tare_kg": 4400,
"payload_kg": 30600,
"volume_m3": 79.3
},
"45HC_PW_A": {
"label": "45' High Cube Pallet Wide (Type A)",
"external_mm": { "length": 13716, "width": 2500, "height": 2750 },
"internal_mm": { "length": 13513, "width": 2444, "height": 2549 },
"door_opening_mm": { "width": 2416, "height": 2439 },
"max_gross_kg": 34000,
"tare_kg": 4180,
"payload_kg": 29820,
"volume_m3": 85.1
},
"45HC_PW_B": {
"label": "45' High Cube Pallet Wide (Type B)",
"external_mm": { "length": 13716, "width": 2500, "height": 2896 },
"internal_mm": { "length": 13513, "width": 2444, "height": 2670 },
"door_opening_mm": { "width": 2416, "height": 2580 },
"max_gross_kg": 34000,
"tare_kg": 5080,
"payload_kg": 28920,
"volume_m3": 89.5
}
}
}

View File

@ -416,7 +416,7 @@
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"num": "18",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
@ -483,17 +483,17 @@
{
"type": "pair",
"left": {
"num": "18",
"num": "19",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "19",
"num": "20",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
},
{
"type": "left_12",
"num": "20",
"num": "21",
"label": "Превышение габаритов дверного проема контейнеров 20DV, 40DV, 40HC"
}
]
@ -508,7 +508,7 @@
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"num": "18",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
@ -575,17 +575,17 @@
{
"type": "pair",
"left": {
"num": "18",
"num": "19",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "19",
"num": "20",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
},
{
"type": "left_12",
"num": "20",
"num": "21",
"label": "Превышение габаритов дверного проема контейнеров 20DV, 40DV, 40HC"
}
]
@ -600,7 +600,7 @@
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"num": "18",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
@ -667,17 +667,17 @@
{
"type": "pair",
"left": {
"num": "18",
"num": "19",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "19",
"num": "20",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
},
{
"type": "left_12",
"num": "20",
"num": "21",
"label": "Превышение габаритов дверного проема контейнеров 20DV, 40DV, 40HC"
}
]
@ -692,7 +692,7 @@
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"num": "21",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},

View File

@ -0,0 +1,801 @@
{
"version": 1,
"source_xlsx": "Запрос для ИИ Общий(3).xlsx",
"by_shipping_type": {
"Автомобильная перевозка (LTL)": {
"sheet": "Авто LTL интерфейс",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество грузовых мест и вес, объем"
},
"right": {
"num": "7",
"label": "Габариты грузовых мест ДхШхВ"
}
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Штабелирвоание с другими отправками"
},
"right": {
"num": "11",
"label": "Штабелирвоание между собой"
}
},
{
"type": "pair",
"left": {
"num": "9",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "13",
"label": "MSDS"
},
"right": {
"num": "14",
"label": "DGM"
}
},
{
"type": "pair",
"left": {
"num": "15",
"label": "Название бренда"
},
"right": {
"num": "16",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "17",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "18",
"label": "Требуется ли замена документов"
}
}
]
},
"Автомобильная перевозка (FTL)": {
"sheet": "Авто FTL интерфейс",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер машин"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "left_12",
"num": "8",
"label": "Габариты грузовых мест ДхШхВ"
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "11",
"label": "MSDS"
},
"right": {
"num": "13",
"label": "DGM"
}
},
{
"type": "pair",
"left": {
"num": "14",
"label": "Название бренда"
},
"right": {
"num": "15",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "16",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "17",
"label": "Требуется ли замена документов"
}
}
]
},
"Морская перевозка (LCL)": {
"sheet": "Море, море+жд, жд LCL интерфейc",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер машин"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Штабелирвоание с другими отправками"
},
"right": {
"num": "11",
"label": "Штабелирвоание между собой"
}
},
{
"type": "pair",
"left": {
"num": "9",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "13",
"label": "MSDS"
},
"right": {
"num": "14",
"label": "DGM"
}
},
{
"type": "pair",
"left": {
"num": "15",
"label": "Название бренда"
},
"right": {
"num": "16",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "17",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "18",
"label": "Требуется ли замена документов"
}
}
]
},
"Железнодорожная перевозка (LCL)": {
"sheet": "Море, море+жд, жд LCL интерфейc",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер машин"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Штабелирвоание с другими отправками"
},
"right": {
"num": "11",
"label": "Штабелирвоание между собой"
}
},
{
"type": "pair",
"left": {
"num": "9",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "13",
"label": "MSDS"
},
"right": {
"num": "14",
"label": "DGM"
}
},
{
"type": "pair",
"left": {
"num": "15",
"label": "Название бренда"
},
"right": {
"num": "16",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "17",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "18",
"label": "Требуется ли замена документов"
}
}
]
},
"Мультимодальная перевозка море + ж/д (LCL)": {
"sheet": "Море, море+жд, жд LCL интерфейc",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер машин"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Штабелирвоание с другими отправками"
},
"right": {
"num": "11",
"label": "Штабелирвоание между собой"
}
},
{
"type": "pair",
"left": {
"num": "9",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "13",
"label": "MSDS"
},
"right": {
"num": "14",
"label": "DGM"
}
},
{
"type": "pair",
"left": {
"num": "15",
"label": "Название бренда"
},
"right": {
"num": "16",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "17",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "18",
"label": "Требуется ли замена документов"
}
}
]
},
"Морская перевозка (FCL)": {
"sheet": "Море, море+жд, жд FCL интерфейc",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "18",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер контейнеров"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "left_12",
"num": "8",
"label": "Габариты грузовых мест ДхШхВ"
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "11",
"label": "MSDS"
},
"right": {
"num": "13",
"label": "Technical Description of Goods in Railway Transport"
}
},
{
"type": "pair",
"left": {
"num": "14",
"label": "Название бренда"
},
"right": {
"num": "15",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "16",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "17",
"label": "Требуется ли замена документов"
}
},
{
"type": "pair",
"left": {
"num": "19",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "20",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
},
{
"type": "left_12",
"num": "21",
"label": "Превышение габаритов дверного проема контейнеров 20DV, 40DV, 40HC"
}
]
},
"Железнодорожная перевозка (FCL)": {
"sheet": "Море, море+жд, жд FCL интерфейc",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "18",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер контейнеров"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "left_12",
"num": "8",
"label": "Габариты грузовых мест ДхШхВ"
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "11",
"label": "MSDS"
},
"right": {
"num": "13",
"label": "Technical Description of Goods in Railway Transport"
}
},
{
"type": "pair",
"left": {
"num": "14",
"label": "Название бренда"
},
"right": {
"num": "15",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "16",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "17",
"label": "Требуется ли замена документов"
}
},
{
"type": "pair",
"left": {
"num": "18",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "19",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
},
{
"type": "left_12",
"num": "20",
"label": "Превышение габаритов дверного проема контейнеров 20DV, 40DV, 40HC"
}
]
},
"Мультимодальная перевозка море + ж/д (FCL)": {
"sheet": "Море, море+жд, жд FCL интерфейc",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "19",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество и типоразмер контейнеров"
},
"right": {
"num": "7",
"label": "Количество грузовых мест и вес, объем"
}
},
{
"type": "left_12",
"num": "8",
"label": "Габариты грузовых мест ДхШхВ"
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "12",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "11",
"label": "MSDS"
},
"right": {
"num": "13",
"label": "Technical Description of Goods in Railway Transport"
}
},
{
"type": "pair",
"left": {
"num": "14",
"label": "Название бренда"
},
"right": {
"num": "15",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "16",
"label": "Требуется ли дополнительный сервис - фитоконтроль, иное"
},
"right": {
"num": "17",
"label": "Требуется ли замена документов"
}
},
{
"type": "pair",
"left": {
"num": "18",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "19",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
},
{
"type": "left_12",
"num": "20",
"label": "Превышение габаритов дверного проема контейнеров 20DV, 40DV, 40HC"
}
]
},
"Авиаперевозка": {
"sheet": "Авиа Интерфейс",
"blocks": [
{
"type": "pair",
"left": {
"num": "4",
"label": "Страна, город, адрес забора"
},
"right": {
"num": "21",
"label": "Страна, город, точный адрес доставки, Место ТО в РФ"
}
},
{
"type": "pair",
"left": {
"num": "6",
"label": "Количество грузовых мест и вес, объем"
},
"right": {
"num": "7",
"label": "Габариты грузовых мест ДхШхВ"
}
},
{
"type": "pair",
"left": {
"num": "10",
"label": "Штабелирвоание с другими отправками"
},
"right": {
"num": "11",
"label": "Штабелирвоание между собой"
}
},
{
"type": "pair",
"left": {
"num": "9",
"label": "Наличие батарейк, газов, жидкостей, аэрозолей"
},
"right": {
"num": "13",
"label": "Батарейки в составе груза / отдельно"
}
},
{
"type": "pair",
"left": {
"num": "12",
"label": "MSDS"
},
"right": {
"num": "14",
"label": "DGM"
}
},
{
"type": "pair",
"left": {
"num": "15",
"label": "Название бренда"
},
"right": {
"num": "16",
"label": "Авторизационное письмо/разрешение на вывоз этих брендов из Китая"
}
},
{
"type": "pair",
"left": {
"num": "17",
"label": "Перелет через третью страну со сменой авинаклданых"
},
"right": {
"num": "18",
"label": "Наличие экспортной лицезии у отправителя"
}
},
{
"type": "pair",
"left": {
"num": "19",
"label": "Дополнительный сервис"
},
"right": {
"num": "20",
"label": "Экспедирование в аэропорту назначения"
}
},
{
"type": "pair",
"left": {
"num": "22",
"label": "Спец требования при доставке до адреса в РФ"
},
"right": {
"num": "23",
"label": "Таможенное оформление"
}
},
{
"type": "pair",
"left": {
"num": "24",
"label": "Место таможенного оформления при экспорте из РФ"
},
"right": {
"num": "25",
"label": "Наличие Фумигации на деревянной таре при экспорте из РФ"
}
}
]
}
}
}

View File

@ -1,154 +0,0 @@
from pypdf import PdfReader
from docx import Document
import openpyxl
from typing import Union
import io
import logging
from bs4 import BeautifulSoup
logger = logging.getLogger(__name__)
class DocumentProcessor:
@staticmethod
def _cell_to_text(v) -> str:
if v is None:
return ""
s = str(v).strip()
return s if s else ""
@staticmethod
def _sheet_rows_text_openpyxl(sheet) -> list[str]:
out: list[str] = []
for row in sheet.iter_rows(values_only=True):
vals = [DocumentProcessor._cell_to_text(c) for c in row]
vals = [x for x in vals if x]
if vals:
out.append(" | ".join(vals))
return out
@staticmethod
def _sheet_cols_text_openpyxl(sheet) -> list[str]:
out: list[str] = []
max_col = sheet.max_column or 0
max_row = sheet.max_row or 0
for c in range(1, max_col + 1):
vals: list[str] = []
for r in range(1, max_row + 1):
v = DocumentProcessor._cell_to_text(sheet.cell(r, c).value)
if v:
vals.append(v)
if vals:
col_letter = openpyxl.utils.get_column_letter(c)
out.append(f"COL {col_letter} TOP_DOWN: " + " || ".join(vals))
out.append(f"COL {col_letter} BOTTOM_UP: " + " || ".join(reversed(vals)))
return out
@staticmethod
def _sheet_rows_text_xlrd(sheet) -> list[str]:
out: list[str] = []
for row_idx in range(sheet.nrows):
row = sheet.row_values(row_idx)
vals = [DocumentProcessor._cell_to_text(c) for c in row]
vals = [x for x in vals if x]
if vals:
out.append(" | ".join(vals))
return out
@staticmethod
def _sheet_cols_text_xlrd(sheet) -> list[str]:
out: list[str] = []
for c in range(sheet.ncols):
vals: list[str] = []
for r in range(sheet.nrows):
v = DocumentProcessor._cell_to_text(sheet.cell_value(r, c))
if v:
vals.append(v)
if vals:
out.append(f"COL {c+1} TOP_DOWN: " + " || ".join(vals))
out.append(f"COL {c+1} BOTTOM_UP: " + " || ".join(reversed(vals)))
return out
def normalize_email_html(html_content: str) -> str:
"""
Очищает HTML письма и нормализует таблицы
"""
soup = BeautifulSoup(html_content, "html.parser")
# удаляем скрипты и стили
for tag in soup(["script", "style"]):
tag.decompose()
# нормализуем таблицы
for table in soup.find_all("table"):
table["style"] = "border-collapse: collapse; width: 100%;"
for cell in table.find_all(["td", "th"]):
cell["style"] = "border:1px solid #ccc;padding:6px;"
return str(soup)
def extract_text(self, content: bytes, filename: str) -> str:
ext = filename.lower().split('.')[-1]
# Изображения не обрабатываем
if ext in ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'webp']:
return ""
if ext == 'pdf':
return self._extract_pdf(content)
elif ext in ['docx', 'doc']:
return self._extract_docx(content)
elif ext in ['xlsx', 'xls']:
return self._extract_excel(content)
elif ext == 'txt':
return content.decode('utf-8')
else:
raise ValueError(f"Unsupported format: {ext}")
def _extract_pdf(self, content: bytes) -> str:
pdf = PdfReader(io.BytesIO(content))
return "\n".join(page.extract_text() for page in pdf.pages)
def _extract_docx(self, content: bytes) -> str:
doc = Document(io.BytesIO(content))
return "\n".join(paragraph.text for paragraph in doc.paragraphs)
def _extract_excel(self, content: bytes) -> str:
try:
# сначала пробуем openpyxl (для .xlsx)
wb = openpyxl.load_workbook(io.BytesIO(content))
text = []
for sheet in wb.worksheets:
text.append(f"=== SHEET: {sheet.title} ===")
# Сохраняем совместимость: построчное чтение.
rows_text = self._sheet_rows_text_openpyxl(sheet)
if rows_text:
text.append("[ROWS]")
text.extend(rows_text)
# Новый режим: чтение по колонкам (общие данные часто внизу столбца).
cols_text = self._sheet_cols_text_openpyxl(sheet)
if cols_text:
text.append("[COLUMNS]")
text.extend(cols_text)
return "\n".join(text)
except Exception as e:
# если не получилось, возможно это .xls, пробуем xlrd
try:
import xlrd
workbook = xlrd.open_workbook(file_contents=content)
text = []
for sheet in workbook.sheets():
text.append(f"=== SHEET: {sheet.name} ===")
rows_text = self._sheet_rows_text_xlrd(sheet)
if rows_text:
text.append("[ROWS]")
text.extend(rows_text)
cols_text = self._sheet_cols_text_xlrd(sheet)
if cols_text:
text.append("[COLUMNS]")
text.extend(cols_text)
return "\n".join(text)
except ImportError:
logger.error("xlrd not installed, cannot parse .xls files")
return ""
except Exception as e2:
logger.error(f"Failed to parse Excel with xlrd: {e2}")
return ""

815
document_processor.py Normal file
View File

@ -0,0 +1,815 @@
import io
import os
import re
import csv
import tempfile
import subprocess
import logging
from typing import List
from bs4 import BeautifulSoup
from pypdf import PdfReader
from docx import Document
import openpyxl
import pdfplumber
from PIL import Image
import pytesseract
from striprtf.striprtf import rtf_to_text
logger = logging.getLogger(__name__)
class DocumentProcessor:
# =========================================================
# HELPERS
# =========================================================
@staticmethod
def _cleanup_text(text: str) -> str:
if not text:
return ""
text = text.replace("\xa0", " ")
text = text.replace("\t", " ")
text = re.sub(r"[ \t]+", " ", text)
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()
@staticmethod
def _cell_to_text(v) -> str:
if v is None:
return ""
s = str(v).strip()
return s if s else ""
# =========================================================
# HTML
# =========================================================
@staticmethod
def normalize_email_html(html_content: str) -> str:
"""
Очищает HTML письма и нормализует таблицы
"""
soup = BeautifulSoup(
html_content,
"html.parser"
)
for tag in soup(["script", "style"]):
tag.decompose()
for table in soup.find_all("table"):
table["style"] = (
"border-collapse: collapse; width: 100%;"
)
for cell in table.find_all(["td", "th"]):
cell["style"] = (
"border:1px solid #ccc;padding:6px;"
)
return str(soup)
# =========================================================
# MAIN
# =========================================================
def extract_text(
self,
content: bytes,
filename: str
) -> str:
ext = filename.lower().split(".")[-1]
try:
# =================================================
# PDF
# =================================================
if ext == "pdf":
return self._extract_pdf(content)
# =================================================
# WORD
# =================================================
elif ext in ["docx", "docm"]:
return self._extract_docx(content)
elif ext == "doc":
return self._extract_doc(content)
elif ext == "rtf":
return self._extract_rtf(content)
# =================================================
# EXCEL
# =================================================
elif ext in ["xlsx", "xlsm", "xlsxm"]:
return self._extract_excel_openpyxl(content)
elif ext == "xls":
return self._extract_excel_xls(content)
elif ext == "csv":
return self._extract_csv(content)
# =================================================
# IMAGES / OCR
# =================================================
elif ext in [
"png",
"jpg",
"jpeg",
"gif",
"bmp",
"webp"
]:
return self._extract_image(content)
elif ext in ["tif", "tiff"]:
return self._extract_tiff(content)
# =================================================
# TEXT
# =================================================
elif ext == "txt":
try:
return content.decode("utf-8")
except Exception:
return content.decode(
"cp1251",
errors="ignore"
)
# =================================================
# UNKNOWN
# =================================================
logger.warning(
f"Unsupported attachment format: {ext}"
)
return ""
except Exception:
logger.exception(
f"Attachment processing failed: {filename}"
)
return ""
# =========================================================
# PDF
# =========================================================
def _extract_pdf(
self,
content: bytes
) -> str:
try:
parts = []
with pdfplumber.open(
io.BytesIO(content)
) as pdf:
for page_num, page in enumerate(
pdf.pages,
start=1
):
parts.append(
f"=== PAGE {page_num} ==="
)
# =====================================
# TEXT
# =====================================
try:
text = page.extract_text()
if text:
parts.append(text)
except Exception as e:
logger.warning(
f"PDF text extraction failed: {e}"
)
# =====================================
# TABLES
# =====================================
try:
tables = page.extract_tables()
for table_idx, table in enumerate(
tables,
start=1
):
parts.append(
f"=== TABLE {table_idx} ==="
)
for row in table:
if not row:
continue
cells = []
for cell in row:
if cell is None:
continue
txt = str(cell).strip()
txt = " ".join(
txt.split()
)
if txt:
cells.append(txt)
if cells:
parts.append(
" | ".join(cells)
)
except Exception as e:
logger.warning(
f"PDF table extraction failed: {e}"
)
text = "\n".join(parts)
return self._cleanup_text(text)
except Exception as e:
logger.error(f"PDF parse failed: {e}")
return ""
# =========================================================
# DOCX / DOCM
# =========================================================
def _extract_docx(
self,
content: bytes
) -> str:
try:
doc = Document(io.BytesIO(content))
parts = []
# =============================================
# PARAGRAPHS
# =============================================
for p in doc.paragraphs:
txt = p.text.strip()
if txt:
parts.append(txt)
# =============================================
# TABLES
# =============================================
for table_idx, table in enumerate(
doc.tables
):
parts.append(
f"=== TABLE {table_idx + 1} ==="
)
for row in table.rows:
cells = []
for cell in row.cells:
cell_parts = []
# =================================
# DIRECT CELL TEXT
# =================================
txt = "\n".join(
p.text.strip()
for p in cell.paragraphs
if p.text.strip()
)
txt = " ".join(txt.split())
if txt:
cell_parts.append(txt)
# =================================
# NESTED TABLES
# =================================
for nested_table in cell.tables:
for nested_row in nested_table.rows:
nested_vals = []
for nested_cell in nested_row.cells:
nested_txt = "\n".join(
p.text.strip()
for p in nested_cell.paragraphs
if p.text.strip()
)
nested_txt = " ".join(
nested_txt.split()
)
if nested_txt:
nested_vals.append(
nested_txt
)
if nested_vals:
cell_parts.append(
" || ".join(
nested_vals
)
)
# =================================
# FINAL CELL
# =================================
if cell_parts:
cells.append(
" <<<TABLE>>> ".join(
cell_parts
)
)
if cells:
parts.append(
" | ".join(cells)
)
text = "\n".join(parts)
return self._cleanup_text(text)
except Exception as e:
logger.error(f"DOCX parse failed: {e}")
return ""
# =========================================================
# DOC
# =========================================================
def _extract_doc(
self,
content: bytes
) -> str:
try:
with tempfile.TemporaryDirectory() as tmpdir:
input_path = os.path.join(
tmpdir,
"temp.doc"
)
with open(input_path, "wb") as f:
f.write(content)
subprocess.run(
[
"libreoffice",
"--headless",
"--convert-to",
"docx",
input_path,
"--outdir",
tmpdir,
],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
converted_path = os.path.join(
tmpdir,
"temp.docx"
)
if not os.path.exists(
converted_path
):
logger.error(
"DOC conversion failed"
)
return ""
with open(
converted_path,
"rb"
) as f:
converted_content = f.read()
return self._extract_docx(
converted_content
)
except Exception as e:
logger.error(f"DOC parse failed: {e}")
return ""
# =========================================================
# RTF
# =========================================================
def _extract_rtf(
self,
content: bytes
) -> str:
try:
try:
text = content.decode("utf-8")
except Exception:
text = content.decode(
"cp1251",
errors="ignore"
)
text = rtf_to_text(text)
return self._cleanup_text(text)
except Exception as e:
logger.error(f"RTF parse failed: {e}")
return ""
# =========================================================
# XLSX
# =========================================================
def _extract_excel_openpyxl(
self,
content: bytes
) -> str:
try:
wb = openpyxl.load_workbook(
io.BytesIO(content),
data_only=True
)
text = []
for sheet in wb.worksheets:
text.append(
f"=== SHEET: {sheet.title} ==="
)
# =====================================
# MERGED CELLS
# =====================================
merged_ranges = list(
sheet.merged_cells.ranges
)
if merged_ranges:
text.append(
"=== MERGED CELLS ==="
)
for r in merged_ranges:
text.append(str(r))
# =====================================
# ROWS
# =====================================
text.append("[ROWS]")
for row in sheet.iter_rows(
values_only=True
):
vals = [
self._cell_to_text(c)
for c in row
]
vals = [x for x in vals if x]
if vals:
text.append(
" | ".join(vals)
)
# =====================================
# COLUMNS
# =====================================
text.append("[COLUMNS]")
max_col = sheet.max_column or 0
max_row = sheet.max_row or 0
for c in range(1, max_col + 1):
vals = []
for r in range(1, max_row + 1):
v = self._cell_to_text(
sheet.cell(r, c).value
)
if v:
vals.append(v)
if vals:
col_letter = (
openpyxl.utils
.get_column_letter(c)
)
text.append(
f"COL {col_letter}: "
+ " || ".join(vals)
)
return self._cleanup_text(
"\n".join(text)
)
except Exception as e:
logger.error(
f"XLSX parse failed: {e}"
)
return ""
# =========================================================
# XLS
# =========================================================
def _extract_excel_xls(
self,
content: bytes
) -> str:
try:
import xlrd
workbook = xlrd.open_workbook(
file_contents=content
)
text = []
for sheet in workbook.sheets():
text.append(
f"=== SHEET: {sheet.name} ==="
)
for row_idx in range(
sheet.nrows
):
row = sheet.row_values(
row_idx
)
vals = [
self._cell_to_text(c)
for c in row
]
vals = [x for x in vals if x]
if vals:
text.append(
" | ".join(vals)
)
return self._cleanup_text(
"\n".join(text)
)
except Exception as e:
logger.error(f"XLS parse failed: {e}")
return ""
# =========================================================
# CSV
# =========================================================
def _extract_csv(
self,
content: bytes
) -> str:
try:
try:
text = content.decode("utf-8")
except Exception:
text = content.decode(
"cp1251",
errors="ignore"
)
reader = csv.reader(
io.StringIO(text)
)
rows = []
for row in reader:
vals = [
str(x).strip()
for x in row
if str(x).strip()
]
if vals:
rows.append(
" | ".join(vals)
)
return self._cleanup_text(
"\n".join(rows)
)
except Exception as e:
logger.error(f"CSV parse failed: {e}")
return ""
# =========================================================
# OCR
# =========================================================
def _ocr_image(
self,
image: Image.Image
) -> str:
try:
text = pytesseract.image_to_string(
image,
lang="rus+eng",
config="--psm 6"
)
return self._cleanup_text(text)
except Exception as e:
logger.error(f"OCR failed: {e}")
return ""
# =========================================================
# IMAGE
# =========================================================
def _extract_image(
self,
content: bytes
) -> str:
try:
image = Image.open(
io.BytesIO(content)
)
return self._ocr_image(image)
except Exception as e:
logger.error(
f"Image parse failed: {e}"
)
return ""
# =========================================================
# TIFF
# =========================================================
def _extract_tiff(
self,
content: bytes
) -> str:
try:
image = Image.open(
io.BytesIO(content)
)
texts = []
for frame in range(
getattr(image, "n_frames", 1)
):
image.seek(frame)
texts.append(
self._ocr_image(image)
)
return self._cleanup_text(
"\n".join(texts)
)
except Exception as e:
logger.error(
f"TIFF parse failed: {e}"
)
return ""

View File

@ -39,6 +39,16 @@ def _row_cells(ws, r: int, max_c: int = 12) -> list[tuple[int, str]]:
return out
def _is_num_like(s: str) -> bool:
if not isinstance(s, str):
return False
t = s.strip()
if not t:
return False
# 1, 1., 1.0
return t.isdigit() or t.replace(".", "", 1).isdigit()
def parse_interface_sheet(ws) -> list[dict]:
"""Разбор листа интерфейса в последовательность визуальных блоков."""
blocks: list[dict] = []
@ -50,19 +60,21 @@ def parse_interface_sheet(ws) -> list[dict]:
r += 1
continue
# Заголовок: 7 ячеек в колонках 1,2,3,4,5,7,9 и все значения — «номера пунктов»
if (
len(cells) == 7
and {c[0] for c in cells} == {1, 2, 3, 4, 5, 7, 9}
and all(str(c[1]).strip().isdigit() or c[1].strip().replace(".", "").isdigit() for c in cells)
):
nums = [c[1] for c in sorted(cells, key=lambda x: (x[0] not in (1, 2, 3, 4, 5, 7, 9), x[0]))]
# порядок как в Excel слева направо по колонкам
nums = [c[1] for c in sorted(cells, key=lambda x: x[0])]
# Заголовок верхнего ряда:
# в новых версиях файла набор колонок может отличаться, поэтому
# распознаём динамически: строка с номерами + следующая строка с подписями
# в тех же колонках.
cols = [c[0] for c in cells]
vals = [c[1] for c in cells]
if len(cells) >= 4 and all(_is_num_like(v) for v in vals):
r2 = r + 1
cells2 = _row_cells(ws, r2)
if len(cells2) == 7 and {c[0] for c in cells2} == {1, 2, 3, 4, 5, 7, 9}:
labels = [c[1] for c in sorted(cells2, key=lambda x: x[0])]
map2 = {c: v for c, v in cells2}
if all(c in map2 and str(map2[c]).strip() for c in cols):
labels = [str(map2[c]).strip() for c in cols]
# Если в следующей строке снова только числа — это не шапка.
if not all(_is_num_like(v) for v in labels):
nums = [str(v).strip() for v in vals]
blocks.append({"type": "header", "nums": nums, "labels": labels})
r += 2
continue

View File

@ -1,3 +1,5 @@
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, Response
from typing import List, Optional, Dict
@ -20,7 +22,7 @@ from urllib.parse import quote
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
os.makedirs("/opt/rag-gemini/app/reports", exist_ok=True)
os.makedirs("/opt/nek/app/reports", exist_ok=True)
SHIPPING_TYPES_FILE = os.path.join(os.path.dirname(__file__), "shipping_types.json")
PROCESSED_SHIPPING_TYPES_FILE = os.path.join(
os.path.dirname(__file__), "shipping_types_processed.json"
@ -167,6 +169,22 @@ app.add_middleware(
expose_headers=["Content-Disposition", "Content-Length"]
)
class CollapseDuplicatePathSlashesMiddleware(BaseHTTPMiddleware):
"""Схлопывает повторяющиеся слэши в path (например POST //process-outlook-emails → /process-outlook-emails)."""
async def dispatch(self, request: Request, call_next):
path = request.scope.get("path") or ""
if "//" in path:
collapsed = path
while "//" in collapsed:
collapsed = collapsed.replace("//", "/")
request.scope["path"] = collapsed
return await call_next(request)
app.add_middleware(CollapseDuplicatePathSlashesMiddleware)
class EmailAttachment(BaseModel):
filename: str
@ -207,15 +225,12 @@ class CargoQueryResponse(BaseModel):
class AnalyzeCargoRequest(BaseModel):
email_ids: List[str]
class CargoLearningRequest(BaseModel):
"""Сохранение примера для обучения: переписка + структурированный ответ (как в отчёте)."""
structured_data: dict
session_id: Optional[str] = None
context_preview: Optional[str] = None
notes: Optional[str] = None
@app.post("/record-cargo-learning")
async def record_cargo_learning(req: CargoLearningRequest):
"""

View File

@ -1,92 +1,41 @@
import logging
import os
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, Response
import httpx
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
UPSTREAM_URL = os.getenv(
"LLM_UPSTREAM_URL",
"https://llm.corp.septem.pro/v1/chat/completions",
).strip()
OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY", "").strip()
def _client_timeout() -> httpx.Timeout:
total = float(os.getenv("LLM_PROXY_TIMEOUT_SECONDS", "120"))
connect = float(os.getenv("LLM_PROXY_CONNECT_TIMEOUT", "15"))
return httpx.Timeout(total, connect=connect)
OPENROUTER_KEY = "sk-9cfed30eb6894df5b9a3ffb4b1fb956d"
@app.post("/v1/chat/completions")
async def proxy(req: Request):
if not OPENROUTER_KEY:
logger.error("OPENROUTER_API_KEY is not set (env or .env)")
return JSONResponse(
status_code=503,
content={
"error": "Service unavailable",
"details": "Set OPENROUTER_API_KEY for the LLM proxy (or add it to .env).",
},
)
try:
body = await req.json()
except Exception as e:
return JSONResponse(
status_code=400,
content={"error": "Invalid JSON", "details": str(e)},
)
verify = os.getenv("HTTPX_VERIFY", "true").lower() not in ("0", "false", "no")
async with httpx.AsyncClient(verify=verify) as client:
async with httpx.AsyncClient() as client:
try:
r = await client.post(
UPSTREAM_URL,
"https://api.deepseek.com/chat/completions",
headers={
"Authorization": f"Bearer {OPENROUTER_KEY}",
"HTTP-Referer": os.getenv("LLM_HTTP_REFERER", "http://localhost"),
"X-Title": os.getenv("LLM_PROXY_X_TITLE", "rag-engine"),
"HTTP-Referer": "http://localhost",
"model": "deepseek-v4-flash",
"X-Title": "rag-engine"
},
json=body,
timeout=_client_timeout(),
)
except httpx.RequestError as e:
logger.error(
"LLM upstream unreachable (%s): %s",
UPSTREAM_URL,
e,
exc_info=True,
timeout=300,
)
except httpx.HTTPError as e:
# Ошибка сети / таймаут
return JSONResponse(
status_code=502,
content={
"error": "Bad gateway",
"details": str(e),
"upstream": UPSTREAM_URL,
"hint": "Проверьте VPN, DNS, доступность хоста и переменную LLM_UPSTREAM_URL.",
},
content={"error": "Bad gateway", "details": str(e)},
)
if r.status_code >= 500:
logger.warning(
"LLM upstream HTTP %s, body (prefix): %s",
r.status_code,
(r.text or "")[:800],
)
print(r.text)
# Если бэкенд всегда отвечает JSON:
# return JSONResponse(status_code=r.status_code, content=r.json())
# Универсальный вариант — пробрасываем «как есть»
return Response(
content=r.content,
status_code=r.status_code,

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,24 @@
"id": 1,
"name": "Автомобильная перевозка (LTL)",
"employee_email": "denis.fomin@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "customs_clearance_place_export_rf"],
"extra_required_fields": [
"hs_code",
"customs_clearance_place_export_rf"
],
"mandatory_counterparty_criteria": "По макету интерфейса обязательно отражать в письме контрагенту и запрашивать у клиента при отсутствии: название клиента; Incoterms; характер/наименование груза и код ТН ВЭД; страна, город, адрес забора; страна, город, точный адрес доставки; место таможенного оформления в РФ; количество грузовых мест и вес; габариты грузовых мест (Д×Ш×В).",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места в см или мм (длина × ширина × высота)\n8\tХарактер груза или наименование груза, код ТН ВЭД\n9\tСодержатся ли в грузе батарейки, газы под давлением, жидкости\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли груз нельзя штабелировать с другими грузами — можно ли штабелировать грузовые места между собой\n12\tЕсли груз — химия, литиевые батареи, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tЕсли есть батарейки — в составе груза или отдельно упакованы\n14\tЕсли груз из п.13 отгружается из материкового Китая по ж/д — дополнительно DGM\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tТребуется ли замена документов\n19\tСтрана, город, точный адрес доставки\n20\tМесто таможенного оформления в РФ",
"keywords": ["ltl", "сборный трак", "догруз", "группаж авто", "less than truckload", "неполная фура", "авто ltl", "мелкий груз авто", "road ltl", "partial truckload"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места в см или мм (длина × ширина × высота) и их вес и объём\n8\tХарактер груза или наименование груза, код ТН ВЭД\n9\tСодержатся ли в грузе батарейки, газы под давлением, жидкости\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли груз нельзя штабелировать с другими грузами — можно ли штабелировать грузовые места между собой\n12\tЕсли груз — химия, литиевые батареи, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tЕсли есть батарейки — в составе груза или отдельно упакованы\n14\tЕсли груз из п.13 отгружается из материкового Китая авто транспортом — дополнительно DGM\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tТребуется ли замена документов\n19\tСтрана, город, точный адрес доставки\n20\tМесто таможенного оформления в РФ",
"keywords": [
"ltl",
"сборный трак",
"догруз",
"группаж авто",
"less than truckload",
"неполная фура",
"авто ltl",
"мелкий груз авто",
"road ltl",
"partial truckload"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We are pleased to confirm the possibility of road freight (LTL) for your cargo under the following terms:\n\n- Route: (Pickup address) -> (Delivery address)\n- Cargo weight: (Cargo weight) kg\n- Number of packages: (Number of packages)\n- Volume: (Volume) m3\n- Dimensions: (Dimensions)\n- Cargo description: (Cargo description)\n- HS code: (HS code)\n- Dangerous properties: (Dangerous properties)\n- MSDS required: (MSDS required)\n- Freight cost: (Estimated cost)\n- Estimated transit time: (Transit time)\n\nTo confirm the order and issue the invoice, please reply to this email or contact your manager.\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nБлагодарим вас за обращение в компанию SEPTEM. Для расчёта автомобильной перевозки (LTL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nПожалуйста, дополните данные по вашему грузу. Вы можете ответить на это письмо или загрузить недостающие документы через личный кабинет.\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -14,10 +28,24 @@
"id": 2,
"name": "Автомобильная перевозка (FTL)",
"employee_email": "denis.fomin@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "vehicle_type", "customs_clearance_place_export_rf"],
"extra_required_fields": [
"hs_code",
"vehicle_type",
"customs_clearance_place_export_rf"
],
"mandatory_counterparty_criteria": "Обязательно в письме и при запросе клиенту: клиент; Incoterms; характер/наименование груза и ТН ВЭД; адрес забора; адрес доставки; место таможенного оформления в РФ; количество и типоразмер машин; вес груза в машине.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер машин\n7\tВес груза в машине\n8\tЕсли нет данных о количестве машин — габариты каждого грузового места (Д×Ш×В в см или мм) и вес каждого места\n9\tХарактер груза или наименование груза, код ТН ВЭД\n10\tСодержатся ли в грузе батарейки, газы под давлением, жидкости\n11\tХимия, литиевые батареи, жидкости, аэрозоли, газы или порошки — MSDS\n12\tБатарейки — в составе груза или отдельно упакованы\n13\tОтгрузка из материкового Китая по ж/д — DGM\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (фитоконтроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления в РФ",
"keywords": ["ftl", "полная фура", "целая машина", "full truckload", "отдельный автомобиль", "авто ftl", "тент фура целиком", "road ftl", "exclusive truck"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер машин\n7\tВес груза в машине и объём\n8\tЕсли нет данных о количестве машин — габариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n9\tХарактер груза или наименование груза, код ТН ВЭД\n10\tСодержатся ли в грузе батарейки, газы под давлением, жидкости\n11\tХимия, литиевые батареи, жидкости, аэрозоли, газы или порошки — MSDS\n12\tБатарейки — в составе груза или отдельно упакованы\n13\tОтгрузка из материкового Китая авто транспортом — DGM\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (фитоконтроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления в РФ",
"keywords": [
"ftl",
"полная фура",
"целая машина",
"full truckload",
"отдельный автомобиль",
"авто ftl",
"тент фура целиком",
"road ftl",
"exclusive truck"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We are pleased to confirm the possibility of road freight (FTL) for your cargo under the following terms:\n\n- Route: (Pickup address) -> (Delivery address)\n- Vehicle type: (Vehicle type)\n- Cargo weight: (Cargo weight) kg\n- Number of packages: (Number of packages)\n- Volume: (Volume) m3\n- Dimensions: (Dimensions)\n- Cargo description: (Cargo description)\n- HS code: (HS code)\n- Dangerous properties: (Dangerous properties)\n- MSDS required: (MSDS required)\n- Freight cost: (Estimated cost)\n- Transit time: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nБлагодарим за обращение в SEPTEM. Для расчёта автомобильной перевозки (FTL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nПожалуйста, дополните данные по грузу.\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -25,10 +53,24 @@
"id": 3,
"name": "Морская перевозка (LCL)",
"employee_email": "marina.eremina@timnet.ch, ekaterina.kochenkova@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "stackable_with_others"],
"extra_required_fields": [
"hs_code",
"stackable_with_others"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество мест и вес; габариты Д×Ш×В; возможность штабелирования с другими отправками.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к морской сборной перевозке (LCL): порт погрузки/выгрузки и морской этап явно указаны в переписке; без доминирования ж/д как основного этапа.",
"keywords": ["море lcl", "морская сборная", "морской lcl", "sea lcl", "сборный морской", "консолидация море", "groupage sea", "lcl порт", "морской группаж", "неполный контейнер море"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес с объёмом\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к морской сборной перевозке (LCL): порт погрузки/выгрузки и морской этап явно указаны в переписке; без доминирования ж/д как основного этапа.",
"keywords": [
"море lcl",
"морская сборная",
"морской lcl",
"sea lcl",
"сборный морской",
"консолидация море",
"groupage sea",
"lcl порт",
"морской группаж",
"неполный контейнер море"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm the possibility of sea freight (LCL) under the following terms:\n\n- Route: (Port of loading) -> (Port of discharge)\n- Shipment type: LCL\n- Cargo weight: (Cargo weight) kg\n- Packages: (Number of packages)\n- Volume: (Volume) m3\n- Dimensions: (Dimensions)\n- Cargo: (Cargo description)\n- HS code: (HS code)\n- Dangerous properties: (Dangerous properties)\n- MSDS: (MSDS required)\n- Freight: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта морской сборной перевозки (LCL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nПожалуйста, дополните данные.\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -36,10 +78,22 @@
"id": 4,
"name": "Железнодорожная перевозка (LCL)",
"employee_email": "marina.eremina@timnet.ch, ekaterina.kochenkova@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "stackable_with_others"],
"extra_required_fields": [
"hs_code",
"stackable_with_others"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество мест и вес; габариты; штабелирование с другими отправками.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к ж/д сборной перевозке (LCL): станции/терминалы ж/д, контейнер сборный по ж/д; морской этап в запросе не основной.",
"keywords": ["жд lcl", "ж/д lcl", "rail lcl", "сборный контейнер жд", "сборная жд", "lcl поезд", "группаж жд", "железная дорога сборный"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес с объёмом\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к ж/д сборной перевозке (LCL): станции/терминалы ж/д, контейнер сборный по ж/д; морской этап в запросе не основной.",
"keywords": [
"жд lcl",
"ж/д lcl",
"rail lcl",
"сборный контейнер жд",
"сборная жд",
"lcl поезд",
"группаж жд",
"железная дорога сборный"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm rail freight (LCL) under the following terms:\n\n- Route: (Pickup address) -> (Delivery address)\n- Shipment type: LCL\n- Weight: (Cargo weight) kg\n- Packages: (Number of packages)\n- Volume: (Volume) m3\n- Dimensions: (Dimensions)\n- Cargo: (Cargo description)\n- HS: (HS code)\n- MSDS: (MSDS required)\n- Cost: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта железнодорожной сборной перевозки (LCL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -47,10 +101,22 @@
"id": 5,
"name": "Мультимодальная перевозка море + ж/д (LCL)",
"employee_email": "marina.eremina@timnet.ch, ekaterina.kochenkova@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "stackable_with_others"],
"extra_required_fields": [
"hs_code",
"stackable_with_others"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество мест и вес; габариты; штабелирование с другими отправками.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nКритерии как для сборного груза при цепочке море и ж/д (интермодаль): в переписке явно присутствуют и морской, и железнодорожный этапы.",
"keywords": ["море жд", "море+жд", "море и жд", "sea rail", "интермодальн", "multimodal lcl", "морской и жд этап", "порт и станция сборный"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес с объёмом\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nКритерии как для сборного груза при цепочке море и ж/д (интермодаль): в переписке явно присутствуют и морской, и железнодорожный этапы.",
"keywords": [
"море жд",
"море+жд",
"море и жд",
"sea rail",
"интермодальн",
"multimodal lcl",
"морской и жд этап",
"порт и станция сборный"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm multimodal sea+rail (LCL) under the following terms:\n\n- Route: (Pickup address) -> (Delivery address)\n- Modes: sea + rail (LCL)\n- Weight: (Cargo weight) kg\n- Packages: (Number of packages)\n- Volume: (Volume) m3\n- Dimensions: (Dimensions)\n- Cargo: (Cargo description)\n- HS: (HS code)\n- MSDS: (MSDS required)\n- Cost: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта мультимодальной перевозки (море + ж/д, LCL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -58,10 +124,22 @@
"id": 6,
"name": "Морская перевозка (FCL)",
"employee_email": "marina.eremina@timnet.ch, ekaterina.kochenkova@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "container_type"],
"extra_required_fields": [
"hs_code",
"container_type"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество и типоразмер контейнеров; вес груза в контейнере.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и вес мест\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМорской FCL: ISO-контейнеры под морской фрахт и погрузку на судно.",
"keywords": ["морской fcl", "море fcl", "sea fcl", "полный контейнер море", "морской контейнер", "fcl порт", "цельный контейнер море", "ocean fcl"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере и объём\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и их вес и объём\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМорской FCL: ISO-контейнеры под морской фрахт и погрузку на судно.",
"keywords": [
"морской fcl",
"море fcl",
"sea fcl",
"полный контейнер море",
"морской контейнер",
"fcl порт",
"цельный контейнер море",
"ocean fcl"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm sea freight (FCL):\n\n- Port: (Port of loading) -> (Port of discharge)\n- Container: (Container type)\n- Weight: (Cargo weight) kg\n- Cargo: (Cargo description)\n- HS: (HS code)\n- MSDS/IMDG: (MSDS required)\n- Cost: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта морской перевозки (FCL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -69,10 +147,21 @@
"id": 7,
"name": "Железнодорожная перевозка (FCL)",
"employee_email": "marina.eremina@timnet.ch, ekaterina.kochenkova@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "container_type"],
"extra_required_fields": [
"hs_code",
"container_type"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество и типоразмер контейнеров; вес груза в контейнере.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и вес мест\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nЖ/д FCL: ISO 20'/40' на платформах/вагонных комплектах (1520 мм); без обязательного морского этапа.",
"keywords": ["жд fcl", "ж/д fcl", "rail fcl", "контейнер по жд целый", "полный контейнер жд", "fcl поезд", "вагон контейнер"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере и объём\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и их вес и объём\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nЖ/д FCL: ISO 20'/40' на платформах/вагонных комплектах (1520 мм); без обязательного морского этапа.",
"keywords": [
"жд fcl",
"ж/д fcl",
"rail fcl",
"контейнер по жд целый",
"полный контейнер жд",
"fcl поезд",
"вагон контейнер"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm rail freight (FCL):\n\n- Route: (Pickup address) -> (Delivery address)\n- Container: (Container type)\n- Weight: (Cargo weight) kg\n- Cargo: (Cargo description)\n- HS: (HS code)\n- MSDS: (MSDS required)\n- Cost: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта железнодорожной перевозки (FCL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -80,10 +169,20 @@
"id": 8,
"name": "Мультимодальная перевозка море + ж/д (FCL)",
"employee_email": "marina.eremina@timnet.ch, ekaterina.kochenkova@timnet.ch, Yana.Schkabrova@timnet.ch",
"extra_required_fields": ["hs_code", "container_type"],
"extra_required_fields": [
"hs_code",
"container_type"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество и типоразмер контейнеров; вес груза в контейнере.",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и вес мест\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМультимодальная перевозка море + ж/д (FCL): в запросе явно сочетаются морской и ж/д этапы с полным контейнером (интермодаль).",
"keywords": ["море жд fcl", "море+жд fcl", "sea rail fcl", "интермодальн fcl", "контейнер море и жд", "порт станция fcl"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере и объём\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и их вес и объём\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМультимодальная перевозка море + ж/д (FCL): в запросе явно сочетаются морской и ж/д этапы с полным контейнером (интермодаль).",
"keywords": [
"море жд fcl",
"море+жд fcl",
"sea rail fcl",
"интермодальн fcl",
"контейнер море и жд",
"порт станция fcl"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm multimodal sea+rail (FCL):\n\n- Route: (Port of loading) / rail -> (Delivery address)\n- Container: (Container type)\n- Weight: (Cargo weight) kg\n- Cargo: (Cargo description)\n- HS: (HS code)\n- MSDS: (MSDS required)\n- Cost: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта мультимодальной перевозки (море + ж/д, FCL) нам необходима следующая информация:\n\n(Необходимая информация)\n\nС уважением,\nКоманда SEPTEM Cargo"
},
@ -91,10 +190,26 @@
"id": 9,
"name": "Авиаперевозка",
"employee_email": "evgeniy.domashnev@timnet.ch, olga.ermakova@timnet.ch, andrey.reshetnyak@timnet.ch, anastasia.fatkina@timnet.ch",
"extra_required_fields": ["hs_code", "stackable_with_others", "total_volume_cbm", "dangerous_goods_clarification", "customs_clearance_place_export_rf"],
"extra_required_fields": [
"hs_code",
"stackable_with_others",
"total_volume_cbm",
"dangerous_goods_clarification",
"customs_clearance_place_export_rf"
],
"mandatory_counterparty_criteria": "Обязательно: клиент; Incoterms; характер груза и ТН ВЭД; адрес забора; адрес доставки; количество мест, вес и объём; габариты Д×Ш×В; штабелирование с другими отправками; сведения о батарейках, газах, жидкостях, аэрозолях; место таможенного оформления при экспорте из РФ (и прочие пункты чек-листа авиа по полному criteria).",
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес, объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, жидкости, хладоагенты, сухой лёд\n10\tШтабелирование с другими отправками\n11\tШтабелирование мест между собой\n12\tMSDS при химии, батареях, жидкостях, аэрозолях, порошках\n13\tБатарейки — в составе или отдельно\n14\tБатарейки и вылет из материкового Китая — DGM (Dangerous Goods Management) report\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tПерелёт через третью страну со сменой авианакладных\n18\tЭкспортная лицензия у отправителя\n19\tДоп. сервис: переупаковка, маркировка, логгеры и т.д.\n20\tКто осуществляет экспедирование в аэропорту прибытия (терминал, сборы авиакомпании)\n21\tСтрана, город, точный адрес доставки\n22\tВывоз из аэропорта — особые требования к автотранспорту, время подачи\n23\tНужно ли таможенное оформление\n24\tМесто таможенного оформления при экспорте из РФ\n25\tФумигация деревянной тары при экспорте из РФ",
"keywords": ["авиа", "авиаперевозка", "самолёт", "air freight", "air cargo", "аэропорт", "flight", "авианакладная", "awb"],
"criteria": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес, объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, жидкости, хладоагенты, сухой лёд\n10\tШтабелирование с другими отправками\n11\tШтабелирование мест между собой\n12\tMSDS при химии, батареях, жидкостях, аэрозолях, порошках\n13\tБатарейки — в составе или отдельно\n14\tБатарейки и вылет из материкового Китая — DGM (Dangerous Goods Management) report\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tПерелёт через третью страну со сменой авианакладных\n18\tЭкспортная лицензия у отправителя\n19\t Дополнительные сервисы (фитоконтроль и т.п.)\n20\tКто осуществляет экспедирование в аэропорту прибытия (терминал, сборы авиакомпании)\n21\tСтрана, город, точный адрес доставки\n22\tВывоз из аэропорта — особые требования к автотранспорту, время подачи\n23\tНужно ли таможенное оформление\n24\tМесто таможенного оформления при экспорте из РФ\n25\tФумигация деревянной тары при экспорте из РФ",
"keywords": [
"авиа",
"авиаперевозка",
"самолёт",
"air freight",
"air cargo",
"аэропорт",
"flight",
"авианакладная",
"awb"
],
"confirmation_template": "Dear Partner!\n\nThank you for contacting SEPTEM. We confirm air freight:\n\n- Route: (Pickup address) -> (Delivery address)\n- Weight: (Cargo weight) kg\n- Packages: (Number of packages)\n- Volume: (Volume) m3\n- Dimensions: (Dimensions)\n- Cargo: (Cargo description)\n- HS: (HS code)\n- Dangerous goods: (Dangerous properties)\n- MSDS: (MSDS required)\n- DGM (при опасном грузе / вылет из материкового Китая — по правилам перевозчика)\n- Cost: (Estimated cost)\n- Transit: (Transit time)\n\nBest regards,\nSEPTEM Cargo Team",
"info_request_template": "Уважаемый (Клиент)!\n\nДля расчёта авиаперевозки нам необходима следующая информация:\n\n(Необходимая информация)\n\nС уважением,\nКоманда SEPTEM Cargo"
}

View File

@ -1,11 +1,11 @@
{
"Автомобильная перевозка (LTL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места в см или мм (длина × ширина × высота)\n8\tХарактер груза или наименование груза, код ТН ВЭД\n9\tСодержатся ли в грузе батарейки, газы под давлением, жидкости\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли груз нельзя штабелировать с другими грузами — можно ли штабелировать грузовые места между собой\n12\tЕсли груз — химия, литиевые батареи, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tЕсли есть батарейки — в составе груза или отдельно упакованы\n14\tЕсли груз из п.13 отгружается из материкового Китая по ж/д — дополнительно DGM\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tТребуется ли замена документов\n19\tСтрана, город, точный адрес доставки\n20\tМесто таможенного оформления в РФ",
"Автомобильная перевозка (FTL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер машин\n7\tВес груза в машине\n8\tЕсли нет данных о количестве машин — габариты каждого грузового места (Д×Ш×В в см или мм) и вес каждого места\n9\tХарактер груза или наименование груза, код ТН ВЭД\n10\tСодержатся ли в грузе батарейки, газы под давлением, жидкости\n11\tХимия, литиевые батареи, жидкости, аэрозоли, газы или порошки — MSDS\n12\tБатарейки — в составе груза или отдельно упакованы\n13\tОтгрузка из материкового Китая по ж/д — DGM\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (фитоконтроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления в РФ",
"Морская перевозка (LCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к морской сборной перевозке (LCL): порт погрузки/выгрузки и морской этап явно указаны в переписке; без доминирования ж/д как основного этапа.",
"Железнодорожная перевозка (LCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к ж/д сборной перевозке (LCL): станции/терминалы ж/д, контейнер сборный по ж/д; морской этап в запросе не основной.",
"Мультимодальная перевозка море + ж/д (LCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест и вес\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, газы под давлением, жидкости в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\tХимия, литиевые батареи, жидкости, аэрозоли, газы, порошки — MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nКритерии как для сборного груза при цепочке море и ж/д (интермодаль): в переписке явно присутствуют и морской, и железнодорожный этапы.",
"Морская перевозка (FCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и вес мест\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМорской FCL: ISO-контейнеры под морской фрахт и погрузку на судно.",
"Железнодорожная перевозка (FCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и вес мест\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nЖ/д FCL: ISO 20'/40' на платформах/вагонных комплектах (1520 мм); без обязательного морского этапа.",
"Мультимодальная перевозка море + ж/д (FCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере\n8\tЕсли нет данных о контейнерах — габариты каждого грузового места (Д×Ш×В) и вес мест\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tБатарейки, газы, жидкости\n11\tMSDS при необходимости\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (крепление в контейнере, фитосанитарный контроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМультимодальная перевозка море + ж/д (FCL): в запросе явно сочетаются морской и ж/д этапы с полным контейнером (интермодаль).",
"Авиаперевозка": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес, объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, жидкости, хладоагенты, сухой лёд\n10\tШтабелирование с другими отправками\n11\tШтабелирование мест между собой\n12\tMSDS при химии, батареях, жидкостях, аэрозолях, порошках\n13\tБатарейки — в составе или отдельно\n14\tБатарейки и вылет из материкового Китая — DGM (Dangerous Goods Management) report\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tПерелёт через третью страну со сменой авианакладных\n18\tЭкспортная лицензия у отправителя\n19\tДоп. сервис: переупаковка, маркировка, логгеры и т.д.\n20\tКто осуществляет экспедирование в аэропорту прибытия (терминал, сборы авиакомпании)\n21\tСтрана, город, точный адрес доставки\n22\tВывоз из аэропорта — особые требования к автотранспорту, время подачи\n23\tНужно ли таможенное оформление\n24\tМесто таможенного оформления при экспорте из РФ\n25\tФумигация деревянной тары при экспорте из РФ"
"Автомобильная перевозка (LTL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес, объём\n7\tГабариты каждого грузового места в см или мм (длина × ширина × высота) и их вес и объём\n8\tХарактер груза или наименование груза, код ТН ВЭД\n9\tСодержатся ли в грузе химия, батарейки, жидкости, аэрозоли, газы или порошки\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли груз нельзя штабелировать с другими грузами — можно ли штабелировать грузовые места между собой\n12\tЕсли груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tЕсли есть батарейки — в составе груза или отдельно упакованы\n14\tЕсли груз из п.13 отгружается из материкового Китая авто транспортом — дополнительно DGM\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tТребуется ли замена документов\n19\tСтрана, город, точный адрес доставки\n20\tМесто таможенного оформления в РФ",
"Автомобильная перевозка (FTL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер машин\n7\tВес груза в машине и объём\n8\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём \n9\tХарактер груза или наименование груза, код ТН ВЭД\n10\tСодержатся ли в грузе химия, батарейки, жидкости, аэрозоли, газы или порошки\n11\tЕсли груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n12\tБатарейки — в составе груза или отдельно упакованы\n13\tОтгрузка из материкового Китая авто транспортом — DGM\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы (фитоконтроль и т.п.)\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\t Страна, город, точный адрес доставки",
"Морская перевозка (LCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес и объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tХимия, батарейки, жидкости, аэрозоли, газы или порошки в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к морской сборной перевозке (LCL): порт погрузки/выгрузки и морской этап явно указаны в переписке; без доминирования ж/д как основного этапа.",
"Железнодорожная перевозка (LCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес и объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tХимия, батарейки, жидкости, аэрозоли, газы или порошки в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nПрименимо к ж/д сборной перевозке (LCL): станции/терминалы ж/д, контейнер сборный по ж/д; морской этап в запросе не основной.",
"Мультимодальная перевозка море + ж/д (LCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес и объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n8\tХарактер груза или наименование, код ТН ВЭД\n9\tХимия, батарейки, жидкости, аэрозоли, газы или порошки в грузе\n10\tМожно ли штабелировать груз с другими отправками\n11\tЕсли нельзя с другими — можно ли штабелировать места между собой\n12\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tБатарейки — в составе или отдельно\n14\tОтгрузка из материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tДополнительные сервисы (фитоконтроль и т.п.)\n18\tСтрана, город, точный адрес доставки\n\nКритерии как для сборного груза при цепочке море и ж/д (интермодаль): в переписке явно присутствуют и морской, и железнодорожный этапы.",
"Морская перевозка (FCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере и объём\n8\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём\n9\tХарактер груза или наименование, код ТН ВЭД\n10\tХимия, батарейки, жидкости, аэрозоли, газы или порошки в грузе\n11\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nМорской FCL: ISO-контейнеры под морской фрахт и погрузку на судно.",
"Железнодорожная перевозка (FCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере и объём\n8\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём \n9\tХарактер груза или наименование, код ТН ВЭД\n10\tХимия, батарейки, жидкости, аэрозоли, газы или порошки\n11\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n\nЖ/д FCL: ISO 20'/40' на платформах/вагонных комплектах (1520 мм); без обязательного морского этапа.",
"Мультимодальная перевозка море + ж/д (FCL)": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество и типоразмер контейнеров\n7\tВес груза в контейнере и объём\n8\tГабариты каждого грузового места (Д×Ш×В в см или мм) и их вес и объём \n9\tХарактер груза или наименование, код ТН ВЭД\n10\tХимия, батарейки, жидкости, аэрозоли, газы или порошки\n11\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n12\tБатарейки в составе или отдельно\n13\tИз материкового Китая по ж/д — Technical Description of Goods in Railway Transport\n14\tНазвание бренда\n15\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n16\tДополнительные сервисы\n17\tТребуется ли замена документов\n18\tСтрана, город, точный адрес доставки\n19\tМесто таможенного оформления при экспорте из РФ\n20\tФумигация деревянной тары при экспорте из РФ\n21\tПроверка габаритов грузовых мест на проходимость в дверной проём контейнеров 20DV, 40DV, 40HC\n22\tМультимодальная перевозка море + ж/д (FCL): в запросе явно сочетаются морской и ж/д этапы с полным контейнером (интермодаль).",
"Авиаперевозка": "1\tНазвание клиента\n2\tУсловия поставки Incoterms\n3\tДата готовности груза\n4\tСтрана, город, адрес забора\n5\tСтоимость груза\n6\tКоличество грузовых мест, вес, объём\n7\tГабариты каждого грузового места (Д×Ш×В в см или мм)и их вес и объём \n8\tХарактер груза или наименование, код ТН ВЭД\n9\tБатарейки, газы, жидкости, хладоагенты, сухой лёд\n10\tШтабелирование с другими отправками\n11\tШтабелирование мест между собой\n12\t Если груз содержит — химия, батарейки, жидкости, аэрозоли, газы или порошки — предоставить MSDS\n13\tБатарейки — в составе или отдельно\n14\tБатарейки и вылет из материкового Китая — DGM (Dangerous Goods Management) report\n15\tНазвание бренда\n16\tЕсли бренд зарегистрирован в таможенной системе Китая — есть ли у отправителя авторизационное письмо/разрешение на вывоз бренда\n17\tПерелёт через третью страну со сменой авианакладных\n18\tЭкспортная лицензия у отправителя\n19\tДополнительные сервисы (фитоконтроль и т.п.)\n20\tКто осуществляет экспедирование в аэропорту прибытия (терминал, сборы авиакомпании)\n21\tСтрана, город, точный адрес доставки\n22\tВывоз из аэропорта — особые требования к автотранспорту, время подачи\n23\tНужно ли таможенное оформление\n24\tМесто таможенного оформления при экспорте из РФ\n25\tФумигация деревянной тары при экспорте из РФ"
}

View File

@ -325,11 +325,24 @@ def collect_extra_required_missing_for_template(shipment: Dict, template_obj: Di
if v is None or (isinstance(v, str) and not str(v).strip()):
out.append(field)
continue
if field == "customs_clearance_place_export_rf":
v = shipment.get("customs_clearance_place_export_rf")
if v is None or (isinstance(v, str) and not str(v).strip()):
out.append(field)
direction = shipment.get("shipment_direction")
# только экспорт из РФ
if direction != "export":
continue
v = shipment.get("customs_clearance_place_export_rf")
if v is None or (
isinstance(v, str) and not str(v).strip()
):
out.append(field)
continue
if field == "total_volume_cbm":
val = shipment.get("total_volume_cbm")
if val is None or val == "":
@ -1155,17 +1168,44 @@ def format_shipment_for_template(
dims = shipment.get("dimensions", [])
if isinstance(dims, list) and dims:
dim_strings = []
for d in dims:
if isinstance(d, dict):
for idx, d in enumerate(dims, start=1):
if not isinstance(d, dict):
continue
l = d.get("length_cm")
w = d.get("width_cm")
h = d.get("height_cm")
if l and w and h:
dim_strings.append(f"{l}×{w}×{h} {dim_unit}")
sizes = [str(v) for v in (l, w, h) if v not in (None, "", 0)]
if not sizes:
continue
parts = [
f"Место {idx}: {'×'.join(sizes)} см"
]
weight = d.get("weight_kg")
if weight not in (None, "", 0):
if template_locale == "en":
parts.append(f"weight {weight} kg")
else:
parts.append(f"вес {weight} кг")
volume = d.get("volume_cbm")
if volume not in (None, "", 0):
if template_locale == "en":
parts.append(f"volume {volume} cbm")
else:
parts.append(f"объём {volume} м³")
dim_strings.append(" | ".join(parts))
if dim_strings:
data["dimensions_str"] = "; ".join(dim_strings)
@ -1701,16 +1741,34 @@ def render_shipment_criteria_checklist(
seen = set()
if isinstance(dims, list):
for d in dims:
if isinstance(d, dict):
for idx, d in enumerate(dims, start=1):
if not isinstance(d, dict):
continue
l = d.get("length_cm")
w = d.get("width_cm")
h = d.get("height_cm")
if l and w and h:
key = f"{l}x{w}x{h}".lower()
if key not in seen:
seen.add(key)
parts.append(f"{l}×{w}×{h} см")
sizes = [str(v) for v in (l, w, h) if v not in (None, "", 0)]
if not sizes:
continue
weight = d.get("weight_kg")
volume = d.get("volume_cbm")
line_parts = [
f"Место {idx}: {'×'.join(sizes)} см"
]
if weight not in (None, "", 0):
line_parts.append(f"вес {weight} кг")
if volume not in (None, "", 0):
line_parts.append(f"объём {volume} м³")
parts.append(" | ".join(line_parts))
return "; ".join(parts) if parts else NO_INFO_TEXT
@ -1727,6 +1785,191 @@ def render_shipment_criteria_checklist(
return vehicle_dims
return NO_INFO_TEXT
def _format_dimensions_with_weight_volume(sh: Dict) -> str:
"""
Габариты каждого места + вес + объём.
"""
dims = sh.get("dimensions")
if not isinstance(dims, list) or not dims:
return NO_INFO_TEXT
parts = []
for idx, d in enumerate(dims, start=1):
if not isinstance(d, dict):
continue
l = d.get("length_cm")
w = d.get("width_cm")
h = d.get("height_cm")
weight = d.get("weight_kg")
volume = d.get("volume_cbm")
row = []
sizes = [
str(v)
for v in (l, w, h)
if v not in (None, "", 0)
]
if sizes:
row.append(f"{'×'.join(sizes)} см")
if weight not in (None, "", 0):
row.append(f"вес {weight} кг")
if volume not in (None, "", 0):
row.append(f"объём {volume} м³")
if row:
parts.append(f"Место {idx}: " + " | ".join(row))
if not parts:
return NO_INFO_TEXT
return "; ".join(parts)
def _filtered_door_clearance(sh: Dict) -> str:
"""
Показывает только проблемные места.
"""
raw = door_clearance_summary(sh)
if (
"Нет габаритов" in raw
or "невозможна" in raw
):
return raw
import re
place_blocks = re.findall(
r"(Место\s+\d+.*?)(?=Место\s+\d+|$)",
raw,
flags=re.S,
)
failed = []
for blk in place_blocks:
if "не проходит" not in blk:
continue
parts = []
for part in blk.split(";"):
part = part.strip()
if (
"не проходит" in part
or part.startswith("Место")
):
parts.append(part)
txt = "; ".join(parts)
if txt:
failed.append(txt)
# =====================================================
# Все проходят
# =====================================================
if not failed:
ct = (sh.get("container_type") or "").strip()
if ct:
return (
f"Все грузовые места проходят "
f"в дверной проём контейнера(ов) {ct}."
)
return (
"Все грузовые места проходят "
"в дверной проём контейнеров."
)
# =====================================================
# Есть проблемы
# =====================================================
return " ".join(failed)
# --- далее оригинальная функция _answer_for_criterion с правками ---
def _answer_for_criterion(criterion_text: str) -> str:
c = criterion_text.lower()
# ... существующие проверки до блока с габаритами ...
# === ИЗМЕНЕНИЕ: критерии с общим весом и объёмом ===
if "вес" in c and "количество" not in c and "габарит" not in c:
w = shipment.get("total_weight_kg")
weight_str = f"{w} кг" if (w is not None and str(w).strip() not in ("", "0", "0.0")) else ""
vol_str = _format_volume_cbm()
if weight_str and vol_str != NO_INFO_TEXT:
return f"{weight_str}, объём: {vol_str}"
elif weight_str:
return weight_str
elif vol_str != NO_INFO_TEXT:
return f"Объём: {vol_str}"
return NO_INFO_TEXT
if "общий объ" in c or "общий объём" in c or "общий объем" in c:
vol = _format_volume_cbm()
w = shipment.get("total_weight_kg")
weight_str = f"{w} кг" if (w is not None and str(w).strip() not in ("", "0", "0.0")) else ""
if vol != NO_INFO_TEXT and weight_str:
return f"{vol}, вес: {weight_str}"
elif vol != NO_INFO_TEXT:
return vol
elif weight_str:
return f"Вес: {weight_str}"
return NO_INFO_TEXT
# ... блоки с габаритами транспортных средств (без изменений) ...
if "габарит" in c and (
"транспортн" in c
or "машин" in c
or "средств" in c
or "кузов" in c
or "прицеп" in c
or "полуприцеп" in c
):
return _format_dimensions_any() # только размеры
# === ИЗМЕНЕНИЕ: габариты грузовых мест добавляем вес и объём ===
if "габарит" in c and "грузов" in c and "мест" in c:
return _format_dimensions_with_weight_volume(shipment)
if (
("палл" in c or "палет" in c or "короб" in c or "carton" in c or "box" in c)
and ("габарит" in c or "размер" in c or "size" in c or "dimension" in c)
):
return _format_dimensions_with_weight_volume(shipment)
# общий fallback для габаритов (если не транспорт)
if "габарит" in c:
return _format_dimensions_with_weight_volume(shipment)
# ... критерии про проём контейнера ...
if ("проём" in c or "проем" in c) and (
"контейнер" in c
or "20dv" in c
or "40dv" in c
or "40hc" in c
or "проходимость" in c
):
return _filtered_door_clearance(shipment)
def _join_desc_and_hs(sh: Dict) -> str:
desc = (sh.get("cargo_description") or "").strip()
hs = (sh.get("hs_code") or "").strip()
@ -1830,7 +2073,18 @@ def render_shipment_criteria_checklist(
def _answer_for_criterion(criterion_text: str) -> str:
c = criterion_text.lower()
# =====================================================
# Проверка дверного проёма контейнера (п.21)
# =====================================================
if (
"дверной проём" in c
or "дверной проем" in c
or "20dv" in c
or "40dv" in c
or "40hc" in c
):
return _filtered_door_clearance(shipment)
# Любой критерий с "габарит" должен в приоритете возвращать размеры, а не перехватываться ветками по весу.
if "габарит" in c:
return _format_dimensions_any()
@ -1897,14 +2151,68 @@ def render_shipment_criteria_checklist(
return NO_INFO_TEXT
return str(pkg).strip()
# =========================================================
# ОБЩИЙ ОБЪЁМ
# =========================================================
if "общий объ" in c or "общий объём" in c or "общий объем" in c:
return _format_volume_cbm()
vol = _format_volume_cbm()
w = shipment.get("total_weight_kg")
weight_ok = (
w is not None
and str(w).strip() not in ("", "0", "0.0")
)
weight_str = f"{w} кг" if weight_ok else ""
# есть и объём и вес
if vol != NO_INFO_TEXT and weight_str:
return f"{vol}, вес: {weight_str}"
# только объём
if vol != NO_INFO_TEXT:
return vol
# только вес
if weight_str:
return f"Вес: {weight_str}"
return NO_INFO_TEXT
# =========================================================
# ОБЩИЙ ВЕС
# =========================================================
if "вес" in c and "количество" not in c:
w = shipment.get("total_weight_kg")
if w is None or str(w).strip() in ("", "0", "0.0"):
weight_ok = (
w is not None
and str(w).strip() not in ("", "0", "0.0")
)
weight_str = f"{w} кг" if weight_ok else ""
vol = _format_volume_cbm()
# есть и вес и объём
if weight_str and vol != NO_INFO_TEXT:
return f"{weight_str}, объём: {vol}"
# только вес
if weight_str:
return weight_str
# только объём
if vol != NO_INFO_TEXT:
return f"Объём: {vol}"
return NO_INFO_TEXT
return f"{w} кг"
if ("проём" in c or "проем" in c) and (
"контейнер" in c
@ -1950,6 +2258,9 @@ def render_shipment_criteria_checklist(
# Перечень DG (авиа п.9, море/авто и т.д.) и формулировки интерфейса («Наличие батарейк, газов…»).
if (
"msds" not in c
and "dgm" not in c
and (
"содержатся ли в грузе" in c
or "опасные вещества" in c
or "опасные свойства" in c
@ -1965,6 +2276,7 @@ def render_shipment_criteria_checklist(
or "сухой лед" in c
)
)
)
):
return format_dangerous_goods_summary(shipment)
@ -1974,15 +2286,27 @@ def render_shipment_criteria_checklist(
return _yes_no_or_no_info(shipment.get("stackable_with_others"))
if "msds" in c or "паспорт безопасности" in c:
msds_negative = _msds_negative_phrase()
if msds_negative:
return msds_negative
if "батаре" in c and _has_batteries(shipment) is not True:
# Если батареек нет, документ по батарейкам не нужен.
if shipment.get("msds_required") is False:
msds_required = shipment.get("msds_required")
# =====================================
# Явно не нужен
# =====================================
if msds_required is False:
return "Не нужен"
return NO_INFO_TEXT
return _required_doc(shipment.get("msds_required"), "msds")
# =====================================
# Нужен / найден
# =====================================
return _required_doc(msds_required, "msds")
if re.search(r"(?<![a-z])dgm(?![a-z])", c):
if "батаре" in c and _has_batteries(shipment) is not True:
if shipment.get("dgm_report_required") is False:
@ -2021,7 +2345,38 @@ def render_shipment_criteria_checklist(
return build_documents_summary(shipment)
if "бренд" in c and "авторизац" in c:
return format_brand_authorization_summary(shipment)
docs = shipment.get("documents_found", {}) or {}
found_docs = docs.get("brand_authorization", []) or []
if found_docs:
filenames = [
d.get("filename", "")
for d in found_docs
if d.get("filename")
]
if filenames:
return (
"Нужно "
f"(документ найден: {', '.join(filenames)})"
)
return "Нужно (документ найден)"
flag = shipment.get("brand_authorization_letter")
if flag is True:
return "Нужно (документ не найден)"
elif flag is False:
return "Не нужно"
else:
return NO_INFO_TEXT
if "название бренда" in c or (
"бренд" in c
and "авторизац" not in c
@ -2032,8 +2387,23 @@ def render_shipment_criteria_checklist(
if "лицензия экспорт" in c:
return _yes_no_or_no_info(shipment.get("exporter_has_export_license"))
if "трансшипмент" in c:
return _yes_no_or_no_info(shipment.get("transshipment_with_third_country"))
if (
"трансшипмент" in c
or "треть" in c
or "сменой авианаклад" in c
or "разрыв наклад" in c
):
transshipment = shipment.get("transshipment_with_third_country")
if transshipment is True:
return "Нужен"
elif transshipment is False:
return "Не нужен"
else:
return NO_INFO_TEXT
if "дополнительный сервис" in c or "дополнительные услуги" in c or "дополнительные сервисы" in c or "сервисы" in c:
extras = shipment.get("additional_services")
@ -2082,9 +2452,13 @@ def render_shipment_criteria_checklist(
return NO_INFO_TEXT
if "батарейки" in c and ("упаков" in c or "отдель" in c):
if _has_batteries(shipment) is not True:
battery_mode = shipment.get("battery_mode")
if battery_mode:
return battery_mode
return NO_INFO_TEXT
return _yes_no_or_no_info(shipment.get("batteries_packed_separately"))
if "тип транспорта" in c:
return (shipment.get("vehicle_type") or "").strip() or NO_INFO_TEXT
@ -2393,12 +2767,37 @@ def display_overview(report: Dict):
with col1 if i < mid else col2:
label = FIELD_LABELS.get(key, key)
if key in ("dimensions", "vehicle_dimensions"):
if key == "dimensions":
dims = []
for d in value:
if isinstance(d, dict) and d.get('length_cm') and d.get('width_cm') and d.get('height_cm'):
dims.append(f"{d['length_cm']}×{d['width_cm']}×{d['height_cm']} см")
for idx, d in enumerate(value, start=1):
if not isinstance(d, dict):
continue
l = d.get("length_cm")
w = d.get("width_cm")
h = d.get("height_cm")
sizes = [str(v) for v in (l, w, h) if v not in (None, "", 0)]
if not sizes:
continue
parts = [
f"Место {idx}: {'×'.join(sizes)} см"
]
if d.get("weight_kg") not in (None, "", 0):
parts.append(f"вес {d['weight_kg']} кг")
if d.get("volume_cbm") not in (None, "", 0):
parts.append(f"объём {d['volume_cbm']} м³")
dims.append(" | ".join(parts))
value_str = "; ".join(dims) if dims else NO_INFO_TEXT
elif key == "criteria" and isinstance(value, str):
# criteria может быть:
# - сырой: "1\tНазвание...\n2\t..."
@ -2423,9 +2822,26 @@ def display_overview(report: Dict):
elif key == "cargo_ready_date":
value_str = format_cargo_ready_date_for_display(shipment)
elif isinstance(value, bool):
if key == "brand_authorization_letter":
value_str = format_brand_authorization_summary(shipment)
else:
# Только для документов: "нужен/не нужен"
doc_bool_keys = {"msds_required", "dgm_report_required", "brand_authorization_letter"}
base_str = ("нужен" if value else "не нужен") if key in doc_bool_keys else ("✅ Да" if value else "❌ Нет")
doc_bool_keys = {
"msds_required",
"dgm_report_required",
"brand_authorization_letter"
}
base_str = (
"нужен" if value else "не нужен"
) if key in doc_bool_keys else (
"✅ Да" if value else "❌ Нет"
)
docs = shipment.get("documents_found", {})
doc_map = {
@ -2441,12 +2857,22 @@ def display_overview(report: Dict):
if found_docs:
filenames = [d["filename"] for d in found_docs]
value_str = f"{base_str} (документ найден: {', '.join(filenames)})"
filenames = [
d["filename"]
for d in found_docs
]
value_str = (
f"{base_str} "
f"(документ найден: {', '.join(filenames)})"
)
else:
value_str = f"{base_str} (документ не найден)"
value_str = (
f"{base_str} "
f"(документ не найден)"
)
else: