// edrpou-page.jsx — UI: автопідтягування контрагента за ЄДРПОУ, майстер «Новий // замовник» і майстер «Новий договір» (із графіком платежів). Дані — edrpou-data.jsx. // локальне поле форми (Babel-скрипти не ділять область — own copy) function OWField({ label, children, wide }) { return ( ); } // ─────────── Поле пошуку за ЄДРПОУ ─────────── function EdrpouLookup({ onFound, autoFocus }) { const [code, setCode] = React.useState(""); const [state, setState] = React.useState("idle"); // idle | loading | found | error const [msg, setMsg] = React.useState(""); const [rec, setRec] = React.useState(null); const ref = React.useRef(null); React.useEffect(() => { if (autoFocus && ref.current) ref.current.focus(); }, [autoFocus]); const run = async () => { setState("loading"); setMsg(""); setRec(null); const r = await window.lookupEdrpou(code); if (r.found) { setState("found"); setRec(r); setMsg(`${r.source} · перевірено ${window.formatDate(r.checkedAt)}`); onFound && onFound(r); } else { setState("error"); setMsg(r.error); } }; return (
{ setCode(e.target.value.replace(/\D/g, "").slice(0, 10)); setState("idle"); }} onKeyDown={(e) => e.key === "Enter" && run()} />
{state === "found" && rec && (
Знайдено в реєстрі {rec.status === "active" ? "Зареєстровано" : "Припинено"}
{rec.name}
ЄДРПОУ{rec.edrpou}
КВЕД{rec.kved} · {rec.kvedText}
Адреса{rec.address}
Керівник{rec.director} ({rec.directorRole})
ПДВ{rec.vatPayer ? "платник ПДВ" : "неплатник ПДВ"}
Реєстрація{window.formatDate(rec.registeredDate)}
{msg}
)} {state === "error" &&
{msg}
}
Демо-реєстр. На бекенді — реальний запит до ЄДР (Opendatabot / ДПС / Дія). Спробуйте код 43217890 або 42889106.
); } // ─────────── Майстер «Новий замовник» ─────────── function ClientWizard({ onClose }) { const [f, setF] = React.useState({ name: "", short: "", type: "legal", edrpou: "", address: "", director: "", vatPayer: true, cName: "", cRole: "", cPhone: "", cEmail: "", note: "" }); const set = (k, v) => setF(prev => ({ ...prev, [k]: v })); const onFound = (r) => setF(prev => ({ ...prev, name: r.name, short: r.short, type: r.type, edrpou: r.edrpou, address: r.address, director: r.director, vatPayer: !!r.vatPayer, cName: prev.cName || (r.director !== "—" ? r.director : ""), cRole: prev.cRole || r.directorRole || "", })); const valid = f.name.trim() && f.edrpou.trim(); const save = () => { window.addClient({ name: f.name.trim(), short: (f.short || f.name).trim(), type: f.type, edrpou: f.edrpou.trim(), address: f.address.trim(), director: f.director.trim(), vatPayer: f.vatPayer, contact: { name: f.cName.trim(), role: f.cRole.trim(), phone: f.cPhone.trim(), email: f.cEmail.trim() }, rating: 0, since: window.SYS_DATE, note: f.note.trim(), }); onClose(true); }; return (
onClose(false)}>
Новий замовник
введіть код ЄДРПОУ — реквізити підтягнуться автоматично
Картка замовника
set("name", e.target.value)} /> set("short", e.target.value)} /> set("edrpou", e.target.value)} /> set("address", e.target.value)} /> set("director", e.target.value)} /> set("cName", e.target.value)} placeholder="ПІБ" /> set("cRole", e.target.value)} /> set("cPhone", e.target.value)} placeholder="+380…" /> set("cEmail", e.target.value)} />
Реквізити можна відкоригувати вручну перед збереженням.
); } // ─────────── Майстер «Новий договір» ─────────── function ContractWizard({ onClose, presetClientId }) { window.useCounterparties(); const clients = window.DATA.CLIENTS; const SYS = window.SYS_DATE || "2026-05-29"; const [clientId, setClientId] = React.useState(presetClientId || (clients[0] && clients[0].id) || ""); const [newClient, setNewClient] = React.useState(false); const [f, setF] = React.useState({ number: window.nextContractNumber(), date: SYS, objName: "", objAddr: "", total: 1000000, vat: "included", tpl: "stages4", subject: "розроблення проєктно-кошторисної документації", }); const set = (k, v) => setF(prev => ({ ...prev, [k]: v })); // ── інтеграція з Робочим простором (Докс) ── const [doksKey, setDoksKey] = React.useState(""); const [doks, setDoks] = React.useState(null); const [doksState, setDoksState] = React.useState("idle"); // idle|loading|ok|error const [doksMsg, setDoksMsg] = React.useState(""); const pullDoks = async () => { setDoksState("loading"); setDoksMsg(""); const r = await window.doksPull(doksKey); if (!r.found) { setDoksState("error"); setDoksMsg(r.error); setDoks(null); return; } setDoks(r); setDoksState("ok"); setDoksMsg(`${r.source} · синхронізовано ${window.formatDate(r.syncedAt)}`); setF(prev => ({ ...prev, objName: r.objName, objAddr: r.objAddr, total: r.total, subject: r.kind === "supervision" ? "здійснення авторського нагляду" : "розроблення проєктної документації та супроводу експертизи", execEntity: r.vatBase ? "tov" : "fop", })); }; const client = window.getClient(clientId); const schedule = (doks && doks.schedule) ? doks.schedule : window.buildSchedule(f.tpl, Number(f.total) || 0, f.date); const m = window.formatMoney; const valid = clientId && f.objName.trim() && Number(f.total) > 0; const save = (openBlank) => { const objId = "obj-" + Date.now(); window.DATA.OBJECTS.push({ id: objId, code: f.number.replace("/", "-"), name: f.objName.trim(), client: client ? client.name : "", address: f.objAddr.trim(), workType: "new", _added: true, edessb: doks ? doks.edessb : undefined, }); const rec = window.addContract({ number: f.number.trim(), date: f.date, client: clientId, obj: objId, total: Number(f.total), status: "draft", subject: f.subject, vatMode: f.vat, schedule: schedule.map(s => ({ ...s })), execEntity: f.execEntity || "tov", doksKey: doks ? doks.key : null, kosht: doks ? doks.kosht : null, gip: doks ? doks.gip : null, consequenceClass: doks ? doks.consequenceClass : null, stage: doks ? doks.stage : null, taskFromDoks: doks ? doks.taskReady : false, }); onClose(rec, openBlank); }; if (newClient) { return { setNewClient(false); if (added) { window.dispatchEvent(new Event("ukrbud-cp")); const list = window.DATA.CLIENTS; setClientId(list[list.length - 1].id); } }} />; } return (
onClose(null)}>
Новий договір
№ {f.number} · {window.formatDate(f.date)}
Робочий простір · Докс підтягнути Завдання, кошторис і ціну з проєкту
{ setDoksKey(e.target.value); setDoksState("idle"); }} onKeyDown={e => e.key === "Enter" && pullDoks()} />
{doksState === "ok" && doks && (
{doks.projectName}
{doks.taskReady ? "Завдання готове → Дод. 1" : "Завдання не завершене"} {doks.consequenceClass} · {doks.stage} ГІП {doks.gip}
{doks.kosht.map((k, i) => ( ))}
{k.name}форма {k.form}{m(k.sum)} ₴
Договірна ціна (без ПДВ) → у договір і план фінансування{m(doks.total)} ₴
{doksMsg} · Виконавець: {doks.vatBase ? "ТОВ (платник ПДВ)" : "ФОП (неплатник ПДВ)"}
)} {doksState === "error" &&
{doksMsg}
} {doksState === "idle" &&
Демо. На бекенді — реальний API-обмін із Робочим простором. Спробуйте код О-1, УКП-2025-47 або АН-89.
}
{client && (
{client.name}
ЄДРПОУ {client.edrpou} · {client.vatPayer === false ? "неплатник ПДВ" : "платник ПДВ"}
{client.address &&
{client.address}
}
)} set("number", e.target.value)} /> set("date", e.target.value)} /> set("subject", e.target.value)} /> set("objName", e.target.value)} placeholder="напр. Капітальний ремонт…" /> set("objAddr", e.target.value)} /> set("total", +e.target.value)} />
{/* Прев'ю графіка */}
Графік платежів
{schedule.map((s, i) => ( ))}
{s.stage} {s.pct}% {m(s.amount)} ₴ до {window.formatDate(s.dueDate)}
Договір створиться як чернетка.
); } window.EdrpouLookup = EdrpouLookup; window.ClientWizard = ClientWizard; window.ContractWizard = ContractWizard;