// MDM SORP — Sourcing screen // Замена «Матрицы возможностей». Воркфлоу: // 1. Категории что мы можем поставить → видны: поставщики, продукты, отрасли // 2. ТН ВЭД per категория → кнопка «Найти клиентов» дёргает таможенный API // 3. Список найденных импортёров → cleanup кнопкой → «В работу» = call-list // 4. Кнопка «Подготовить скрипт» = LLM генерит скрипт первичного звонка (function () { const { useState, useMemo } = React; const useToast = window.useToast; // ──────────────────────────────────────────────────────────── // Sample data: categories we can supply (~ subset of 34 categories from xlsx) // ──────────────────────────────────────────────────────────── const CATEGORIES = [ { id: "CAT-01", group: "Разделение и фильтрование", name: "Центрифуги", hs: ["8421.11", "8421.19", "8421.21"], suppliers: 12, products: 89, industries: ["Нефтегаз", "Химия", "Фарма", "ГОК"], rank: "A", note: "Эксклюзив с Xiangtan + Jieda. Сильное направление." }, { id: "CAT-02", group: "Разделение и фильтрование", name: "Фильтры и фильтр-прессы", hs: ["8421.21", "8421.29", "8421.99"], suppliers: 9, products: 56, industries: ["Химия", "Пищевая", "ГОК"], rank: "A" }, { id: "CAT-03", group: "Сушка и термообработка", name: "Сушилки и печи", hs: ["8419.31", "8419.39", "8419.81"], suppliers: 7, products: 34, industries: ["Химия", "Фарма", "Пищевая"], rank: "B" }, { id: "CAT-04", group: "Реакторное оборудование", name: "Реакторы и автоклавы", hs: ["8419.89"], suppliers: 5, products: 22, industries: ["Химия", "Фарма"], rank: "B" }, { id: "CAT-05", group: "Колонны и сепарация", name: "Ректификационные колонны", hs: ["8419.40"], suppliers: 4, products: 18, industries: ["Нефтегаз", "Химия"], rank: "B" }, { id: "CAT-06", group: "Тепло- и массообмен", name: "Теплообменники", hs: ["8419.50"], suppliers: 11, products: 67, industries: ["Нефтегаз", "Химия", "Энергетика"], rank: "A" }, { id: "CAT-07", group: "Тепло- и массообмен", name: "Выпарные и кристаллизаторы", hs: ["8419.40", "8419.89"], suppliers: 6, products: 28, industries: ["Химия", "Минудобрения"], rank: "B" }, { id: "CAT-09", group: "Насосы, компрессоры, турбины", name: "Насосы", hs: ["8413.50", "8413.60", "8413.70", "8413.81"], suppliers: 14, products: 78, industries: ["Нефтегаз", "Химия", "ЖКХ"], rank: "A" }, { id: "CAT-10", group: "Насосы, компрессоры, турбины", name: "Компрессоры", hs: ["8414.30", "8414.40", "8414.80"], suppliers: 8, products: 41, industries: ["Нефтегаз", "Химия"], rank: "B" }, { id: "CAT-13", group: "Разделение и фильтрование", name: "Циклоны и гидроциклоны", hs: ["8421.39"], suppliers: 3, products: 14, industries: ["ГОК", "Цемент"], rank: "C" }, { id: "CAT-14", group: "Измельчение и классификация", name: "Дробилки и мельницы", hs: ["8474.10", "8474.20"], suppliers: 6, products: 25, industries: ["ГОК", "Цемент", "Строй"], rank: "B" }, { id: "CAT-22", group: "Запорно-регулирующая арматура", name: "Арматура промышленная", hs: ["8481.10", "8481.20", "8481.80"], suppliers: 9, products: 88, industries: ["Нефтегаз", "ЖКХ", "Энергетика"], rank: "A" }, ]; // Mock customs lookup result (when user clicks «Найти клиентов») // Realistic Russian importers per HS-code. const CUSTOMS_HITS = { "8421.11": [ { inn: "5403028262", name: "Сибур-Кстово", region: "Нижегородская обл.", year: 2024, last_qty: "3 ед.", last_supplier: "GEA Westfalia (DE)", customer_score: 0.92 }, { inn: "6164103725", name: "ЛУКОЙЛ-Югнефтепродукт", region: "Ростовская обл.", year: 2024, last_qty: "1 ед.", last_supplier: "Alfa Laval (SE)", customer_score: 0.88 }, { inn: "5902291800", name: "Метафракс Кемикалс", region: "Пермский край", year: 2023, last_qty: "2 ед.", last_supplier: "Andritz (AT)", customer_score: 0.95, is_existing: true }, { inn: "5256017920", name: "Каустик", region: "Нижегородская обл.", year: 2024, last_qty: "1 ед.", last_supplier: "Flottweg (DE)", customer_score: 0.81 }, { inn: "6677001119", name: "Уралхимпласт", region: "Свердловская обл.", year: 2023, last_qty: "1 ед.", last_supplier: "Andritz (AT)", customer_score: 0.78 }, { inn: "0276995412", name: "Сода-Уфа", region: "Башкортостан", year: 2024, last_qty: "1 ед.", last_supplier: "GEA Westfalia (DE)", customer_score: 0.73 }, { inn: "3435060776", name: "ТМК-Волжский", region: "Волгоградская обл.", year: 2023, last_qty: "1 ед.", last_supplier: "Pieralisi (IT)", customer_score: 0.69, is_existing: true }, ], "8421.19": [ { inn: "5256037200", name: "СИБУР-Полимер", region: "Тверская обл.", year: 2024, last_qty: "1 ед.", last_supplier: "Hiller (DE)", customer_score: 0.87 }, { inn: "5005009305", name: "Метаклэй", region: "Москва", year: 2023, last_qty: "1 ед.", last_supplier: "Pieralisi (IT)", customer_score: 0.71 }, ], "8413.50": [ { inn: "7707083893", name: "Газпромнефть-Снабжение", region: "Москва", year: 2024, last_qty: "12 ед.", last_supplier: "Sulzer (CH)", customer_score: 0.91 }, { inn: "2632001144", name: "Невинномысский Азот", region: "Ставропольский край", year: 2024, last_qty: "5 ед.", last_supplier: "KSB (DE)", customer_score: 0.86 }, { inn: "5052017580", name: "Щёкиноазот", region: "Тульская обл.", year: 2023, last_qty: "3 ед.", last_supplier: "Grundfos (DK)", customer_score: 0.83 }, { inn: "7714065881", name: "ФосАгро-Череповец", region: "Вологодская обл.", year: 2024, last_qty: "7 ед.", last_supplier: "KSB (DE)", customer_score: 0.89 }, ], }; // ──────────────────────────────────────────────────────────── // Main screen // ──────────────────────────────────────────────────────────── function SourcingScreen() { const [selected, setSelected] = useState(CATEGORIES[0].id); const [hits, setHits] = useState([]); // search results const [cleaned, setCleaned] = useState(null); // post-cleanup result const [callList, setCallList] = useState(null);// final list ready to call const [script, setScript] = useState(null); // generated script const [loading, setLoading] = useState(null); // 'search' | 'clean' | 'script' | null const [groupFilter, setGroupFilter] = useState("all"); const [industryFilter, setIndustryFilter] = useState("all"); const [hsSelected, setHsSelected] = useState({}); // {hs: bool} const toast = useToast(); const cat = CATEGORIES.find(c => c.id === selected); const groups = [...new Set(CATEGORIES.map(c => c.group))]; const industries = [...new Set(CATEGORIES.flatMap(c => c.industries))]; const filteredCats = CATEGORIES.filter(c => (groupFilter === "all" || c.group === groupFilter) && (industryFilter === "all" || c.industries.includes(industryFilter)) ); // Reset downstream state on category change const selectCat = (id) => { setSelected(id); setHits([]); setCleaned(null); setCallList(null); setScript(null); const c = CATEGORIES.find(c => c.id === id); // Pre-select first HS code setHsSelected(c ? { [c.hs[0]]: true } : {}); }; // Stub API calls — simulate with timers const findClients = () => { const codes = Object.keys(hsSelected).filter(k => hsSelected[k]); if (!codes.length) { toast("Выбери хотя бы один ТН ВЭД"); return; } setLoading("search"); setTimeout(() => { const all = codes.flatMap(code => (CUSTOMS_HITS[code] || []).map(h => ({ ...h, hs: code }))); setHits(all); setCleaned(null); setCallList(null); setScript(null); setLoading(null); toast(`Найдено ${all.length} импортёров по ${codes.length} коду(ам)`, { icon: "sparkle" }); }, 800); }; const cleanList = () => { if (!hits.length) return; setLoading("clean"); setTimeout(() => { // Remove existing customers + low score const c = hits.filter(h => !h.is_existing && h.customer_score >= 0.75); setCleaned(c); setCallList(null); setScript(null); setLoading(null); toast(`Очищено: ${hits.length - c.length} (существующие + score < 0.75)`, { icon: "check" }); }, 500); }; const sendToCalls = () => { const list = cleaned || hits; if (!list.length) return; setCallList(list); toast(`${list.length} компаний отправлены в очередь прозвона`, { icon: "check" }); }; const generateScript = () => { if (!callList || !callList.length) { toast("Сначала отправь список в работу"); return; } setLoading("script"); setTimeout(() => { const sample = callList[0]; setScript({ subject: `${cat.name} для ${sample.region.split(" ")[0]}`, intro: `Здравствуйте, ${sample.name}. Меня зовут [имя], я представляю SORP Group — мы поставляем промышленное оборудование напрямую от китайских заводов.`, hook: `Заметили в открытой таможенной статистике, что вы в ${sample.year} году импортировали ${cat.name.toLowerCase()} (ТН ВЭД ${sample.hs}) от ${sample.last_supplier}. Текущая ситуация с поставками из ЕС изменила правила игры.`, pitch: `Мы выстроили прямые контракты с ${cat.suppliers} ${cat.name.toLowerCase().includes("центрифуг") ? "ведущими китайскими производителями центрифуг" : "профильными заводами"} (в том числе по эксклюзиву). По ${cat.name.toLowerCase()} ${cat.hs[0]} — у нас 60-70% от европейских цен при сопоставимом качестве и legal-схеме параллельного импорта через UAE-юрлицо.`, objection: `Если возражение «уже работаем с привычным поставщиком» — предложить пилотную поставку небольшой партии для оценки качества + TCO calculation.`, cta: `Готов прислать наш каталог по ${cat.name.toLowerCase()} и три актуальных кейса по вашей отрасли. Кому из ваших коллег это лучше адресовать?`, }); setLoading(null); toast("Скрипт готов", { icon: "sparkle" }); }, 1200); }; return (
{/* Header */}

Sourcing

Что мы можем поставить · поиск новых клиентов по ТН ВЭД через таможенные API
{CATEGORIES.length}категорий
{CATEGORIES.reduce((s, c) => s + c.suppliers, 0)}поставщиков
{CATEGORIES.reduce((s, c) => s + c.products, 0)}продуктов
{/* Filters */}
({ v: g, l: g }))]} onChange={setGroupFilter}/> ({ v: i, l: i }))]} onChange={setIndustryFilter}/>
{filteredCats.length} категорий
{/* Two-column layout: categories left, workflow right */}
{/* Categories list */}
{filteredCats.map(c => ( ))}
{/* Workflow panel */}
{cat && ( <> {/* Selected category card */}
{cat.group} · {cat.id}

{cat.name}

Ранг {cat.rank}
{cat.note &&
{cat.note}
}
Поставщики
{cat.suppliers} заводов
Продукты в каталоге
{cat.products} позиций
Покупающие отрасли
{cat.industries.map(i => {i})}
{/* Step 1: HS codes */} 0}>
{cat.hs.map(code => ( ))}
Дёргает API ФТС / VPS / customs.ru — возвращает компании которые импортировали данный код за 2 года
{/* Step 2: cleanup */} {hits.length > 0 && (
Найдено {hits.length} компаний {cleaned && · после очистки {cleaned.length}}
{(cleaned || hits).map(h => ( ))}
ИНН Компания Регион Импортировал Прошлый поставщик Score
{h.inn} {h.name} {h.is_existing && Уже клиент} {h.region} {h.last_qty} · {h.year} {h.last_supplier} = 0.85 ? " high" : h.customer_score >= 0.7 ? " mid" : " low")}> {Math.round(h.customer_score * 100)}
{!cleaned && ( )} {cleaned && (
Очищено · убрано: {hits.length - cleaned.length}
)}
)} {/* Step 3: send to call queue */} {(cleaned || hits.length > 0) && ( {!callList ? ( <>
Список {(cleaned || hits).length} компаний попадёт в очередь прозвона. Распределение по PM по гео или равномерно.
) : (
Отправлено {callList.length} компаний. Назначены PM-ам.
)}
)} {/* Step 4: script */} {callList && ( {!script ? ( ) : (
Тема{script.subject}
Открытие{script.intro}
Зацепка{script.hook}
Предложение{script.pitch}
Возражение{script.objection}
CTA{script.cta}
)}
)} )}
); } function Step({ n, title, done, children }) { return (
{done ? : n} {title}
{children}
); } function FilterDropdown({ label, value, options, onChange }) { return ( ); } window.SourcingScreen = SourcingScreen; })();