// career-data.jsx — кар'єрна модель команди // Логіка кваліфікаційного зростання за сертифікатами: // • Архітектор + сертифікат → переведення на ГАП (якщо посада вакантна), // інакше — лише надбавка за сертифікат. // • Інженер-проєктувальник: СС1 → ІІ категорії, СС2 → І категорії, СС3 → провідний. // • Кошторисник / технолог: сертифікат без класу СС → одразу надбавка. // // careerState(person) повертає поточний/цільовий стан + дію (промоушн) і // прив'язку до HR-шаблону задач, який оформлює перехід. (function () { // Фіксовані надбавки до окладу (₴) const SALARY_BUMP = { architectToGap: 5000, // архітектор → ГАП engineerStep: 3000, // за кожен щабель категорії certBonus: 3000, // надбавка за сертифікат без зміни посади }; window.CAREER_BUMP = SALARY_BUMP; const CLASS_RANK = { "СС1": 1, "СС2": 2, "СС3": 3 }; // Щаблі інженерної категорії: ранг → ярлик і клас наслідків, що його відкриває const ENGINEER_LADDER = [ { rank: 0, label: "без категорії", needs: null }, { rank: 1, label: "ІІ категорії", needs: "СС1" }, { rank: 2, label: "І категорії", needs: "СС2" }, { rank: 3, label: "Провідний інженер-проєктувальник", needs: "СС3" }, ]; window.ENGINEER_LADDER = ENGINEER_LADDER; // --- Класифікація треку за посадою / роллю / типом зайнятості --- function careerTrack(person) { if (!person) return "none"; if (person.employment === "contractor") return "contractor"; const r = ((person.positionOfficial || "") + " " + (person.role || "")).toLowerCase(); if (/гап|головний архітектор/.test(r)) return "gap"; if (/гіп|головний інженер проєкту|директор/.test(r)) return "gip"; if (/архітектор/.test(r)) return "architect"; if (/кошторис/.test(r)) return "specialist"; if (/технолог/.test(r)) return "specialist"; if (/інженер|конструктор|проєктувальник/.test(r)) return "engineer"; return "none"; } window.careerTrack = careerTrack; const TRACK_LABELS = { gip: "Керівництво · ГІП", gap: "Головний архітектор проєкту", architect: "Архітектурний напрям", engineer: "Інженер-проєктувальник", specialist: "Спеціаліст із сертифікацією", contractor: "Субпідряд (ЦПХ)", none: "Поза кваліфікаційною прогресією", }; function highestCertClass(person) { let max = 0, has = false; (person.qualCerts || []).forEach(qc => { has = true; const v = CLASS_RANK[(qc.class || "").trim()] || 0; if (v > max) max = v; }); return { rank: max, has }; } // Поточний ранг інженера з тексту посади function engineerCurrentRank(person) { const r = (person.positionOfficial || person.role || "").toLowerCase(); if (/провідн/.test(r)) return 3; if (/(іі|ii|2)\s*катег/.test(r)) return 1; // ІІ категорії if (/(і|i|1)\s*катег/.test(r)) return 2; // І категорії return 0; } // Чи зайнята вже посада ГАП кимось у команді function gapExists() { return (window.DATA.TEAM || []).some(p => careerTrack(p) === "gap"); } function moneyApplies(person) { return person.salaryModel === "fixed" || person.salaryModel === "fixed_bonus"; } // === Головна функція === // Повертає: { track, trackLabel, status, currentPosition, certInfo, // promotion?: { kind, targetPosition, salaryDelta, reason, templateId, badge }, // nextStep?: { text, requires } } function careerState(person) { const track = careerTrack(person); const trackLabel = TRACK_LABELS[track] || "—"; const base = { track, trackLabel, currentPosition: person.positionOfficial || person.role || "—", promotion: null, nextStep: null, status: "compliant", }; if (track === "gip") { return { ...base, status: "top", topNote: "Вища керівна позиція. Кваліфікаційна прогресія завершена (ГІП, СС3)." }; } if (track === "gap") { return { ...base, status: "top", topNote: "Головний архітектор проєкту — вершина архітектурного напряму." }; } if (track === "contractor") { return { ...base, status: "na", note: "Працює за договором ЦПХ — поза штатним розписом і кваліфікаційною драбиною компанії." }; } if (track === "none") { return { ...base, status: "na", note: "Для цієї посади кар'єрні переходи за сертифікатом не налаштовані." }; } const cert = highestCertClass(person); if (track === "architect") { if (!cert.has) { return { ...base, status: "pending-cert", nextStep: { text: "Отримати кваліфікаційний сертифікат архітектора", requires: "сертифікат архітектора → переведення на ГАП (+5 000 ₴)" } }; } if (!gapExists()) { return { ...base, status: "promotion", promotion: { kind: "gap", targetPosition: "Головний архітектор проєкту", targetRole: "ГАП", salaryDelta: moneyApplies(person) ? SALARY_BUMP.architectToGap : 0, reason: "Перший сертифікований архітектор → переведення на посаду ГАП.", templateId: "hrt-architect-gap", badge: "Доступне переведення → ГАП", } }; } // ГАП уже зайнятий → лише надбавка return { ...base, status: "promotion", promotion: { kind: "cert", targetPosition: person.positionOfficial || person.role, salaryDelta: moneyApplies(person) ? SALARY_BUMP.certBonus : 0, reason: "Посада ГАП уже зайнята — оформлюється надбавка за сертифікат без зміни посади.", templateId: "hrt-cert-bonus", badge: "Доступна надбавка за сертифікат", } }; } if (track === "engineer") { const curRank = engineerCurrentRank(person); const certRank = cert.rank; // 0..3 const targetRank = Math.max(curRank, certRank); if (certRank > curRank) { const step = ENGINEER_LADDER[targetRank]; const delta = (targetRank - curRank) * SALARY_BUMP.engineerStep; return { ...base, status: "promotion", promotion: { kind: "category", targetPosition: targetRank === 3 ? "Провідний інженер-проєктувальник" : `Інженер-проєктувальник ${step.label}`, categoryLabel: step.label, salaryDelta: moneyApplies(person) ? delta : 0, reason: `Сертифікат класу ${cert.rank === 1 ? "СС1" : cert.rank === 2 ? "СС2" : "СС3"} відкриває щабель «${step.label}».`, templateId: "hrt-engineer-category", badge: `Доступне підвищення → ${step.label}`, } }; } // Немає підвищення зараз — підказка наступного щабля if (curRank < 3) { const next = ENGINEER_LADDER[curRank + 1]; return { ...base, status: cert.has ? "compliant" : "open", nextStep: { text: `Отримати сертифікат ${next.needs}`, requires: `${next.needs} → ${next.label} (+3 000 ₴)` } }; } return { ...base, status: "top", topNote: "Провідний інженер-проєктувальник — найвищий щабель інженерної категорії." }; } if (track === "specialist") { if (cert.has || (person.qualCerts && person.qualCerts.length)) { return { ...base, status: "promotion", promotion: { kind: "cert", targetPosition: person.positionOfficial || person.role, salaryDelta: moneyApplies(person) ? SALARY_BUMP.certBonus : 0, reason: "Сертифікат без класу наслідків — надбавка нараховується одразу, без зміни посади.", templateId: "hrt-cert-bonus", badge: "Доступна надбавка за сертифікат", } }; } return { ...base, status: "open", nextStep: { text: "Отримати кваліфікаційний сертифікат", requires: "сертифікат → надбавка +3 000 ₴" } }; } return base; } window.careerState = careerState; })();