// supervision-tasks.jsx — Задачі з договору авторського нагляду // Договір АН (status "supervision") породжує в ERP: // • 24 виїзди типу «Авт. нагляд» (графік, прив'язаний до етапів БМР) // • орг-задачі: наказ про групу АН, реєстрація в ЄДЕССБ, завести журнал // • підсумкові: підсумковий огляд + передача документації // Виконані візити живлять графік фінансування (акт по візитах). // Усі задачі — звичайні task-обʼєкти ERP; виїзди використовують модель visit. (function () { const { useState } = React; const Ico = window.Ico; const TODAY = window.TODAY_ISO || "2026-05-26"; const money = (n) => (window.formatMoney ? window.formatMoney(n) : n); // ── Етапи БМР (для угрупування візитів) ────────────────── const SUP_STAGES = [ { id: "demont", label: "Демонтаж · підготовка", color: "#9a857a" }, { id: "struct", label: "Конструктив · підсилення", color: "#7a6a4f" }, { id: "walls", label: "Перегородки · мокрі процеси", color: "#5e7a48" }, { id: "eng", label: "Інженерні мережі · приховані", color: "#1e6e8a" }, { id: "finish", label: "Опорядження · реставрація фасаду", color: "#a85a1c" }, { id: "equip", label: "Інженерне обладнання", color: "#6a3a8a" }, { id: "commiss", label: "Пусконалагодження · здача", color: "#A50F1F" }, ]; const SUP_STAGE_BY = Object.fromEntries(SUP_STAGES.map(s => [s.id, s])); // ── Графік 24 візитів: [no, дата, етап, мета, відповідальний(ERP), акт прихованих] ── const SUP_VISITS = [ [1, "2025-10-07", "demont", "Огляд стану несучих конструкцій після демонтажу. Звірка з АР.", "ok", false], [2, "2025-10-21", "struct", "Огляд армування підсилень фундаментів. Акт прихованих робіт.", "ap", true], [3, "2025-11-04", "struct", "Огляд армування монолітної ділянки перекриття. Акт прихованих робіт.", "ap", true], [4, "2025-11-18", "struct", "Контроль монтажу металоконструкцій (КМ).", "ap", false], [5, "2025-12-02", "walls", "Перевірка розбивки осей перегородок, вузли примикання до існуючих.", "tm", false], [6, "2025-12-16", "walls", "Контроль рівня чорнових підлог, ухилів.", "tm", false], [7, "2025-12-25", "eng", "Огляд прокладки внутрішніх мереж ВК до закриття. Акт прихованих робіт.", "vs", true], [8, "2026-01-13", "eng", "Огляд повітропроводів ОВ за підвісною стелею. Акт прихованих робіт.", "vs", true], [9, "2026-01-27", "eng", "Огляд електромонтажу прихованих ділянок (ЕО). Акт прихованих робіт.", "dl", true], [10, "2026-02-10", "eng", "Огляд монтажу слабкострумових систем (СС): пожежна сигналізація, СКД.", "dl", false], [11, "2026-02-24", "eng", "Гідравлічні випробування контурів опалення.", "vs", false], [12, "2026-03-10", "eng", "Огляд герметичності мереж ВК. Акт випробувань.", "vs", true], [13, "2026-03-24", "finish", "Контроль підготовки основ під чистові підлоги.", "tm", false], [14, "2026-04-07", "finish", "Гідроізоляція вологих зон, вузли примикання.", "tm", false], [15, "2026-04-21", "finish", "Огляд монтажу підвісних стель з ревізіями.", "tm", false], [16, "2026-05-05", "finish", "Контроль реставрації фасаду (пам'ятка-складова).", "tm", false], [17, "2026-05-12", "equip", "Огляд монтажу вузла ІТП. Перевірка приєднань.", "vs", false], [18, "2026-05-19", "equip", "Огляд монтажу вентиляційних установок.", "vs", false], [19, "2026-05-26", "commiss", "Пусконалагодження систем ОВ. Контроль повітрообмінів за проєктом.", "vs", false], [20, "2026-06-02", "commiss", "Випробування електроустановки. Заміри опору ізоляції, контури заземлення.", "dl", true], [21, "2026-06-09", "commiss", "Комплексне випробування інженерних систем. 72-годинний прогон.", "ok", false], [22, "2026-06-16", "commiss", "Огляд систем безпеки та сигналізації перед здачею.", "dl", false], [23, "2026-06-23", "commiss", "Контрольний огляд опоряджувальних робіт.", "tm", false], [24, "2026-06-30", "commiss", "Підсумковий огляд. Підсумковий запис у журналі. Передача документації.", "ok", false], ]; // ── Орг- та підсумкові задачі (звичайні task, не visit) ── const SUP_ORG_TASKS = [ { key: "order", title: "Видати наказ про утворення групи авторського нагляду", who: "ok", section: "АН", date: "2025-09-30", est: "1 год" }, { key: "edessb", title: "Зареєструвати наказ про авт. нагляд в ЄДЕССБ", who: "ok", section: "АН", date: "2025-10-02", est: "1 год" }, { key: "journal", title: "Завести журнал авторського нагляду (№ 89/2025)", who: "ap", section: "АН", date: "2025-10-03", est: "0.5 год" }, ]; const SUP_FINAL_TASKS = [ { key: "final-review", title: "Підсумковий огляд об'єкта перед здачею в експлуатацію", who: "ok", section: "АН", date: "2026-06-29", est: "3 год" }, { key: "handover", title: "Передача виконавчої документації авт. нагляду замовнику", who: "ap", section: "АН", date: "2026-07-02", est: "2 год" }, ]; window.SUP_STAGES = SUP_STAGES; window.SUP_VISITS = SUP_VISITS; // ── Орієнтовна вартість одного візиту (з «наглядових» етапів графіка) ── function supervisionValue(contract) { const sched = contract.schedule || []; const naglyad = sched.filter(s => /нагляд/i.test(s.stage)); const sum = naglyad.reduce((a, s) => a + (s.amount || 0), 0); return sum > 0 ? sum : (contract.total || 0); } function visitPrice(contract) { return Math.round(supervisionValue(contract) / SUP_VISITS.length); } const idFor = (contract) => "sup-" + (contract.number || contract.id).replace(/\D+/g, "") ; const prefixFor = (contract) => idFor(contract) + "-"; // ── Генерація комплекту задач ──────────────────────────── window.generateSupervisionTasks = function (contract) { const pref = prefixFor(contract); const obj = contract.obj; const objStage = "supervision"; const out = []; const dayOf = (iso) => String(new Date(iso).getDate()); const isPast = (iso) => iso < TODAY; const isToday = (iso) => iso === TODAY; // 1) Орг-задачі (усі в минулому → виконано) SUP_ORG_TASKS.forEach(t => { out.push({ id: pref + "org-" + t.key, title: t.title, obj, stage: objStage, section: t.section, assignee: t.who, assignees: [t.who], day: dayOf(t.date), est: t.est, status: isPast(t.date) ? "done" : "todo", note: null, subtasks: [], checklists: [], supMeta: { kind: "org", date: t.date }, }); }); // 2) 24 візити SUP_VISITS.forEach(([no, date, stageId, purpose, who, hiddenAct]) => { const past = isPast(date), today = isToday(date); const phase = past ? "done" : (today ? "ready" : "plan"); const status = past ? "done" : (today ? "live" : "todo"); const kit = window.buildKitFromIds ? window.buildKitFromIds((window.getVisitType ? window.getVisitType("supervision").defaultKit : [])) : []; if (past || today) kit.forEach(k => { k.packed = true; if (k.assetId) k.checkedOut = true; }); const didItems = [ { id: "d-look", text: "Перевірити критичні вузли за проєктом", done: past }, { id: "d-journal", text: "Запис у журнал авторського нагляду", done: past }, ]; if (hiddenAct) didItems.push({ id: "d-hidden", text: "Підписати акт огляду прихованих робіт", done: past }); out.push({ id: pref + "V" + String(no).padStart(2, "0"), kind: "visit", title: `Авт. нагляд · візит ${String(no).padStart(2, "0")}`, obj, stage: objStage, section: "АН", assignee: who, assignees: [who, "ok"].filter((v, i, a) => a.indexOf(v) === i), day: dayOf(date), time: null, est: "2 год", status, note: purpose, subtasks: [], checklists: [], visit: { typeId: "supervision", phase, team: [who, "ok"].filter((v, i, a) => a.indexOf(v) === i), kit, transport: { mode: "car", amount: 500, status: past ? "ready" : "todo", note: "Службове авто" }, coordination: { status: past ? "confirmed" : "draft", datetime: new Date(date).toLocaleDateString("uk-UA", { day: "2-digit", month: "short" }), contactName: "Наталія Корчинська", contactRole: "Гол. спец. відділу буд., Мін'юст", contactPhone: "+380 44 271 17 17", notifiedAt: past ? "завчасно" : null, }, closeout: { didItems, journalAN: past, photos: past ? 12 : 0, drawings: false, expensesActual: past ? 500 : null, files: [], }, }, supMeta: { kind: "visit", no, stageId, date, hiddenAct }, }); }); // 3) Підсумкові задачі SUP_FINAL_TASKS.forEach(t => { out.push({ id: pref + "fin-" + t.key, title: t.title, obj, stage: objStage, section: t.section, assignee: t.who, assignees: [t.who], day: dayOf(t.date), est: t.est, status: isPast(t.date) ? "done" : "todo", note: null, subtasks: [], checklists: [], supMeta: { kind: "final", date: t.date }, }); }); return out.map(t => (window.normalizeTask ? window.normalizeTask(t) : t)); }; // ── Прогрес авт. нагляду (рахується з tasks) ───────────── window.supervisionProgress = function (contract, tasks) { const pref = prefixFor(contract); const mine = (tasks || []).filter(t => t.id && t.id.indexOf(pref) === 0); const visits = mine.filter(t => t.kind === "visit").sort((a, b) => (a.supMeta?.no || 0) - (b.supMeta?.no || 0)); const orgs = mine.filter(t => t.supMeta && t.supMeta.kind === "org"); const finals = mine.filter(t => t.supMeta && t.supMeta.kind === "final"); const isDone = (t) => t.status === "done" || (t.visit && t.visit.phase === "done"); const visitsDone = visits.filter(isDone).length; const next = visits.find(t => !isDone(t)); // прогрес по етапах БМР const byStage = SUP_STAGES.map(s => { const list = visits.filter(v => v.supMeta && v.supMeta.stageId === s.id); return { stage: s, total: list.length, done: list.filter(isDone).length }; }).filter(x => x.total > 0); const price = visitPrice(contract); return { generated: mine.length > 0, visitsTotal: visits.length || SUP_VISITS.length, visitsDone, next, byStage, orgsDone: orgs.filter(isDone).length, orgsTotal: orgs.length, finalsDone: finals.filter(isDone).length, finalsTotal: finals.length, price, valueDone: visitsDone * price, valueTotal: (visits.length || SUP_VISITS.length) * price, visits, }; }; // ═══════════════ КОМПОНЕНТ: БЛОК АВТ. НАГЛЯДУ ═══════════════ function SupervisionBlock({ contract, tasks, onCreateTasks, onGoToTasks, onPrintAct }) { const p = window.supervisionProgress(contract, tasks); const obj = window.getObject(contract.obj); const generate = () => { const newTasks = window.generateSupervisionTasks(contract); onCreateTasks && onCreateTasks(newTasks); }; const fmtNextDate = p.next && p.next.supMeta ? new Date(p.next.supMeta.date).toLocaleDateString("uk-UA", { day: "2-digit", month: "long" }) : null; // акт за виконані візити → на «наглядовий» етап графіка const actStageIdx = (contract.schedule || []).findIndex(s => /нагляд/i.test(s.stage) && !s.paidDate); const canAct = p.visitsDone > 0 && actStageIdx >= 0; if (!p.generated) { return (
Авторський нагляд
Договір АН на об'єкт {obj?.code}. Графік — {window.SUP_VISITS.length} візитів за {window.SUP_STAGES.length} етапами БМР, плюс орг- та підсумкові задачі.
{window.SUP_VISITS.length} виїздів типу «Авт. нагляд» наказ про групу · ЄДЕССБ · журнал підсумковий огляд · передача документації
); } const pct = Math.round(p.visitsDone / p.visitsTotal * 100); return (
Авторський нагляд {pct}%
Графік згенеровано · {p.visitsTotal} візитів, {window.SUP_STAGES.length} етапів БМР
{/* зведення */}
{p.visitsDone}/ {p.visitsTotal}
візитів виконано
{p.orgsDone}/ {p.orgsTotal}
орг-задач
{money(p.valueDone)}
вартість виконаних (орієнт.)
{fmtNextDate || "—"}
{p.next ? `наступний · ${p.next.assignee ? (window.getTeam(p.next.assignee)?.initials || "") : ""}` : "усі візити закрито"}
{/* прогрес по етапах БМР */}
{p.byStage.map(({ stage, total, done }) => (
{stage.label} {done}/{total}
))}
{canAct && (
{p.visitsDone} виконаних візитів — можна оформити акт за «{contract.schedule[actStageIdx].stage}»
)}
); } window.SupervisionBlock = SupervisionBlock; })();