// Единый источник версии веб-приложения. Инкрементируется при каждой // существенной правке (semver). Отображается в ProfileMenu (десктоп) и на // вкладке «Аккаунт» (мобильное), синхронизируется с APK versionName в // mobile/android/app/build.gradle. // // Когда менять: // PATCH — багфикс, мелкий UI-фикс. // MINOR — новая фича без breaking changes. // MAJOR — несовместимое API/schema изменение. // // 0.6.3 (2026-06-09) — аудит-фиксы tech-debt (deploy/mobile/config). // 0.7.0 (2026-06-15) — спринт: списание склада, маршруты цеха, папки сборки, // каскад удаления заявки, auth-стабильность + логирование, вход 1-тап, // резерв всей спеки, разбивка резерва, роутинг-фиксы. ИНКРЕМЕНТ КАЖДЫЙ ДЕПЛОЙ. // 0.7.1 (2026-06-16) — синхронизация: FG-маршрут из плана+редактора (рассеиватель // ТПА→ВАК→Склад№2 больше не схлопывается в простой), ВАК=металлизация, checkBOM // читает реальный остаток Склада№2 (Барашек G), pfSku на партиях запуска для // списания со склада, гард запуска без спеки, диагностика списания (alLog). // 0.7.2 (2026-06-16) — резерв W2 при создании заявки: резерв считается по BOM // ПРОДУКТА заявки (не хардкод Гранты), reserveFullSpec (W1+W2) вместо W1-only, // reserveForAssembly резолвит ПФ по имени (apadog/клапан без invId теперь // резервируются). Цех не запускает производство того, что есть на Складе №2. // 0.7.3 (2026-06-16) — мультиагентный раунд 8 фиксов Цеха/резерва + adversarial // review: дубль партии после переноса (split только для локальных + cuid- // реконсиляция), приход ПФ на Склад№2 (writeoff резолв по bomN/имени, не падает // на pfSku=null), сборка ФГ достигает complete при ненулевом W2-стоке, Гранта G // спека не затирается пустым снапшотом, резерв на готовый ПФ а не сырьё, // завершение заявки приходует ФГ на Склад№3, правка остатка W2 → StockMovement, // Outsource-квадрат + редактируемое кол-во отправки в карточке. // 0.7.4 (2026-06-16) — ground-truth раунд (по live-снапшоту): каскад остатков // W2 (снапшот пере-захватывал _seedStock из уже посчитанного stock → +delta // каждый снапшот) — фикс: _seedStock переносится при rebuild INVENTORY_FULL + // фиксируется в редакторе до optimistic-записи; партии Цеха с requestId=null // (самостоятельные запуски) больше НЕ прячутся орфан-фильтром (возврат/исчезание // позиций); превью резерва (RequestDetailsModal + calcRequest) — stockAware и по // BOM продукта, а не хардкод Гранты. // 0.7.5 (2026-06-16) — (1) ФИКС регресса v0.7.4: орфан-фильтр Цеха снова прячет // партии с requestId=null (createRequest-сбои/сироты, НЕ легит-запуски — легит // запуск создаёт заявку и линкует партии). Возврат «удалил заявку → 3 позиции в // Цеху». (2) Совместный резерв: calcBomReserve показывает ВСЕ 3 группы вместе — // готовые ПФ (Склад№2, в наличии) + сырьё на производство дефицита (Склад№1) + // фурнитура (Склад№1). _w2-строки только для отображения (резерв W2 — assembly). // Проверено на реальном BOM С-161: 21 строка (4 ПФ + 2 сырьё + 15 фурнитура). // 0.7.6 (2026-06-16) — (1) РЕЗЕРВ единым каналом: ПФ Склада№2 + сырьё W1 + фурнитура // W1 резервируются ВСЕ через per-invId /reserve (персистит). reserveForAssembly // убран из reserveFullSpec — он слал W2 в /assembly-reserve, не сохранявший // кастом-спеки (invId) → «ПФ не резервировались» (assemblyReserves=0 на сервере). // (2) ПРАВА не слетают: sticky-роль (держим роль AUTH.user-владельца при // downgrade whoami), refresh по COOKIE без in-memory accessToken (401/heartbeat/ // mount). Durable: backend ACCESS_TOKEN_TTL=15m → нужен bump (env, на LAN-HTTP // Secure-cookie рефреша не работает). // 0.7.7 (2026-06-17) — 3 корневых фикса (глубокий аудит): (1) учёт склада — // pfSku партий = row.invId (не синтетика BOM-N) → приход ПФ виден на складе; // (2) права на заводском LAN-HTTP — cookie secure/sameSite из env + ожила // sticky-роль (roleKey→level); (3) партии-сироты — только с requestId, // реконсиляция по _linkKey, дедуп-гард. + материалы AL и пр. в редакторах. // 0.8.0 (2026-06-17) — СПРИНТ «полный цикл наряда» (Н1–Н5, мультисессионно): // частичная отгрузка готовых фар на Склад №3 (shipFinishedToW3 — приход ФГ + // расход ПФ/покупных пропорц. отгрузке + drawdown резерва + стикеры паллет // A4/QR + сканер); наряд показывает Заявлено·Отгружено·Осталось; досрочное // закрытие с ОБЯЗАТЕЛЬНОЙ причиной (брак/веская) → AuditLog; корректируемый // ПФ-batch (overproduction покомпонентно) на 3-м окне запуска; drag «Буфер/ // Склад№2 → Сборка» в Цеху. Backend: Shipment-модель + миграция // 20260617120000_shipment + POST /shipment + POST /request/:id/close + shipments[] // в снапшоте. Reconcile: учёт ФГ/ПФ ТОЛЬКО на отгрузке (confirmAssemblyPart — // чистый прогресс); фикс 3 reload-течей резерва (release/releaseForRequest/ // releaseAssembly всегда бьют в backend, идемпотентно). Верифицировано: backend // на реальном Postgres + fullcycle 40/40 + reserve-edge 10/10 + vitest 182/182. // 0.8.1 (2026-06-17) — фиксы по тестам отгрузки частями (Александр): (1) DROP // готового ПФ НАПРЯМУЮ в 3 конечные зоны — «Сборка»/«Буфер производства»/ // «Склад №2», без обязательной цепочки через буфер; (2) папка сборки держится // по наряду (shipped>0) — БОЛЬШЕ НЕ пропадает после частичной отгрузки/отката // партий (раньше зависела от наличия партии в asm). Подтверждено: резерв // кратно отгрузке, «минус отгружено» в наряде, ФГ на Складе №3 — работают. // ⏭ Баг «перенос партии в Цеху не персистится → возврат на ТПА» (cechTransfers // не доходят до backend, optimistic-расхождение) — отдельный аккуратный фикс. // 0.8.2 (2026-06-17) — фиксы по тестам Цеха/отгрузки (Александр): (1) БАГ «возврат // на ТПА» — createTransfer теперь вызывается ВСЕГДА (резолв backend-cuid партии // даже для локальной, через _batchByRequestId) → переносы партий в Цеху // персистятся на backend (раньше createTransfer=0 в логах, только commit→404); // (2) drop готового ПФ в 3 конечные зоны напрямую (Сборка/Буфер/Склад№2); (3) // папка сборки ИСЧЕЗАЕТ при закрытии наряда (status done/cancelled); (4) синий // бейдж «отгружено N» на готовой фаре Склада №3 (до закрытия наряда). Резерв на // отгрузке = v0.8.0 (W2+покупные; сырьё W1 — на производстве через writeoff): // «резерв не меняется на отгрузке» лечится фиксом переносов п.1 (Цех→W2 теперь // персистится → writeoff W1 срабатывает). Harness 41/41 + reserve-edge 10/10. // 0.8.3 (2026-06-17) — усиление фикса Цеха (launch_flow уведомляет Цех о // реконсиляции local→cuid, чтобы перенос точно персистился) + changelog // версии при наведении в меню аккаунта. // 0.8.4 (2026-06-17) — changelog в меню читаемый + раскрывается ВНИЗ; синий бейдж // отгруженного теперь и в плиточном виде Склада №3 (раньше только в списке); // диагностика переноса партий в Цеху (alLog('cech')) для отладки «возврата на ТПА». // 0.8.5 (2026-06-17) — ОКОНЧАТЕЛЬНЫЙ фикс «возврата на ТПА» (доказан на live-данных // ORDER-MQIQHNAG: createTransfer=0, 4 партии застряли в origin). 3 корня: // (A) performTransfer резолвил backend-cuid по УСТАРЕВШЕМУ closure-снимку items // (тайл уже перерисован backend-партией, а closure держал локальную) → cuid не // находился → createTransfer не звался; теперь резолв по АВТОРИТЕТНОМУ item из // prev.items (как newShipmentId). (B) merge оставлял ЛОКАЛЬНЫЙ дубль партии // рядом с backend-тайлом (реконсиляция local→cuid терялась: cech mirror-эффект // осиротил ссылку launch_flow, FG-PF-путь не диспатчил) → пользователь тащил // дубль; теперь дубль с backend-близнецом (по requestId-маппингу externalId→cuid // + pfSku/имя) ДРОПАЕТСЯ, split-партии сохраняются. (C) резолвер матчит по pfSku // (стабильнее имени). Доказано харнессом 11/11 на РЕАЛЬНЫХ формах с live-БД. // 0.8.6 (2026-06-18) — НАСТОЯЩИЙ корень «возврата на ТПА» (доказан probe'ом на // живой вкладке: перенос делался, но createTransfer НИ РАЗУ не вызывался, без // ошибок). performTransfer ставил newShipmentId/realQty ВНУТРИ setState- // updater'а и читал ПОСЛЕ setState; React НЕ гонит updater синхронно при // непустой очереди обновлений (Цех под SSE — постоянно) → значения null → // весь блок отправки на backend ПРОПУСКАЛСЯ. Теперь расчёт СИНХРОННЫЙ (stateRef // + монотонный shipSeqRef), вызов createTransfer БЕЗУСЛОВНЫЙ → перенос // персистит (backend двигает stages на create), тайл не откатывается. commit/ // cancel читают _serverId из stateRef. + очередь: 4xx (404 commit/несущ. id) — // ДРОПается, не зацикливается (был флуд 404). + диагностика cech.transfer.try/resp. // 0.8.7 (2026-06-18) — синий бейдж «↑N» (отгружено по открытому наряду) на // готовой фаре Склада №3 ТЕПЕРЬ КАСКАДИТСЯ на папки/группы как amber-резерв: // shippedFgForSku/shippedItemsInGroup/shippedSumInGroup (data_materials); // shipBadge на плитках подгрупп (WarehouseStripe), подподгрупп и inline-позиций // (warehouse_pages). Гаснет при закрытии наряда. Источник — getRequestProgress. // 0.8.8 (2026-06-18) — ФИКС «Гранта не резервируется» (детерминированный, по // live-логам сервера: 78× FK-400 `InventoryReserve_requestId_fkey`). КОРЕНЬ: // резерв писался с локальным/внешним ключом заявки (ORDER-XXX = externalId), а // FK ссылается на Request.id (cuid) → ORDER-XXX ≠ cuid → FK-400, резерв не // писался. Плюс очередь повторяла 4xx ВЕЧНО (флуд 400 от мёртвой ORDER-MQIR77ZL). // ФИКС в reserve/release/assembly-методах: (A) резолв requestId→cuid по REQUESTS // (id|externalId) перед POST; (B) 4xx → drop из очереди, не зацикливать // (зеркало фикса очереди Цеха 0.8.6). Рабочий путь С-161 не затронут. // 0.8.9 (2026-06-18) — КНОПКА ОБНОВЛЕНИЯ без ручных манипуляций (Александр: // убрать Ctrl+Shift+R / Ctrl+F5 / F12). Приложение само опрашивает version.jsx // (no-store, каждые 2 мин + на фокус); при новой версии на сервере — зелёный // бейдж на иконке аккаунта + кнопка «Обновить» в выпадашке версии. Клик → // window.__applyUpdate(): чистит ВСЕ кэши + обновляет SW + перезагрузка → // network-first тянет свежий фронт. Данные на сервере не теряются. // 0.9.0 (2026-06-18) — РЕЗЕРВ «мигает и исчезает» — ФИКС. Корень: reserve-POST // падал транзитно (403 при резолве роли viewer→admin, ИЛИ FK-400 гонка с // коммитом createRequest), а мой 4xx-drop из 0.8.8 ДРОПал его навсегда → // оптимистичный резерв затирался снапшотом (0 на сервере). Теперь дроп ТОЛЬКО // при безнадёжном отказе (404 / FK на удалённую заявку); 403 и FK-на-живую- // заявку — РЕТРАЯТСЯ (_permanentReserveFail). Резерв доезжает и держится. // 0.9.1 (2026-06-18) — ФИКС «алюминий литьевой не резервируется». Корень: // RAW_MATERIAL_SPECS содержал только 6 пластиков, AL не было → позиции // сырья INV-RAW-AL не существовало, и строка резерва AL (для C-161 и любой // фары с AL-литьём) уходила в никуда. Добавлен AL в RAW_MATERIAL_SPECS // (INV-RAW-AL, кг, АК12) + MATERIAL_WASTE['AL']. Резерв алюминия теперь // записывается, как у пластиков. Снапшот сохраняет позицию (preserve unknown). // 0.9.2 (2026-06-22) — ФИКС резерва лампы (invId). Корень (sweep на дампе боевой // БД): код передавал ЧИСЛОВОЙ id номенклатуры лампы (1303) вместо складского // invId (INV-PUR-ELE-016). Для спек СО строкой-лампой (Гранта L/R, GRN-FL) → // POST /api/state/reserve падал 400 на числовом invId → резерв НЕ создавался // ВЕСЬ; для спек без неё (C-161 и др.) → reserveLampForRequest(_findInv→null) // тихо не резервировал лампу. Добавлен _resolveLampInvId (по sku→имени→фолбэк) // в launch_flow (effectiveBom + оба reserveLampForRequest + _lampInvId). // Верифицировано: 21/21 FG-спек OK (резерв вкл. AL+лампа, отгрузка кратно). // 0.9.3 (2026-06-22) — РЕКУРСИВНАЯ РАЗУЗЛОВКА ПОДСБОРОК (корпус G/GFL). Корпус в // спеке фары — ЕДИНАЯ позиция «под сборку»: при резерве заявки есть готовый // корпус на Складе №2 → берётся ЦЕЛИКОМ (его внутренности барашек/шестерни/ // винты повторно НЕ резервируются — они внутри); нет → разузловывается // РЕКУРСИВНО (тело-PP в сырьё W1 + барашек/шестерни снова «есть на W2 целиком / // нет → сырьё» + винты только на производимые корпуса); частично — N целиком + // остаток из сырья. calcBomReserve переписан с плоского прохода на обход дерева // с отрезанием поддерева на ПФ-узле + дедуп дублей flatten (раньше корпус и его // дети считались плоско-независимо → двойной резерв). Цех симметрично: при // корпусе-в-наличии барашек/шестерни в Цех НЕ запускаются (был рассинхрон). // Резолв ПФ сторона-aware (L/R) — подстраховка (в живой спеке invId уже точен). // Верифицировано на ЖИВОЙ спеке GRN-R (3 сценария) + 8 рекурсивных vitest + // 190/190 backend + babel-parse. // 0.9.4 (2026-06-22) — консолидированный релиз (несколько сессий): (1) Цех — // подтверждение детали в сборке синхронизируется со статусом в зоне (матч // детали↔партии по pfSku↔invId, переживает SSE-снапшот; раньше _bomN терялся // после снапшота → статус не синхронизировался); (2) Цех зона «Склад №3» — // история частичных отгрузок наряда (N ед · дата); (3) экспорт/импорт базы // знаний через Excel в Настройках; (4) раздел «Обновления» в Настройках (вся // история версий). + фикс данных на сервере: шайба д10 C-161 = изготавливаемая // (internal/PVC/«ТПА→Склад №2»). // 0.9.5 (2026-06-22) — НОРМАЛИЗАЦИЯ СПЕК: спека фары ссылается на под-спеки ПФ // (корпус/бленда/отражатель…) + покупное + лампа, СЫРЬЯ в FG-спеке НЕТ. Корпус // — подсборка из ПФ (тело-PP + ссылки на барашек/шестерни/бабочку + метизы). // Резерв (рекурсия 0.9.3 по sku-ссылкам, правок не требует): ПФ есть на W2 → // целиком, нет → разузловка вглубь. Цех (launch_flow `_walkCech`): недостающие // ПФ на ВСЕХ уровнях — отдельными позициями (корпус → барашек/шестерни, если их // тоже нет). Списание ОДНОУРОВНЕВОЕ (`_consumeOneLevel`+writeoffPfBatch): партия // ПФ потребляет ПРЯМЫЕ входы (тело→сырьё W1, внутренности→ПФ со Склада №2, // покупные→W1), НЕ рекурсивно до сырья → двойного расхода нет. shipFinishedToW3 // уже одноуровневое. Данные: 62 под-спеки + 4 FG записаны в Postgres через // PUT /api/taxonomy/specification. 26 vitest + live-снапшот. // 0.9.6 (2026-06-22) — УЧЁТ СКЛАДА в Excel + раскол плитки дашборда. (1) Новый // лист «Учёт склада» в экспорте: текущий остаток (из движений склада) vs // неснижаемый остаток, статус «требуется закуп» (покупное W1) / «требуется // изготовление» (ПФ W2/W3) при остатке ≤ минимум+10%. Остаток и минимум // РЕДАКТИРУЕМЫ прямо в листе — импорт применяет: количество → движение склада // (adjust на разницу), минимум → карточка позиции (minStock). (2) Импорт из // Excel применяет ВСЕ редактируемые поля (не только sku/name); перенос группы // по имени, недостающие группы создаются. (3) Главная: плитка «Требуется закуп» // разделена на «Требуется закуп» (покупное) и «Требуется изготовление» (ПФ). // master_data_io.jsx + dashboard.jsx. Verified Playwright; задеплоено scp. // 0.9.7 (2026-06-22) — СТОРОНА-МОДЕЛЬ L/R + ЛАМПЫ ПО СЛОТАМ + имена ПФ + rename. // (1) Сторона-модель: общие (generic) полуфабрикаты (барашек/бленда/бабочка/ // шестерни/очки/крышки/опоры/тешка/заглушки/ручка/упор/пыльник) сведены в ОДНУ // каноничную позицию (-L); сторона-дубль (-R) удалён (23 спеки + 23 позиции; // сток дублей ~0, барашек 10000 — дубль, канон держит свои 10000). Сторона- // специфичны ТОЛЬКО корпус/рассеиватель/отражатель (KRP/RAS/REF/RDH/RPV). Спеки // фар и корпусов ссылаются на каноничные generic-ПФ → GRN-L и GRN-R по общим // деталям ИДЕНТИЧНЫ, различаются корпус/рассеив/отраж. (2) Выбор лампы ПО СЛОТАМ // (строки-лампы спеки; Гранта 3: H4/S25/T20), дефолт = лампа спеки; детектор // бульба по ПЕРВОМУ слову (держатели и «Пыльник/Прижим …лампы» — НЕ лампы; фикс // JS-бага \b после кириллицы). Шаг «Выберите лампу» НЕ пропускается (спека без // ламп → ручной слот, дефолт «Без лампы»). Списание ламп по слотам, спец-лампы // гейтятся отгрузкой (нет дубля с shipFinishedToW3). Только FG, к ПФ не относится. // (3) Имя ПФ-спеки резолвится со складской позиции по sku (был артикул // WIP-BAR-GRN-L вместо «Барашек G»). [Пункт (4) «Лампочка»→«Лампа» УБРАН из changelog // 2026-06-22: по факту НЕ применён — 3 позиции (BOM-016/017/018) и 6 узлов спек // остались «Лампочка». Соседи задекларировали, но не сделали.] Живая база: // PUT спек (репойнт+rename ПФ) + DELETE дублей -R. Верифицировано: // calcBomReserve на живом стоке (GRN-L==GRN-R generic), 10/10 слотов, 8/8 vitest. // 0.9.8 (2026-06-22) — ЗАКОН «ОСТАТОК ≥ 0» (требование Александра). Остаток позиции // физически не может быть < 0 — клампится Math.max(0,…) в ЦЕНТРАЛЬНОЙ точке // (data_server_sync: эффективный остаток = посев + Σ движений), все экраны читают // it.stock → видят ≥0, и Excel «Учёт склада» (показ + импорт количества floor'ятся). // Ниже нуля уйти нельзя: из нуля ничего не изготовить. Backend-reject движения <0 // (авторитетный уровень) — отдельным заходом. Файлы: data_server_sync.jsx, // screens/master_data_io.jsx. Verified Playwright (netDelta -99999 → 0, импорт -50 → 0). // 0.9.9 (2026-06-22) — АЛЮМИНИЙ (литьевой/магнетрон) + ЗАКОН ≥0 на backend. // (1) calcBomReserve/writeoff: литьевой AL → реальная позиция INV-PUR-MISC-603 // (не призрак INV-RAW-AL; AL убран из RAW_MATERIAL_SPECS — больше нет 13-й // плитки-фантома в «Пластиках»); магнетрон-расходник INV-PUR-MISC-605 на узлах // с «Металлизация»/«ВАК» в маршруте, 7 г/деталь. Общий резолвер _rawInvIdFor // в data_materials + лукапах dashboard/mobile/mobile_dashboard (резерв и показ // не разъезжаются). Плоские спеки (C-161/Веста/Калина/Солярис/Приора/РС-100) // считают магнетрон; нормализованные ПФ Гранты — после простановки маршрута. // (2) backend mutations.ts (REBUILD): /api/state/stock-movement клампит дельту // так, чтобы нетто-остаток не ушёл ниже нуля (+ AuditLog) — авторитетный // уровень закона ≥0 (фронт-кламп 0.9.8 — лишь показ; иначе negative-debt на // сервере поглотил бы будущий приход). Verified: vitest 190/190, equivalence // frontend↔backend цел, live-снимок (C-161: 603=13.8кг + 605=200 деталей). // (3) ЛАМПЫ — папки 12В/24В/LED/накаливания на СЕРВЕРЕ. Серверные позиции // ламп не хранят voltClass → «Склад»→«Лампы» был плоским, и выбор лампы при // запуске фильтровал по voltClass → группы пустые. Фикс: при гидрации снимка // (data_server_sync) для w1-lamps деривируем voltClass/bulbType из имени // (_deriveLampMeta, экспортирован из data_nomenclature). 15 ламп → 7/2/2/4, // 0 «Прочее». Локальная раскладка теперь и на сервере. // 0.9.10 (2026-06-22) — ФИКС ВОСКРЕШЕНИЯ ДУБЛЕЙ ЛАМП (backend REBUILD). Позиции // BOM-016/017/018 «Лампочка H4/S25/T20» задваивали реальные «Лампа» // (INV-PUR-ELE-016/017/018) на «Складе». Корень: `seed-taxonomy.ts` запускается // на КАЖДЫЙ старт контейнера (Dockerfile CMD) и пересоздавал их // (BOM_GROUP_MAP 16/17/18→w1-lamps) → `deploy.ps1 --force-recreate` (0.9.9) // воскресил ранее удалённые дубли. Фикс: 16/17/18 убраны из BOM_GROUP_MAP // (дубли не создаём), rename «Лампочка»→«Лампа», grantaTree лампы → реальные // INV-PUR-ELE-* (LAMP_INVID) + самолечащий deleteMany (item+движения+резервы) // на каждом старте сида. tsc чист. // 0.9.11 (2026-06-22) — ЛАМПЫ: официальные имена + рамка дефолта; РЕЗИНА → покупное. // (1) При запуске фары подпись слота, имя лампы по умолчанию, имя наряда и Цех // теперь берут ОФИЦИАЛЬНОЕ каталожное имя по invId (_catalogLampForRow в // launch_flow.jsx), а не легаси-имя узла спеки → больше нет рассинхрона «слот // S25 12V Amber» vs «карточка PY21W». Рамка выбранной лампы — нормализованное // сравнение invId (INV-PUR-ELE-016 == PUR-ELE-016) → дефолт подсвечивается. // Живые спеки GRN-L/R-H4 n16/17/18 приведены к каталогу (PUT, 6 узлов). // (2) Резина (Пароотвод/Прокладка/Бондаж/Втулка/Кольцо) перенесена ПФ(W2)→ // покупное сырьё(W1) в новую папку «Уплотнители - покупное» (w1-upl); папка W2 // → «Уплотнители - ПФ». seed-taxonomy.ts: +w1-upl в GROUPS, BOM_GROUP_MAP // 25/26→w1-upl, имена ламп BOM_GRANTA_L → каталожные (re-seed-safe). backend REBUILD. // v0.9.12 (2026-06-23): корпус-подсборка → ОТДЕЛЬНЫЙ наряд при дефиците (буфер + // своя папка сборки в Цеху + приход готового корпуса на Склад №2, фара забирает // с W2; резерв на входы корпуса, без фантома); «Тело корпуса G/GFL» — отдельный // ПФ; задел ПФ на окне «Наличие» разделён на «Спецификация фары»/«Спецификация // корпуса»; папка наряда в зоне «Сборка» появляется СРАЗУ при запуске; при // дефиците корпуса — 2 папки (фара+корпус); честный индикатор комплектности; // кнопка удаления спеки ПФ; убран тумблер плитки/список в Спецификациях; дубли // «Корпус G/GFL (подсборка)» удалены. (Часть данных — тело-ПФ/дедуп/репойнт — на // живом сервере применяется отдельным data-push, см. deploy/QUEUE.md.) // v0.9.15 (2026-06-24): Цех — наряд-корпус в окне сборки шлёт «на Склад №2» (была // ошибочная «на Склад №3»). Склад №3 / главная — «ПФ как запчасть» (корпус + // рассеиватель) встроены плитками-в-рамке в полосу Склада №3 рядом с готовыми // фарами, дублируют подгруппы Склада №2 «под реализацию»; пустая подгруппа-дубль // «Рассеиватели» (SPG) убрана; на Складе №2 у корпуса/рассеивателя бейдж «ПФ-запч.». // Архив нарядов: завершённый наряд «Сохранить в архив» вместо удаления + кнопка // «Архив» на полосе буфера и на странице «Сборщик»; архив переживает перезагрузку. // «Сборщик» показывает реальные наряды в производстве (была пустая страница). // Главная «Готовые продукты» — реальный остаток Склада №3, разбит по типам // (ПТФ / Фара-блок-фара / Фонарь / ДХО). Кнопки шапки 📌/✎ опущены под шапку. // backend REBUILD: новая колонка Request.archived (миграция) + seed убирает SPG. // v0.9.16 (2026-06-25): БАТЧ нескольких сессий. // (1) Кнопка «Корректировка» на позиции склада (1/2/3) ожила → открывает полную // модалку: готовая фара / составной ПФ → редактор спецификации (дерево BOM, как в // Настройках, правится прямо со склада, автосохранение); простая деталь/сырьё → // развесовка (вес ед./масса остатка/материал/поставщик/резерв) + правка остатка // (проводкой движения склада). Развесовка в шапке редактора подтягивает веса из // спек полуфабрикатов рекурсивно (ПФ-ссылка → её спека → … до листа). // (2) Бейдж «вес примерный» (фиолетовый) на позициях/подгруппах/группах склада + // развесовка 2 ДХО Hyundai по аналогу. // (3) Печать QR-стикеров паллет: номер наряда больше не вылазит за лист; // постраничное превью (по 4, клик→зум); 2 режима — «По стандарту» (равное кол-во) // и «Выборочно» (своё кол-во на каждой паллете, печать по очереди). // (4) Главная: группы готовой продукции (ПТФ/Фара/Фонарь/ДХО) и «ПФ как запчасть» // стали выпадающими (свёрнуто по умолчанию); у ПФ добавлена полоса наличия. // (5) Надёжность: React/Babel теперь локальные (работа без интернета на заводе); // авто-переподключение синхронизации при разрыве/возврате вкладки; честные // баннеры «раздел в разработке» в Настройках (Сотрудники/Роли) + кнопка // «Установить приложение» (PWA) видна прямо в шапке. // (6) backend REBUILD (F072): защита от двойного учёта движения склада — // PARTIAL UNIQUE INDEX по КОМПОЗИТУ (sourceId, invId) (одна операция = много // движений по invId, но повтор той же пары — no-op P2002). Пред-чек прода: // 0 дублей пар (sourceId,invId) → миграция применилась чисто. (Дубли по // ОДНОМУ sourceId — 48 групп — легит: разные invId одной отгрузки.) // v0.9.17 (2026-06-25): ПЕРЕНОС ПОЗИЦИИ ПО ИЕРАРХИИ СКЛАДА. На каждой плитке // позиции — кнопка ⇄ (правый верхний угол, под ролью менеджер). Открывает // дерево всей иерархии (Склад → подгруппа, подподгруппы — контекстом), // выбор папки-назначения → «Подтвердить» → позиция переезжает ФИЗИЧЕСКИ // (API.updateItem → PATCH /api/taxonomy/item, персист + SSE всем терминалам) // + оптимистичное обновление. Ссылки на позицию идут по артикулу (sku/invId) // — при переносе не ломаются; производственный маршрут не трогаем (это план // цеха, не место хранения). Фронт-only, backend уже на 0.9.16. // v0.9.18 (2026-06-25): PWA-кнопка «Установить» — ВСЕГДА видна и в ЛЮБОМ браузере // + на ЭКРАНЕ ВХОДА (раньше была только в шапке после логина и пряталась на // Firefox → Александр в Comet её не видел). type=button (не сабмитит форму). // Клик: есть beforeinstallprompt → нативная установка в окно; нет → понятная // инструкция по браузеру; на HTTP подсказывает открыть https-адрес // (155.212.161.65.sslip.io), т.к. установка возможна только из secure-контекста. // v0.9.19 (2026-06-25): PWA-УСТАНОВКА ЗАРАБОТАЛА — НАСТОЯЩИЙ корень. Манифест // ссылался на /icons/icon-192.png и icon-512.png (+maskable), а на сервере в // icons/ лежали ТОЛЬКО .svg — PNG отсутствовали (деплой-тары их не включали, // были только в репо). 404 на иконки → Chrome НЕ считал приложение // устанавливаемым → beforeinstallprompt НЕ слался даже в Chrome по https → // кнопка показывала лишь подсказку. Фикс: выложены icon-192/512 + maskable + // apple-touch на FirstVDS (200, image/png, верные размеры). Теперь критерии // PWA выполнены → нативная установка в Chrome/Edge по https-адресу. // ⚠ Деплой-тар фронта ДОЛЖЕН включать icons/ + manifest.webmanifest. // v0.9.20 (2026-06-25): PWA-кнопка КОРРЕКТНА ВО ВСЕХ БРАУЗЕРАХ. Улучшен детект // платформы (Safari macOS/iOS + iPad-iOS13 по maxTouchPoints + Opera/Yandex) // и подсказки: Chrome/Edge/Opera/Яндекс → нативная установка (значок в // адресной строке); Firefox десктоп → «не поддерживает, откройте в Chromium»; // Safari Mac → «Файл → Добавить в Dock»; Safari iPhone/iPad → «Поделиться → На // экран Домой». Кнопка ПРИСУТСТВУЕТ в каждом браузере (UA-тест 7/7). // v0.9.21 (2026-06-25): ЗАПУСК ПФ НА ПРОИЗВОДСТВО — РЕЗЕРВ СЫРЬЯ, А НЕ ГОТОВОГО // ПФ. Баг (Александр): запуск партии полуфабриката (напр. Тешка) на // изготовление резервировал уже готовый такой же ПФ со Склада №2 вместо сырья, // из которого он делается — если ПФ был в наличии, сырьё не резервировалось // вовсе. Общий для ВСЕХ полуфабрикатов. Корень: reserveForRequest жёстко звал // calcBomReserve со stockAware:true (правильно для СБОРКИ фары — берём готовые // ПФ; неверно для ПРОИЗВОДСТВА самого ПФ). Фикс: stockAware теперь // override-able (дефолт true), PF-путь launch_flow передаёт stockAware:false → // разузловка в сырьё (лист-ПФ → его материал/масса; подсборка-корпус → // рекурсивно тело + дети + закупные). +3 теста (196/196 backend PASS). // v0.9.22 (2026-06-25): ДОРОЖНЫЕ КАРТЫ ПФ + ЛАК ПРИ ПРОИЗВОДСТВЕ + РАСХОД СЫРЬЯ. // (1) 44 полуфабриката Гранты/GFL/С-161 не имели цеховой дорожной карты — их // нельзя было провести по цеху/запустить в производство. Проставлены маршруты: // простые→«ТПА→Склад№2»; рассеиватели→«ТПА→Лакировка→Склад№2»; отражатели→ // «…→Металлизация→Склад№2» (пластик) / «Литьё AL→Мех→Голтовка→Лак→Металл→ // Склад№2» (алюминий); корпус-в-сборе→«Сборка→Склад№2»; бленда→«ТПА→Склад№2»; // очки напыленные→металлизация. Починены «Закупка» у рассеивателей Гранты и // оборванный маршрут рассеивателя Соляриса-R. (2) ЛАК (защитный/базовый) // списывается при ИЗГОТОВЛЕНИИ полуфабриката (этап лакировки), а не при сборке // фары — и ровно ОДИН раз (раньше лак-child спеки не списывался при // производстве; на сборке фары лак/магнетрон больше не списываются повторно). // (3) Расход сырья PFS и ТЭП (TPV) починен — для них не было позиции сырья на // складе, расход обнулялся; позиции созданы. (4) Убраны мёртвые синонимы «ВАК» // из словаря маршрутов (канон — «Металлизация»). Данные (маршруты/лак/сырьё) — // data-push на сервер; код — data_cech.jsx + data_materials.jsx. // v0.9.23 (2026-06-25): БЕЙДЖ «ВЕС ЛАКА ПРИМЕРНЫЙ». Лак (защитный/базовый), // добавленный в спеки полуфабрикатов, имеет placeholder-массу 10 г (0.01 кг) — // реальный расход лака на деталь нужно выверить. В редакторе спецификации // (Настройки → Спецификации) на строке-лаке теперь фиолетовый бейдж «вес // примерный — требует корректировки» (тот же, что на складе для развесовки по // аналогу). Помечается флагом weightApprox на строке спеки. (settings.jsx) // v0.9.24 (2026-06-25): ЭКСПОРТ/ИМПОРТ EXCEL — СПЕЦИФИКАЦИИ ГОТОВОЙ ПРОДУКЦИИ. // Лист «03_Спецификации» в выгрузке стал РЕДАКТИРУЕМЫМ и заливается обратно: // состав каждого изделия (№, наименование, кол-во, ед., тип, invId, вес, материал, // страна, маршрут, фото) — готовая продукция сверху, полуфабрикаты ниже, перед // каждым изделием строка-шапка. Фото — список имён + кликабельная ссылка на // изображение. Импорт (Настройки → Бэкапы → Импорт из Excel) пересобирает дерево // по ключу SKU+Путь, шлёт на сервер ТОЛЬКО изменённые изделия (PUT specification), // показывает их в предпросмотре. Защита состава: дубль «Путь» в изделии или // удаление узла-родителя с оставшимися детьми → изделие ОТКЛОНЯЕТСЯ с // предупреждением; числа с пробелом-разделителем не схлопываются молча в 1. // Скрытые _node_json/_photos_json + лист _base_specs хранят оригинал. (master_data_io.jsx) window.APP_VERSION = '0.9.24'; // Даты выпуска версий (для раздела «Обновления» в Настройках). Формат YYYY-MM-DD. window.APP_VERSION_DATES = { '0.9.24': '2026-06-25', '0.9.23': '2026-06-25', '0.9.22': '2026-06-25', '0.9.21': '2026-06-25', '0.9.20': '2026-06-25', '0.9.19': '2026-06-25', '0.9.18': '2026-06-25', '0.9.17': '2026-06-25', '0.9.16': '2026-06-25', '0.9.15': '2026-06-24', '0.9.14': '2026-06-24', '0.9.13': '2026-06-24', '0.9.12': '2026-06-23', '0.9.11': '2026-06-22', '0.9.10': '2026-06-22', '0.9.9': '2026-06-22', '0.9.8': '2026-06-22', '0.9.7': '2026-06-22', '0.9.6': '2026-06-22', '0.9.5': '2026-06-22', '0.9.4': '2026-06-22', '0.9.3': '2026-06-22', '0.9.2': '2026-06-22', '0.9.1': '2026-06-18', '0.9.0': '2026-06-18', '0.8.9': '2026-06-18', '0.8.8': '2026-06-18', '0.8.7': '2026-06-18', '0.8.6': '2026-06-18', '0.8.5': '2026-06-17', '0.8.4': '2026-06-17', '0.8.3': '2026-06-17', '0.8.2': '2026-06-17', '0.8.1': '2026-06-17', '0.8.0': '2026-06-17', '0.7.7': '2026-06-17', '0.7.6': '2026-06-16', '0.7.5': '2026-06-16', '0.7.4': '2026-06-16', '0.7.3': '2026-06-16', '0.7.2': '2026-06-16', '0.7.1': '2026-06-16', '0.7.0': '2026-06-15', '0.6.3': '2026-06-09', }; // Краткий changelog для меню аккаунта (наведение на номер версии) и раздела // «Обновления» в Настройках. Короткие, понятные строки — что изменилось для // пользователя. Версия → массив строк-пунктов. Мини-попап показывает только // текущую версию; Настройки → всю историю (сортировка по semver вниз). window.APP_CHANGELOG = { '0.9.24': [ 'Экспорт в Excel теперь включает спецификации готовой продукции: на листе «Спецификации» — состав каждого изделия (детали, количество, единицы, материал, маршрут, фото). Готовая продукция сверху, полуфабрикаты ниже, перед каждым изделием — строка с его названием.', 'Эту таблицу можно править прямо в Excel и загрузить обратно (Настройки → Бэкапы → Импорт из Excel): система примет изменения состава изделий — показывает их в предпросмотре перед применением.', 'Фото детали — кликабельной ссылкой (открывает изображение); чтобы отвязать фото, убери его строку в ячейке. Новые фото добавляются в карточке спецификации в приложении.', 'Защита от ошибок: если в одном изделии задублирован «Путь» или удалён узел-родитель с оставшимися деталями — такое изделие не зальётся, появится понятное предупреждение (состав не испортится).', ], '0.9.23': [ 'Лак (защитный и базовый), добавленный в спецификации полуфабрикатов, считается в килограммах с примерной массой 10 г на деталь — её нужно выверить под реальный расход. В редакторе спецификации (Настройки → Спецификации) на строке лака теперь фиолетовый бейдж «вес примерный — требует корректировки» (такой же, как на складе для веса по аналогу).', ], '0.9.22': [ 'Дорожные карты цеха проставлены полуфабрикатам Гранты, Гранты-FL и С-161, у которых их раньше не было, — теперь эти детали (рассеиватели, отражатели, бленды, шестерни, корпуса и др.) можно запускать в производство и проводить по цеху. Корпус в сборе идёт через зону «Сборка» на Склад №2. Исправлены рассеиватели Гранты (ошибочно помеченные «Закупка») и рассеиватель Соляриса, у которого маршрут не доходил до склада.', 'Лак (защитный и базовый) теперь списывается при изготовлении самого полуфабриката — на этапе лакировки — и ровно один раз. Раньше лак не списывался при производстве полуфабриката, а при сборке фары мог списаться повторно. Магнетрон — аналогично. Герметик по-прежнему списывается при сборке фары.', 'Исправлен расход сырья для деталей из материалов PFS и термоэластопласта (ТЭП): раньше расход не списывался со склада, так как для этих материалов не было заведено позиции сырья. Позиции созданы.', ], '0.9.21': [ 'Запуск полуфабриката на производство теперь резервирует сырьё, из которого он изготавливается, а не списывает уже готовый такой же полуфабрикат со Склада №2. Раньше при запуске партии (например, Тешки) на изготовление система бронировала готовый полуфабрикат, если он был в наличии, — и сырьё под производство не резервировалось. Касалось всех полуфабрикатов. Для сборки готовой фары поведение прежнее (готовые полуфабрикаты по-прежнему берутся со склада).', ], '0.9.20': [ 'Кнопка «Установить» теперь корректно работает во всех браузерах. Chrome, Edge, Opera, Яндекс.Браузер — устанавливают приложение в отдельное окно. Firefox на ПК установку не поддерживает — кнопка подсказывает открыть в Chrome/Edge. Safari на Mac — «Файл → Добавить в Dock»; на iPhone/iPad — «Поделиться → На экран „Домой“». В каждом браузере кнопка на месте с правильной инструкцией.', ], '0.9.19': [ 'Установка приложения (PWA) теперь действительно работает. Причина, по которой раньше не предлагалась установка: на сервере не хватало иконок приложения (192 и 512 пикселей), без которых браузер не считает сайт приложением. Иконки добавлены. Теперь в Chrome или Edge по адресу https://155.212.161.65.sslip.io кнопка «Установить» открывает приложение в отдельном окне.', ], '0.9.18': [ 'Кнопка «Установить» теперь видна в любом браузере и сразу на экране входа (раньше пряталась до входа и в части браузеров). Где браузер поддерживает установку (Chrome/Edge и др. на https) — устанавливает приложение в отдельное окно; где нет — показывает понятную инструкцию. Если открыли по http — подсказывает открыть защищённый адрес https://155.212.161.65.sslip.io, иначе браузер установку не разрешает.', ], '0.9.17': [ 'Перенос позиции между складами и папками. На каждой плитке позиции появилась кнопка ⇄ в правом верхнем углу (под ролью «менеджер»). Нажатие открывает дерево всей иерархии склада (Склад → подгруппа, подподгруппы показаны для ориентира), выбираете папку-назначение и жмёте «Подтвердить» — позиция переезжает не только на экране, но и физически: сохраняется на сервере и обновляется на всех терминалах.', ], '0.9.16': [ 'Кнопка «Корректировка» на позиции склада (1/2/3) теперь работает. Для готовой фары или составного полуфабриката открывается спецификация — её можно править прямо со склада (добавить/изменить/удалить компоненты, веса, количество; сохраняется автоматически). Для простой детали/сырья — карточка с развесовкой (вес единицы, масса остатка, материал, поставщик, резерв) и корректировкой остатка.', 'Развесовка изделия в спецификации считается с учётом весов всех полуфабрикатов — вес подтягивается из спецификации каждого узла рекурсивно, до самой детали (раньше у изделия из ссылок-полуфабрикатов общий вес выходил нулевым).', 'На складе у позиций, подгрупп и групп с примерным («выдуманным») весом — фиолетовый бейдж, чтобы сразу видеть, что вес требует уточнения. Добавлены примерные веса корпуса и рассеивателя Hyundai.', 'Печать QR-стикеров паллет: номер наряда больше не вылазит за лист; постраничный просмотр стикеров (по 4, клик — крупно); два режима — «По стандарту» (равное количество на каждой паллете) и «Выборочно» (своё количество на каждой паллете, печать по очереди).', 'Главная: группы готовой продукции (ПТФ / Фара / Фонарь / ДХО) и «ПФ как запчасть» сворачиваются (клик по заголовку раскрывает); у полуфабрикатов появилась полоса наличия.', 'Кнопка «Установить приложение» теперь видна прямо в шапке (раньше пряталась, если браузер не предлагал установку).', 'Надёжность: приложение работает без интернета на заводе (библиотеки загружаются локально, не из сети); синхронизация сама переподключается при обрыве связи или возврате на вкладку; в Настройках честно помечены разделы «в разработке».', 'Защита от двойного учёта на складе: при плохой связи повторная отправка одной и той же операции больше не может списать или начислить остаток дважды (на уровне базы данных).', ], '0.9.15': [ 'Цех: при производстве корпуса отдельным нарядом в окне сборки кнопка теперь правильно «Принять корпус на Склад №2» (раньше при открытии папки писалось «Отгрузить на Склад №3»). Полуфабрикаты едут на Склад №2, готовые фары — на Склад №3.', 'Склад №3 и главная: корпус и рассеиватель «как запчасть» показаны рядом с готовыми фарами на Складе №3 (в зелёной рамке, дублируют подгруппы Склада №2 — то же название, код и количество, под реализацию). На Складе №2 у корпуса и рассеивателя — отметка «ПФ-запч.». Пустая подгруппа-дубль «Рассеиватели» со Склада №3 убрана.', 'Архив нарядов: завершённый наряд теперь можно «Сохранить в архив» вместо удаления. Кнопка «Архив» — на полосе буфера заявок и на странице «Сборщик»; открывает список архивных нарядов с возможностью восстановить. Архив переживает перезагрузку страницы.', 'Страница «Сборщик» теперь показывает реальные наряды в производстве (раньше была пустой). Туда же ведёт плитка «В производстве» с главной.', 'Главная, блок «Готовые продукты»: показывает реальный остаток Склада №3, разбитый по типам изделий — ПТФ, Фара / блок-фара, Фонарь, ДХО.', 'Кнопки «Скрыть шапку» (📌) и «Редактировать склад» (✎) опущены под шапку; при скрытой шапке поднимаются в верхние углы.', ], '0.9.14': [ 'Расходные материалы считаются в своих единицах. Герметик — в миллилитрах (был ошибочно в штуках). На шаге «Проверка комплектности» у каждой позиции теперь видна единица: «× 5 мл», «нехв. 5000 мл», «× 0.01 кг» — а не голое число, которое читалось как штуки.', 'Лак (защитный и базовый) и магнетрон добавлены отдельными редактируемыми строками во все полуфабрикаты, где идёт лакировка/металлизация. Количество задаётся в кг и правится прямо в спецификации (раньше лак висел заглушкой «1 кг» и не редактировался). Магнетрон считается один раз (убран двойной счёт).', 'Корпуса блок-фар больше не металлизируются и не лакируются (C-161, С-018 Калина, ДХО Солярис, С-018 2170) — идут с литья сразу на склад/сборку; лишний расход магнетрона на корпус убран.', 'Удалены неиспользуемые позиции «Корпус 003 (Hyundai)» и «Рассеиватель 003 (Hyundai)».', 'Редактор спецификации: в выборе единицы добавлены «мл» и «г» (раньше герметик показывался как «шт»).', ], '0.9.13': [ 'Экран входа: вместо выбора пользователя карточкой — обычная форма «Логин и пароль». Логин — это email (например, aleksandr@autosvet.local), пароль вводится вручную и больше не хранится в коде приложения.', 'Вход в систему теперь один: убрано отдельное окно браузера с логином/паролем при открытии сайта — сразу открывается форма входа.', ], '0.9.12': [ 'Производство корпуса отдельным нарядом. При запуске фары, если готового корпуса нет на Складе №2, автоматически создаётся отдельная заявка на производство корпуса — она видна в буфере заявок и получает свою папку в зоне «Сборка» Цеха. Готовый корпус приходит на Склад №2, откуда фара забирает его при сборке.', 'Цех: папка наряда в зоне «Сборка» появляется сразу после запуска партии (раньше — только после того, как в зону занесли первую деталь). При дефиците корпуса в «Сборке» видны две папки — фара и корпус.', 'Честный индикатор комплектности на шаге подтверждения запуска: «✓ Комплектность» / «⚙ Корпус в производство» / «⚠ Дефицит» (раньше всегда показывалось «✓ Комплектность»).', 'Резерв при дефиците корпуса: на несуществующий корпус резерв больше не вешается (нет «зарезервировано при нулевом остатке»). Резервируются входы корпуса — сырьё тела и комплектующие — под нарядом корпуса.', '«Тело корпуса G» и «Тело корпуса GFL» теперь отдельные полуфабрикаты со своим складским учётом и партией; корпус ссылается на них.', 'Задел (выпуск сверх наряда) на окне «Наличие» разделён на две группы — «Спецификация фары» и «Спецификация корпуса»: тело корпуса, барашек и шестерни можно лить с запасом отдельно от остальной фары.', 'Спецификации: добавлена кнопка удаления спецификации полуфабриката (корзинка в строке, как у готовых фар). Убрана кнопка-переключатель «плитки/список» — всегда список. Убраны дубли «Корпус G/GFL (подсборка)» — остались только канонические «Корпус фары G» / «Корпус в сборе GFL».', ], '0.9.11': [ 'Выбор лампы при запуске: подпись слота и название лампы теперь показывают официальное складское название (например, «Лампа PY21W 12V 21W (BAU15s)»), а не устаревшее «Лампа S25 12V Amber» — слот и карточка больше не расходятся. Лампа по умолчанию отмечена рамкой.', 'Резиновые уплотнители (Пароотвод, Прокладка, Бондаж, Втулка, Уплотнительное кольцо) перенесены из полуфабрикатов (Склад №2) в покупное сырьё (Склад №1) — новая папка «Уплотнители - покупное». Папка на Складе №2 переименована в «Уплотнители - ПФ».', ], '0.9.10': [ 'Убраны дубли ламп: «Лампочка H4 / S25 / T20» (BOM-016/017/018) задваивали настоящие «Лампа …» на «Складе». Они возвращались при каждом перезапуске сервера из фонового наполнения базы — теперь оно их не создаёт и само удаляет. На складе остаётся один полный набор ламп «Лампа …».', ], '0.9.9': [ 'Алюминий теперь учитывается двумя реальными позициями из группы «Алюминий»: литьевой (тело литых деталей — расход по массе) и для магнетрона (расходник металлизации — 7 г на каждую металлизируемую деталь, включая пластиковые корпуса и отражатели). Призрак «Алюминий 5000 кг» в «Пластиках» убран — резерв и расход теперь идут на настоящие складские позиции.', 'Остаток на складе не может уйти в минус и на сервере: движение, которое увело бы остаток ниже нуля, ограничивается нулём (из нуля ничего не изготовить). Это закрепляет закон «остаток ≥ 0» на уровне базы, а не только на экране.', 'Лампы на «Складе» снова разложены по папкам 12 В · 24 В · LED · Лампы накаливания (как было локально) — раньше на сервере они показывались одним списком. Выбор лампы при запуске тоже снова сгруппирован по этим папкам.', ], '0.9.8': [ 'Остаток на складе больше не может быть отрицательным: число всегда ноль или больше. Ниже нуля уйти физически нельзя — из нуля ничего не изготовить. Закреплено в расчётах, на всех экранах и в Excel «Учёт склада» (и при показе остатка, и при импорте количества).', ], '0.9.7': [ 'Выбор лампы при запуске — теперь по гнёздам: у Гранты три лампы (H4, S25, T20) выбираются по отдельности, по умолчанию подставлены лампы из спецификации. Ламподержатели и детали вроде «пыльник/прижим центральной лампы» больше не считаются лампами и не подменяются.', 'Шаг «Выберите лампу» показывается всегда; для изделий без лампы в спецификации можно вручную добавить лампу или оставить «Без лампы».', 'Общие полуфабрикаты (барашек, бленда, бабочка, шестерни, очки, крышки, опоры и т.д.) больше не двоятся на левую и правую сторону — сведены в одну позицию; левая и правая фара берут их с одного склада. По сторонам различаются только корпус, рассеиватель и отражатель.', 'Полуфабрикаты в Настройках → Спецификации снова показываются по названию (например, «Барашек G»), а не артикулом.', ], '0.9.6': [ 'Новый лист «Учёт склада» при экспорте в Excel: по каждой позиции виден текущий остаток и неснижаемый остаток, и статус «требуется закуп» (покупное) или «требуется изготовление» (полуфабрикаты) — загорается, когда остаток подходит к минимуму (минимум + 10%).', 'Количество и неснижаемый остаток можно править прямо в листе «Учёт склада» — после импорта файла они применяются: количество меняется движением по складу, минимум записывается в карточку позиции.', 'Импорт из Excel теперь применяет правки всех полей (артикул, наименование, группа, материал, поставщик и т.д.), а не только артикула и названия. Перенос позиции в другую группу — по имени группы; недостающие группы создаются автоматически.', 'На главной плитка «Требуется закуп» разделена на две: «Требуется закуп» (покупное) и «Требуется изготовление» (полуфабрикаты).', ], '0.9.5': [ 'Спецификации фар переведены на ссылки: фара состоит из готовых полуфабрикатов (корпус, бленда, отражатель…), а сырьё указано в спецификации каждого полуфабриката отдельно. При создании заявки и в сборке виден чистый список полуфабрикатов — без сырья.', 'Корпус — отдельная подсборка из своих полуфабрикатов (барашек, шестерни, бабочка). Если корпуса не хватает на складе, в Цех попадают только реально недостающие детали, а списание идёт без двойного расхода материала.', ], '0.9.4': [ 'Цех: подтверждение детали в сборке теперь синхронизируется со статусом в зоне (раньше после обновления страницы статус «поступило» мог не отображаться).', 'Цех, зона «Склад №3»: показывается история отгрузок наряда — сколько единиц и когда отгружено, по каждой частичной отгрузке.', 'Настройки: экспорт и импорт базы знаний (номенклатура и спецификации) через Excel.', 'Настройки: новый раздел «Обновления» — полная история версий с описанием изменений.', ], '0.9.3': [ 'Корпус фары теперь учитывается как единая позиция «под сборку»: если готовый корпус есть на Складе №2 — он берётся целиком, без повторного резерва его деталей (барашек, шестерни, винты). Если корпуса нет — резервируется сырьё на его изготовление (рекурсивно, до самых деталей); если корпуса хватает частично — часть берётся целиком, остаток собирается из сырья.', 'Цех больше не запускает партии деталей корпуса (барашек, шестерни), когда сам корпус берётся со склада целиком.', ], '0.9.2': [ 'Выбранная лампа теперь действительно резервируется при запуске для всех изделий. Исправлена ошибка с неверным складским кодом лампы: из-за неё для блок-фар Гранта запуск вообще не создавал резерв, а для остальных фар лампа не попадала в резерв.', ], '0.9.1': [ 'Литьевой алюминий теперь резервируется при запуске (C-161 и другие фары с AL-литьём) — раньше для него не было позиции сырья на складе.', ], '0.9.0': [ 'Резерв при запуске больше не «мигает и исчезает» — он переотправляется при кратком сбое прав или гонке и надёжно сохраняется.', 'Кнопка «Обновить» прямо в приложении: при новой версии — зелёный бейдж на иконке аккаунта, нажал «Обновить» — и всё обновилось. Больше никаких Ctrl+Shift+R и DevTools.', ], '0.8.9': [ 'Появилась кнопка «Обновить» прямо в приложении и зелёная точка на иконке аккаунта при выходе новой версии — больше не нужны Ctrl+Shift+R и DevTools', ], '0.8.8': [ 'Резерв при запуске теперь сохраняется для всех изделий (Гранта и др.) — раньше резерв молча не записывался из-за рассинхрона ключа заявки', 'Убран бесконечный поток ошибок 400 от «зависшей» операции резерва удалённого наряда', ], '0.8.7': [ 'Синий бейдж «отгружено N» на готовой фаре Склада №3 теперь виден и на папках/группах (как amber-резерв) — на самой позиции и на всех папках над ней', ], '0.8.6': [ 'Цех: перенос партии ТЕПЕРЬ РЕАЛЬНО сохраняется на сервере — позиция больше не откатывается на ТПА/литьё (исправлен глубокий баг: отправка на сервер молча пропускалась)', 'Убран бесконечный поток ошибок 404 от застрявших повторов в очереди', ], '0.8.5': [ 'Цех: перенос партии по дорожной карте — позиция больше не возвращается на ТПА после перезагрузки', 'Убран двойник партии (локальная копия рядом с серверной) после запуска наряда', ], '0.8.4': [ 'Окно «что нового» в меню — читаемое и раскрывается вниз', 'Синий бейдж «отгружено» теперь виден и в плиточном виде Склада №3', ], '0.8.3': [ 'Цех: переносы партий по дорожной карте надёжно сохраняются — позиция больше не возвращается на ТПА', 'Меню аккаунта: список правок версии при наведении на номер', ], '0.8.2': [ 'Цех: готовый ПФ можно дропнуть сразу в «Сборку» / «Буфер» / «Склад №2»', 'Папка сборки исчезает после завершения наряда', 'Синий бейдж «отгружено N» на готовой фаре Склада №3 (до закрытия наряда)', ], '0.8.1': [ 'Папка сборки больше не пропадает после частичной отгрузки', ], '0.8.0': [ 'Частичная отгрузка готовых фар на Склад №3 (по частям)', 'Наряд показывает «Заявлено · Отгружено · Осталось»', 'Стикеры паллет A4 с QR-кодом + сканер на телефоне', 'Досрочное закрытие наряда с обязательной причиной', 'Правка тиража ПФ на запуске (можно налить больше про запас)', ], '0.7.7': [ 'Произведённые полуфабрикаты теперь корректно приходуются на склад (раньше приход мог «теряться»)', 'Права доступа больше не слетают на заводском сервере по локальной сети (HTTP)', 'Убраны «партии-сироты» в Цеху, не привязанные ни к одному наряду', ], '0.7.6': [ 'Резерв полуфабрикатов Склада №2 теперь надёжно сохраняется', 'Права доступа перестали слетать — роль владельца держится при кратких сбоях связи', ], '0.7.5': [ 'После удаления наряда в Цеху больше не остаются «висящие» позиции', 'Окно резерва показывает сразу три группы: готовые ПФ на складе + сырьё на изготовление + покупная фурнитура', ], '0.7.4': [ 'Исправлен накапливающийся «раздув» остатков Склада №2', 'Позиции Цеха от самостоятельных запусков больше не исчезают и не «возвращаются» назад', 'Превью резерва считается по спецификации самого изделия, а не всегда по Гранте', ], '0.7.3': [ 'Пакет из 8 исправлений Цеха и резерва: убран дубль партии после переноса, корректный приход ПФ на склад, сборка фары доходит до готовности при наличии деталей на складе', ], '0.7.2': [ 'Резерв при создании заявки считается по спецификации заказанного изделия', 'Цех больше не запускает в производство то, что уже есть на Складе №2', ], '0.7.1': [ 'Синхронизация склад↔цех: маршрут детали берётся из плана и редактора маршрутов (этап металлизации / ВАК больше не теряется)', 'Проверка комплектности читает реальный остаток Склада №2', ], '0.7.0': [ 'Большой спринт: списание склада при производстве, редактор маршрутов Цеха, папки сборки', 'Каскадное удаление наряда, вход в одно касание, резерв всей спецификации, стабильность прав доступа', ], '0.6.3': [ 'Технические доработки развёртывания, мобильной сборки и конфигурации', ], };