Загрузить файлы в «/»
This commit is contained in:
parent
14841c0835
commit
4fedd08e8f
|
|
@ -1,88 +1,88 @@
|
||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
"source": "Таблица основных типов ISO-контейнеров (внешние/внутренние размеры, дверной проём, масса, объём)",
|
"source": "Таблица основных типов ISO-контейнеров (внешние/внутренние размеры, дверной проём, масса, объём)",
|
||||||
"usage": "Сверка п.21 FCL (проходимость груза в дверной проём 20DV, 40DV, 40HC) и оценка payload/объёма."
|
"usage": "Сверка п.21 FCL (проходимость груза в дверной проём 20DV, 40DV, 40HC) и оценка payload/объёма."
|
||||||
},
|
},
|
||||||
"types": {
|
"types": {
|
||||||
"20DV": {
|
"20DV": {
|
||||||
"label": "20' Standard (Dry Cube) — соответствует 20DV в критериях",
|
"label": "20' Standard (Dry Cube) — соответствует 20DV в критериях",
|
||||||
"external_mm": { "length": 6058, "width": 2438, "height": 2591 },
|
"external_mm": { "length": 6058, "width": 2438, "height": 2591 },
|
||||||
"internal_mm": { "length": 5905, "width": 2350, "height": 2381 },
|
"internal_mm": { "length": 5905, "width": 2350, "height": 2381 },
|
||||||
"door_opening_mm": { "width": 2336, "height": 2291 },
|
"door_opening_mm": { "width": 2336, "height": 2291 },
|
||||||
"max_gross_kg": 30480,
|
"max_gross_kg": 30480,
|
||||||
"tare_kg": 2370,
|
"tare_kg": 2370,
|
||||||
"payload_kg": 28110,
|
"payload_kg": 28110,
|
||||||
"volume_m3": 33.2
|
"volume_m3": 33.2
|
||||||
},
|
},
|
||||||
"20HC": {
|
"20HC": {
|
||||||
"label": "20' High Cube",
|
"label": "20' High Cube",
|
||||||
"external_mm": { "length": 6058, "width": 2438, "height": 2896 },
|
"external_mm": { "length": 6058, "width": 2438, "height": 2896 },
|
||||||
"internal_mm": { "length": 5905, "width": 2350, "height": 2693 },
|
"internal_mm": { "length": 5905, "width": 2350, "height": 2693 },
|
||||||
"door_opening_mm": { "width": 2340, "height": 2597 },
|
"door_opening_mm": { "width": 2340, "height": 2597 },
|
||||||
"max_gross_kg": 30480,
|
"max_gross_kg": 30480,
|
||||||
"tare_kg": 2340,
|
"tare_kg": 2340,
|
||||||
"payload_kg": 28140,
|
"payload_kg": 28140,
|
||||||
"volume_m3": 37.5
|
"volume_m3": 37.5
|
||||||
},
|
},
|
||||||
"20HC_PW": {
|
"20HC_PW": {
|
||||||
"label": "20' High Cube Pallet Wide",
|
"label": "20' High Cube Pallet Wide",
|
||||||
"external_mm": { "length": 6058, "width": 2484, "height": 2896 },
|
"external_mm": { "length": 6058, "width": 2484, "height": 2896 },
|
||||||
"internal_mm": { "length": 5898, "width": 2426, "height": 2698 },
|
"internal_mm": { "length": 5898, "width": 2426, "height": 2698 },
|
||||||
"door_opening_mm": { "width": 2374, "height": 2585 },
|
"door_opening_mm": { "width": 2374, "height": 2585 },
|
||||||
"max_gross_kg": 30480,
|
"max_gross_kg": 30480,
|
||||||
"tare_kg": 2580,
|
"tare_kg": 2580,
|
||||||
"payload_kg": 27900,
|
"payload_kg": 27900,
|
||||||
"volume_m3": 38.6
|
"volume_m3": 38.6
|
||||||
},
|
},
|
||||||
"40DV": {
|
"40DV": {
|
||||||
"label": "40' Standard (Dry Van) — соответствует 40DV в критериях",
|
"label": "40' Standard (Dry Van) — соответствует 40DV в критериях",
|
||||||
"external_mm": { "length": 12192, "width": 2438, "height": 2591 },
|
"external_mm": { "length": 12192, "width": 2438, "height": 2591 },
|
||||||
"internal_mm": { "length": 12039, "width": 2350, "height": 2372 },
|
"internal_mm": { "length": 12039, "width": 2350, "height": 2372 },
|
||||||
"door_opening_mm": { "width": 2336, "height": 2291 },
|
"door_opening_mm": { "width": 2336, "height": 2291 },
|
||||||
"max_gross_kg": 30480,
|
"max_gross_kg": 30480,
|
||||||
"tare_kg": 4000,
|
"tare_kg": 4000,
|
||||||
"payload_kg": 26480,
|
"payload_kg": 26480,
|
||||||
"volume_m3": 67.8
|
"volume_m3": 67.8
|
||||||
},
|
},
|
||||||
"40HC": {
|
"40HC": {
|
||||||
"label": "40' High Cube — соответствует 40HC в критериях",
|
"label": "40' High Cube — соответствует 40HC в критериях",
|
||||||
"external_mm": { "length": 12192, "width": 2438, "height": 2896 },
|
"external_mm": { "length": 12192, "width": 2438, "height": 2896 },
|
||||||
"internal_mm": { "length": 12039, "width": 2350, "height": 2693 },
|
"internal_mm": { "length": 12039, "width": 2350, "height": 2693 },
|
||||||
"door_opening_mm": { "width": 2340, "height": 2597 },
|
"door_opening_mm": { "width": 2340, "height": 2597 },
|
||||||
"max_gross_kg": 32500,
|
"max_gross_kg": 32500,
|
||||||
"tare_kg": 4200,
|
"tare_kg": 4200,
|
||||||
"payload_kg": 28300,
|
"payload_kg": 28300,
|
||||||
"volume_m3": 76.5
|
"volume_m3": 76.5
|
||||||
},
|
},
|
||||||
"40HC_PW": {
|
"40HC_PW": {
|
||||||
"label": "40' High Cube Pallet Wide",
|
"label": "40' High Cube Pallet Wide",
|
||||||
"external_mm": { "length": 12192, "width": 2500, "height": 2896 },
|
"external_mm": { "length": 12192, "width": 2500, "height": 2896 },
|
||||||
"internal_mm": { "length": 12039, "width": 2432, "height": 2693 },
|
"internal_mm": { "length": 12039, "width": 2432, "height": 2693 },
|
||||||
"door_opening_mm": { "width": 2432, "height": 2597 },
|
"door_opening_mm": { "width": 2432, "height": 2597 },
|
||||||
"max_gross_kg": 35000,
|
"max_gross_kg": 35000,
|
||||||
"tare_kg": 4400,
|
"tare_kg": 4400,
|
||||||
"payload_kg": 30600,
|
"payload_kg": 30600,
|
||||||
"volume_m3": 79.3
|
"volume_m3": 79.3
|
||||||
},
|
},
|
||||||
"45HC_PW_A": {
|
"45HC_PW_A": {
|
||||||
"label": "45' High Cube Pallet Wide (Type A)",
|
"label": "45' High Cube Pallet Wide (Type A)",
|
||||||
"external_mm": { "length": 13716, "width": 2500, "height": 2750 },
|
"external_mm": { "length": 13716, "width": 2500, "height": 2750 },
|
||||||
"internal_mm": { "length": 13513, "width": 2444, "height": 2549 },
|
"internal_mm": { "length": 13513, "width": 2444, "height": 2549 },
|
||||||
"door_opening_mm": { "width": 2416, "height": 2439 },
|
"door_opening_mm": { "width": 2416, "height": 2439 },
|
||||||
"max_gross_kg": 34000,
|
"max_gross_kg": 34000,
|
||||||
"tare_kg": 4180,
|
"tare_kg": 4180,
|
||||||
"payload_kg": 29820,
|
"payload_kg": 29820,
|
||||||
"volume_m3": 85.1
|
"volume_m3": 85.1
|
||||||
},
|
},
|
||||||
"45HC_PW_B": {
|
"45HC_PW_B": {
|
||||||
"label": "45' High Cube Pallet Wide (Type B)",
|
"label": "45' High Cube Pallet Wide (Type B)",
|
||||||
"external_mm": { "length": 13716, "width": 2500, "height": 2896 },
|
"external_mm": { "length": 13716, "width": 2500, "height": 2896 },
|
||||||
"internal_mm": { "length": 13513, "width": 2444, "height": 2670 },
|
"internal_mm": { "length": 13513, "width": 2444, "height": 2670 },
|
||||||
"door_opening_mm": { "width": 2416, "height": 2580 },
|
"door_opening_mm": { "width": 2416, "height": 2580 },
|
||||||
"max_gross_kg": 34000,
|
"max_gross_kg": 34000,
|
||||||
"tare_kg": 5080,
|
"tare_kg": 5080,
|
||||||
"payload_kg": 28920,
|
"payload_kg": 28920,
|
||||||
"volume_m3": 89.5
|
"volume_m3": 89.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,178 +1,178 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Справочник ISO-контейнеров: промпт RAG и сверка п.21 FCL (дверной проём vs габариты груза)."""
|
"""Справочник ISO-контейнеров: промпт RAG и сверка п.21 FCL (дверной проём vs габариты груза)."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
_DIR = os.path.dirname(os.path.abspath(__file__))
|
_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
_JSON_PATH = os.path.join(_DIR, "container_iso_reference.json")
|
_JSON_PATH = os.path.join(_DIR, "container_iso_reference.json")
|
||||||
|
|
||||||
_iso_cache: Optional[Dict[str, Any]] = None
|
_iso_cache: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
|
||||||
def load_iso_reference() -> Dict[str, Any]:
|
def load_iso_reference() -> Dict[str, Any]:
|
||||||
global _iso_cache
|
global _iso_cache
|
||||||
if _iso_cache is not None:
|
if _iso_cache is not None:
|
||||||
return _iso_cache
|
return _iso_cache
|
||||||
try:
|
try:
|
||||||
with open(_JSON_PATH, "r", encoding="utf-8") as f:
|
with open(_JSON_PATH, "r", encoding="utf-8") as f:
|
||||||
_iso_cache = json.load(f)
|
_iso_cache = json.load(f)
|
||||||
except Exception:
|
except Exception:
|
||||||
_iso_cache = {"types": {}}
|
_iso_cache = {"types": {}}
|
||||||
return _iso_cache
|
return _iso_cache
|
||||||
|
|
||||||
|
|
||||||
def iso_reference_prompt_block() -> str:
|
def iso_reference_prompt_block() -> str:
|
||||||
"""Компактный блок для system prompt LLM (сверка п.21 и выбора контейнера)."""
|
"""Компактный блок для system prompt LLM (сверка п.21 и выбора контейнера)."""
|
||||||
data = load_iso_reference()
|
data = load_iso_reference()
|
||||||
types = data.get("types") or {}
|
types = data.get("types") or {}
|
||||||
if not types:
|
if not types:
|
||||||
return ""
|
return ""
|
||||||
lines: List[str] = [
|
lines: List[str] = [
|
||||||
"СПРАВОЧНИК ISO-КОНТЕЙНЕРОВ (мм, кг, м³) — для сверки с п.21 FCL (дверной проём) и оценки объёма/полезной нагрузки. "
|
"СПРАВОЧНИК ISO-КОНТЕЙНЕРОВ (мм, кг, м³) — для сверки с п.21 FCL (дверной проём) и оценки объёма/полезной нагрузки. "
|
||||||
"Не выдумывай числа; при расчётах используй только эти значения.",
|
"Не выдумывай числа; при расчётах используй только эти значения.",
|
||||||
"Ключи 20DV / 40DV / 40HC соответствуют формулировкам в критериях (проём 20DV, 40DV, 40HC).",
|
"Ключи 20DV / 40DV / 40HC соответствуют формулировкам в критериях (проём 20DV, 40DV, 40HC).",
|
||||||
]
|
]
|
||||||
for key in ("20DV", "20HC", "20HC_PW", "40DV", "40HC", "40HC_PW", "45HC_PW_A", "45HC_PW_B"):
|
for key in ("20DV", "20HC", "20HC_PW", "40DV", "40HC", "40HC_PW", "45HC_PW_A", "45HC_PW_B"):
|
||||||
t = types.get(key)
|
t = types.get(key)
|
||||||
if not isinstance(t, dict):
|
if not isinstance(t, dict):
|
||||||
continue
|
continue
|
||||||
door = t.get("door_opening_mm") or {}
|
door = t.get("door_opening_mm") or {}
|
||||||
inn = t.get("internal_mm") or {}
|
inn = t.get("internal_mm") or {}
|
||||||
dw = door.get("width")
|
dw = door.get("width")
|
||||||
dh = door.get("height")
|
dh = door.get("height")
|
||||||
il = inn.get("length")
|
il = inn.get("length")
|
||||||
iw = inn.get("width")
|
iw = inn.get("width")
|
||||||
ih = inn.get("height")
|
ih = inn.get("height")
|
||||||
vol = t.get("volume_m3")
|
vol = t.get("volume_m3")
|
||||||
pay = t.get("payload_kg")
|
pay = t.get("payload_kg")
|
||||||
lines.append(
|
lines.append(
|
||||||
f"- {key} ({t.get('label', '')}): дверь {dw}×{dh} мм; "
|
f"- {key} ({t.get('label', '')}): дверь {dw}×{dh} мм; "
|
||||||
f"внутри {il}×{iw}×{ih} мм; объём ~{vol} м³; payload ~{pay} кг"
|
f"внутри {il}×{iw}×{ih} мм; объём ~{vol} м³; payload ~{pay} кг"
|
||||||
)
|
)
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def _edges_mm_from_dim(d: Dict[str, Any]) -> Optional[Tuple[float, float, float]]:
|
def _edges_mm_from_dim(d: Dict[str, Any]) -> Optional[Tuple[float, float, float]]:
|
||||||
"""Длина/ширина/высота груза в мм (из см в shipment)."""
|
"""Длина/ширина/высота груза в мм (из см в shipment)."""
|
||||||
if not isinstance(d, dict):
|
if not isinstance(d, dict):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _num(v: Any) -> Optional[float]:
|
def _num(v: Any) -> Optional[float]:
|
||||||
if v is None:
|
if v is None:
|
||||||
return None
|
return None
|
||||||
if isinstance(v, (int, float)):
|
if isinstance(v, (int, float)):
|
||||||
return float(v)
|
return float(v)
|
||||||
s = str(v).strip().replace(" ", "").replace(",", ".")
|
s = str(v).strip().replace(" ", "").replace(",", ".")
|
||||||
try:
|
try:
|
||||||
return float(s)
|
return float(s)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
l = _num(d.get("length_cm"))
|
l = _num(d.get("length_cm"))
|
||||||
w = _num(d.get("width_cm"))
|
w = _num(d.get("width_cm"))
|
||||||
h = _num(d.get("height_cm"))
|
h = _num(d.get("height_cm"))
|
||||||
if l is None or w is None or h is None:
|
if l is None or w is None or h is None:
|
||||||
return None
|
return None
|
||||||
return l * 10.0, w * 10.0, h * 10.0
|
return l * 10.0, w * 10.0, h * 10.0
|
||||||
|
|
||||||
|
|
||||||
def _box_fits_door(
|
def _box_fits_door(
|
||||||
edges_mm: Tuple[float, float, float],
|
edges_mm: Tuple[float, float, float],
|
||||||
door_w_mm: float,
|
door_w_mm: float,
|
||||||
door_h_mm: float,
|
door_h_mm: float,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Два наименьших ребра груза должны укладываться в ширину/высоту проёма (сортировка)."""
|
"""Два наименьших ребра груза должны укладываться в ширину/высоту проёма (сортировка)."""
|
||||||
s0, s1, s2 = sorted(edges_mm)
|
s0, s1, s2 = sorted(edges_mm)
|
||||||
dw, dh = sorted((door_w_mm, door_h_mm))
|
dw, dh = sorted((door_w_mm, door_h_mm))
|
||||||
return s0 <= dw + 0.5 and s1 <= dh + 0.5
|
return s0 <= dw + 0.5 and s1 <= dh + 0.5
|
||||||
|
|
||||||
|
|
||||||
def _codes_from_container_type(ct: str) -> List[str]:
|
def _codes_from_container_type(ct: str) -> List[str]:
|
||||||
"""Из строки вида «2×40HC, 1×20DC» извлекает коды из справочника."""
|
"""Из строки вида «2×40HC, 1×20DC» извлекает коды из справочника."""
|
||||||
if not isinstance(ct, str) or not ct.strip():
|
if not isinstance(ct, str) or not ct.strip():
|
||||||
return ["20DV", "40DV", "40HC"]
|
return ["20DV", "40DV", "40HC"]
|
||||||
t = ct.upper()
|
t = ct.upper()
|
||||||
found: List[str] = []
|
found: List[str] = []
|
||||||
|
|
||||||
def _add(size: str, kind: Optional[str]) -> None:
|
def _add(size: str, kind: Optional[str]) -> None:
|
||||||
k = (kind or "DV").upper()
|
k = (kind or "DV").upper()
|
||||||
if size == "20":
|
if size == "20":
|
||||||
code = "20" + ("HC" if k in ("HC", "HQ") else "DV")
|
code = "20" + ("HC" if k in ("HC", "HQ") else "DV")
|
||||||
elif size == "40":
|
elif size == "40":
|
||||||
code = "40" + ("HC" if k in ("HC", "HQ") else "DV")
|
code = "40" + ("HC" if k in ("HC", "HQ") else "DV")
|
||||||
elif size == "45":
|
elif size == "45":
|
||||||
code = "45HC_PW_B"
|
code = "45HC_PW_B"
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if code not in found:
|
if code not in found:
|
||||||
found.append(code)
|
found.append(code)
|
||||||
|
|
||||||
for m in re.finditer(
|
for m in re.finditer(
|
||||||
r"(\d{1,3})\s*[x×х**]\s*(20|40|45)\s*(DC|DV|GP|HC|HQ|OT|FR|RF|RH)?",
|
r"(\d{1,3})\s*[x×х**]\s*(20|40|45)\s*(DC|DV|GP|HC|HQ|OT|FR|RF|RH)?",
|
||||||
t,
|
t,
|
||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
):
|
):
|
||||||
_add(m.group(2), m.group(3))
|
_add(m.group(2), m.group(3))
|
||||||
for m in re.finditer(
|
for m in re.finditer(
|
||||||
r"\b(20|40|45)\s*(DC|DV|GP|HC|HQ|OT|FR|RF|RH)\b",
|
r"\b(20|40|45)\s*(DC|DV|GP|HC|HQ|OT|FR|RF|RH)\b",
|
||||||
t,
|
t,
|
||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
):
|
):
|
||||||
_add(m.group(1), m.group(2))
|
_add(m.group(1), m.group(2))
|
||||||
if not found:
|
if not found:
|
||||||
return ["20DV", "40DV", "40HC"]
|
return ["20DV", "40DV", "40HC"]
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
|
||||||
def door_clearance_summary(shipment: Dict[str, Any]) -> str:
|
def door_clearance_summary(shipment: Dict[str, Any]) -> str:
|
||||||
"""
|
"""
|
||||||
Текст для п.21: сверка габаритов мест с дверным проёмом 20DV/40DV/40HC из справочника.
|
Текст для п.21: сверка габаритов мест с дверным проёмом 20DV/40DV/40HC из справочника.
|
||||||
"""
|
"""
|
||||||
data = load_iso_reference()
|
data = load_iso_reference()
|
||||||
types = data.get("types") or {}
|
types = data.get("types") or {}
|
||||||
dims = shipment.get("dimensions")
|
dims = shipment.get("dimensions")
|
||||||
if not isinstance(dims, list) or not dims:
|
if not isinstance(dims, list) or not dims:
|
||||||
return (
|
return (
|
||||||
"Нет габаритов грузовых мест — проверка проходимости по справочнику дверных проёмов "
|
"Нет габаритов грузовых мест — проверка проходимости по справочнику дверных проёмов "
|
||||||
"(20DV 2336×2291 мм, 40DV 2336×2291 мм, 40HC 2340×2597 мм) невозможна."
|
"(20DV 2336×2291 мм, 40DV 2336×2291 мм, 40HC 2340×2597 мм) невозможна."
|
||||||
)
|
)
|
||||||
|
|
||||||
ct_raw = (shipment.get("container_type") or "").strip()
|
ct_raw = (shipment.get("container_type") or "").strip()
|
||||||
codes = _codes_from_container_type(ct_raw)
|
codes = _codes_from_container_type(ct_raw)
|
||||||
parts: List[str] = []
|
parts: List[str] = []
|
||||||
for i, d in enumerate(dims, start=1):
|
for i, d in enumerate(dims, start=1):
|
||||||
edges = _edges_mm_from_dim(d)
|
edges = _edges_mm_from_dim(d)
|
||||||
if not edges:
|
if not edges:
|
||||||
parts.append(f"Место {i}: нет полных Д×Ш×В в см.")
|
parts.append(f"Место {i}: нет полных Д×Ш×В в см.")
|
||||||
continue
|
continue
|
||||||
place_bits: List[str] = []
|
place_bits: List[str] = []
|
||||||
for code in codes:
|
for code in codes:
|
||||||
spec = types.get(code)
|
spec = types.get(code)
|
||||||
if not isinstance(spec, dict):
|
if not isinstance(spec, dict):
|
||||||
continue
|
continue
|
||||||
door = spec.get("door_opening_mm") or {}
|
door = spec.get("door_opening_mm") or {}
|
||||||
dw = float(door.get("width") or 0)
|
dw = float(door.get("width") or 0)
|
||||||
dh = float(door.get("height") or 0)
|
dh = float(door.get("height") or 0)
|
||||||
if dw <= 0 or dh <= 0:
|
if dw <= 0 or dh <= 0:
|
||||||
continue
|
continue
|
||||||
ok = _box_fits_door(edges, dw, dh)
|
ok = _box_fits_door(edges, dw, dh)
|
||||||
label = spec.get("label", code)
|
label = spec.get("label", code)
|
||||||
status = "проходит" if ok else "не проходит"
|
status = "проходит" if ok else "не проходит"
|
||||||
place_bits.append(f"{code} ({int(dw)}×{int(dh)} мм): {status}")
|
place_bits.append(f"{code} ({int(dw)}×{int(dh)} мм): {status}")
|
||||||
if place_bits:
|
if place_bits:
|
||||||
parts.append(f"Место {i} (рёбра {int(min(edges))}×{int(sorted(edges)[1])}×{int(max(edges))} мм): " + "; ".join(place_bits))
|
parts.append(f"Место {i} (рёбра {int(min(edges))}×{int(sorted(edges)[1])}×{int(max(edges))} мм): " + "; ".join(place_bits))
|
||||||
else:
|
else:
|
||||||
parts.append(f"Место {i}: нет данных дверного проёма в справочнике для кодов {', '.join(codes)}.")
|
parts.append(f"Место {i}: нет данных дверного проёма в справочнике для кодов {', '.join(codes)}.")
|
||||||
|
|
||||||
note = (
|
note = (
|
||||||
" Оценка: два наименьших габарита сравниваются с шириной и высотой дверного проёма (ориентация коробки)."
|
" Оценка: два наименьших габарита сравниваются с шириной и высотой дверного проёма (ориентация коробки)."
|
||||||
)
|
)
|
||||||
prefix = (
|
prefix = (
|
||||||
f"Справочник дверных проёмов (п.21). Контейнеры в заявке: {ct_raw or 'не указаны — проверка по 20DV/40DV/40HC'}. "
|
f"Справочник дверных проёмов (п.21). Контейнеры в заявке: {ct_raw or 'не указаны — проверка по 20DV/40DV/40HC'}. "
|
||||||
)
|
)
|
||||||
return prefix + " ".join(parts) + note
|
return prefix + " ".join(parts) + note
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
83
proxy.py
83
proxy.py
|
|
@ -1,92 +1,41 @@
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.responses import JSONResponse, Response
|
from fastapi.responses import JSONResponse, Response
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
try:
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
UPSTREAM_URL = os.getenv(
|
OPENROUTER_KEY = "sk-9cfed30eb6894df5b9a3ffb4b1fb956d"
|
||||||
"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)
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/v1/chat/completions")
|
@app.post("/v1/chat/completions")
|
||||||
async def proxy(req: Request):
|
async def proxy(req: Request):
|
||||||
if not OPENROUTER_KEY:
|
body = await req.json()
|
||||||
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:
|
async with httpx.AsyncClient() as client:
|
||||||
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:
|
|
||||||
try:
|
try:
|
||||||
r = await client.post(
|
r = await client.post(
|
||||||
UPSTREAM_URL,
|
"https://api.deepseek.com/chat/completions",
|
||||||
headers={
|
headers={
|
||||||
"Authorization": f"Bearer {OPENROUTER_KEY}",
|
"Authorization": f"Bearer {OPENROUTER_KEY}",
|
||||||
"HTTP-Referer": os.getenv("LLM_HTTP_REFERER", "http://localhost"),
|
"HTTP-Referer": "http://localhost",
|
||||||
"X-Title": os.getenv("LLM_PROXY_X_TITLE", "rag-engine"),
|
"model": "deepseek-v4-flash",
|
||||||
|
"X-Title": "rag-engine"
|
||||||
},
|
},
|
||||||
json=body,
|
json=body,
|
||||||
timeout=_client_timeout(),
|
timeout=300,
|
||||||
)
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
logger.error(
|
|
||||||
"LLM upstream unreachable (%s): %s",
|
|
||||||
UPSTREAM_URL,
|
|
||||||
e,
|
|
||||||
exc_info=True,
|
|
||||||
)
|
)
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
# Ошибка сети / таймаут
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=502,
|
status_code=502,
|
||||||
content={
|
content={"error": "Bad gateway", "details": str(e)},
|
||||||
"error": "Bad gateway",
|
|
||||||
"details": str(e),
|
|
||||||
"upstream": UPSTREAM_URL,
|
|
||||||
"hint": "Проверьте VPN, DNS, доступность хоста и переменную LLM_UPSTREAM_URL.",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if r.status_code >= 500:
|
print(r.text)
|
||||||
logger.warning(
|
|
||||||
"LLM upstream HTTP %s, body (prefix): %s",
|
|
||||||
r.status_code,
|
|
||||||
(r.text or "")[:800],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Если бэкенд всегда отвечает JSON:
|
||||||
|
# return JSONResponse(status_code=r.status_code, content=r.json())
|
||||||
|
|
||||||
|
# Универсальный вариант — пробрасываем «как есть»
|
||||||
return Response(
|
return Response(
|
||||||
content=r.content,
|
content=r.content,
|
||||||
status_code=r.status_code,
|
status_code=r.status_code,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Автомобильная перевозка (LTL)",
|
||||||
|
"employee_email": "denis.fomin@timnet.ch, Yana.Schkabrova@timnet.ch",
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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 порт",
|
||||||
|
"морской группаж",
|
||||||
|
"неполный контейнер море"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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 поезд",
|
||||||
|
"группаж жд",
|
||||||
|
"железная дорога сборный"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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",
|
||||||
|
"морской и жд этап",
|
||||||
|
"порт и станция сборный"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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 поезд",
|
||||||
|
"вагон контейнер"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue