(function (global) { "use strict"; /** Показатели для зоотех-расчёта рациона (weighted average). */ const NUTRIENT_ROWS = [ { label: "Осн. корм", unit: "г/кг СВ", keys: ["Осн.Корм"], hint: "СВ этого сырья, учитываемое как объёмный (основной) корм в норме dm_main. 0 — концентрат/добавка.", }, { label: "Сыр. протеин", unit: "г/кг", keys: ["Сыр. Протеин"] }, { label: "уСП", unit: "г/кг", keys: ["уСП"] }, { label: "RNB", unit: "г/кг", keys: ["RNB", "БРА", " БРА "] }, { label: "ЧЭЛ — КРС", unit: "МДж/кг", keys: ["ЧЭЛ- КРС", " ЧЭЛ- КРС"] }, { label: "ОЭ — КРС", unit: "МДж/кг", keys: ["ОЭ-КРС", " ОЭ-КРС"] }, { label: "Сырая клетчатка", unit: "г/кг", keys: ["Сырая клетч", "Сырая клетчатка"] }, { label: "Структур. клетч.", unit: "г/кг", keys: ["Структур клетч", "Структур. клетч", "Структур. клетчатка"] }, { label: "Сырой жир", unit: "г/кг", keys: ["Сырой жир"] }, ]; /** Лаб. ввод для native derive (см. LAB_FORMULAS.md). СВ — только component.dry_matter (%). */ const LAB_INPUT_ROWS = [ { label: "Сырая зола", unit: "г/кг", keys: ["Сырая зола"] }, { label: "ВРХ орг. вещ.", unit: "%", keys: ["ВРХ Орг Вещ"], hint: "Обязательно для грубых и сочных кормов. Без значения подставится дефолт WESP (65% / 72%).", }, { label: "уСП в ОР", unit: "г/кг", keys: ["уСП в ОР", "уСП в ОВ"], hint: "Лабораторное уСП: при значении > 1 заменяет расчёт по формуле Lebzien.", }, { label: "КРС протеин", unit: "%", keys: ["КРС Протеин"] }, { label: "КРС сырой жир", unit: "%", keys: ["КРС Сырой жир"] }, { label: "КРС сырая клетч.", unit: "%", keys: ["КРС Сырая клетч"] }, { label: "КРС БЭВ", unit: "%", keys: ["КРС БЭВ"] }, { label: "НДК", unit: "г/кг", keys: ["НДК", "НДК Общ"] }, { label: "NFC", unit: "г/кг", keys: ["NFC", "БЕР"] }, ]; /** Минералы, аминокислоты и прочий лаб. ввод из AgroStar / импорта. */ const EXTRA_LAB_ROWS = [ { label: "КДК (ADF)", unit: "г/кг", keys: ["КДК", "КДК общ"] }, { label: "Ca", unit: "г/кг", keys: ["Ca"] }, { label: "P", unit: "г/кг", keys: ["P"] }, { label: "Mg", unit: "г/кг", keys: ["Mg"] }, { label: "K", unit: "г/кг", keys: ["K"] }, { label: "S", unit: "г/кг", keys: ["S"] }, { label: "Cl", unit: "г/кг", keys: ["CL", "Cl"] }, { label: "Сахар", unit: "г/кг", keys: ["Сахар"] }, { label: "Крахмал", unit: "г/кг", keys: ["Крахмал"] }, { label: "Нераств. протеин", unit: "%", keys: ["% нераствор протеин"] }, { label: "Лизин", unit: "г/кг", keys: ["Лизин"] }, { label: "Метионин", unit: "г/кг", keys: ["Метионин"] }, { label: "Лейцин", unit: "г/кг", keys: ["Лейцин"] }, { label: "Изолейцин", unit: "г/кг", keys: ["Изолейцин"] }, { label: "Валин", unit: "г/кг", keys: ["Валин"] }, ]; const ALL_FORM_ROWS = [...NUTRIENT_ROWS, ...LAB_INPUT_ROWS, ...EXTRA_LAB_ROWS]; const CARD_INFO_COLUMNS = [ { label: "Сухое вещество", value(component) { const dm = component.dryMatter ?? component.dry_matter; return dm == null || dm === "" ? "—" : `${dm}%`; }, }, { label: "№ справочника", value(component) { const n = component.externalNo ?? component.external_no; return n == null || n === "" ? "—" : String(n); }, }, { label: "Цена", value(component) { const price = component.price; return price == null || price === "" ? "—" : `${price} руб/кг`; }, }, { label: "Осн. корм", value(_component, nutrients) { const val = readNutrientValue(nutrients, ["Осн.Корм"]); return val == null ? "—" : `${val} г/кг`; }, }, { label: "Сыр. протеин", value(_component, nutrients) { const val = readNutrientValue(nutrients, ["Сыр. Протеин"]); return val == null ? "—" : `${val} г/кг`; }, }, { label: "ОЭ — КРС", value(_component, nutrients) { const val = readNutrientValue(nutrients, ["ОЭ-КРС", " ОЭ-КРС"]); return val == null ? "—" : `${val} МДж/кг`; }, }, { label: "СВ (расч.)", value(component) { const dm = component.dryMatter ?? component.dry_matter; if (dm == null || dm === "") return "—"; const n = Number(dm); if (Number.isNaN(n)) return "—"; return `${(n * 10).toFixed(1)} г/кг`; }, }, { label: "Показателей", value(_component, nutrients) { return String(Object.keys(nutrients || {}).length); }, }, ]; function normalizeKey(s) { return String(s || "") .replace(/\s+/g, " ") .trim() .toLowerCase(); } function parseNutrients(raw) { if (!raw) return {}; if (typeof raw === "object") return { ...raw }; try { return JSON.parse(raw) || {}; } catch (_) { return {}; } } function readNutrientValue(nutrients, keys) { const map = nutrients || {}; for (const search of keys) { const target = normalizeKey(search); for (const [k, v] of Object.entries(map)) { if (normalizeKey(k) !== target) continue; const n = Number(v); if (!Number.isNaN(n)) return n; } } return null; } function canonicalKey(keys) { return keys[0]; } function buildNutrientsPayload(root) { const payload = {}; if (!root) return payload; root.querySelectorAll("[data-nutrient-key]").forEach((input) => { const key = input.getAttribute("data-nutrient-key"); const val = input.value.trim(); if (key && val !== "") payload[key] = Number(val); }); return payload; } function rowFieldsHtml(rows, nutrients) { return rows .map((row) => { const key = canonicalKey(row.keys); const val = readNutrientValue(nutrients, row.keys); const hint = row.hint ? ` title="${row.hint}"` : ""; return ( `
` + `` + `` + `
` ); }) .join(""); } const _OMD_FEED_TYPES = new Set(["грубые корма", "сочные корма", "объемные корма", "объёмные корма"]); function feedGroupNeedsOmd(component) { if (!component || !component.type) return false; return _OMD_FEED_TYPES.has(normalizeKey(component.type)); } function omdWarningHtml(component, nutrients) { if (!feedGroupNeedsOmd(component)) return ""; if (readNutrientValue(nutrients, ["ВРХ Орг Вещ", "КРС Орг Вещ"]) != null) return ""; return ( `
` + `Для грубых и сочных кормов укажите ВРХ орг. вещ. — ` + `без анализа расчёт энергии и уСП будет ненадёжным.` + `
` ); } function nutrientsFormHtml(nutrients, component) { const calcRows = rowFieldsHtml(NUTRIENT_ROWS, nutrients); const labRows = rowFieldsHtml(LAB_INPUT_ROWS, nutrients); const extraRows = rowFieldsHtml(EXTRA_LAB_ROWS, nutrients); const warn = omdWarningHtml(component, nutrients); return ( warn + `
` + `
` + `` + `Показатели для расчёта рациона` + `
` + `
${calcRows}
` + `
` + `
` + `
` + `` + `Лабораторный ввод (derive)` + `
` + `
${labRows}
` + `
` + `
` + `
` + `` + `Минералы и аминокислоты` + `
` + `
${extraRows}
` + `
` ); } function mountCreateForm() { const mount = document.getElementById("labNutrientsCreateMount"); if (!mount) return; mount.innerHTML = nutrientsFormHtml({}); mount.dataset.mounted = "1"; } function mountEditForm() { const mount = document.getElementById("labNutrientsEditMount"); if (!mount) return; mount.innerHTML = nutrientsFormHtml({}); mount.dataset.mounted = "1"; } function resetCreateForm() { const mount = document.getElementById("labNutrientsCreateMount"); if (!mount) return; mount.innerHTML = nutrientsFormHtml({}); mount.dataset.mounted = "1"; } function fillCreateFromComponent(component) { const mount = document.getElementById("labNutrientsCreateMount"); if (!mount || !component) return; const nutrients = parseNutrients(component.nutrients); mount.innerHTML = nutrientsFormHtml(nutrients, component); mount.dataset.mounted = "1"; } function fillEditNutrients(component) { const mount = document.getElementById("labNutrientsEditMount"); if (!mount) return; mount.innerHTML = nutrientsFormHtml(parseNutrients(component.nutrients), component); mount.dataset.mounted = "1"; } function formatCardItems(component) { const nutrients = parseNutrients(component.nutrients); return CARD_INFO_COLUMNS.map((col) => { const value = col.value(component, nutrients); return { label: col.label, value, empty: value === "—", }; }); } function renderCardInfoHtml(component) { if (!global.WESP_CAN_LAB) return ""; return formatCardItems(component) .map( (item) => `
` + `${item.label}` + `${item.value}` + `
` ) .join(""); } function collectFromCreateForm() { return buildNutrientsPayload(document.getElementById("labNutrientsCreateMount")); } function collectFromEditForm() { return buildNutrientsPayload(document.getElementById("labNutrientsEditMount")); } global.WespLabComponentsNutrients = { NUTRIENT_ROWS, LAB_INPUT_ROWS, EXTRA_LAB_ROWS, normalizeKey, readNutrientValue, parseNutrients, nutrientsFormHtml, mountCreateForm, mountEditForm, resetCreateForm, fillCreateFromComponent, fillEditNutrients, renderCardInfoHtml, collectFromCreateForm, collectFromEditForm, init() { if (!global.WESP_CAN_LAB) return; mountCreateForm(); mountEditForm(); }, }; })(window);