Skip to main content
Полный перечень полей — в глоссарии.

Что описывает эта страница

Раздел отвечает за расходные материалы мастерской и их складской учёт. Здесь два тесно связанных объекта: Складской ресурс — сам расходник с запасом и остатком, и Ресурс задачи мастерской — строка привязки ресурса (или единицы инвентаря) к конкретной ремонтной задаче. Отдельный Сервис складского учёта отвечает за то, чтобы остаток корректно уменьшался при использовании расходника в задаче и возвращался при отмене. Главная идея: остаток на складе — это «живое» число, которое автоматически меняется по мере того, как расходники добавляются в ремонтные задачи и убираются из них. При этом все проверки (хватает ли остатка, допустимо ли дробное количество) выполняются под блокировкой строки, чтобы два одновременных списания не «ушли в минус».

Модель данных

Складской ресурс

Расходный материал с учётом запаса. Хранит полный объём закупки и текущий доступный остаток. Тип учёта определяет поведение количеств:
  • Штучный (целые единицы) — количество всегда округляется до целого, дробные значения запрещены (например, «3 фильтра», но не «3,5»).
  • Количественный (дробный) — допускается произвольное дробное значение (например, «2,75 литра краски»).
Удаление ресурса — мягкое: при удалении через интерфейс запись лишь помечается как удалённая и пропадает из списков и из выборки для чтения/редактирования. Сама строка и её остаток сохраняются в базе.

Ресурс задачи мастерской

Строка, которая связывает складской ресурс либо единицу/группу инвентаря с конкретной ремонтной задачей, фиксируя количество, стоимость позиции и статус подтверждения.
Позиция может ссылаться на складской расходник, на единицу инвентаря или на группу инвентаря — поля независимы и все допускают пустое значение. Складской остаток трогается только тогда, когда заполнен именно складской ресурс; для «инвентарных» позиций списание со склада не выполняется.
Уникальность. Для одной компании нельзя дважды привязать одну и ту же единицу инвентаря к одной и той же задаче — на уровне базы действует ограничение на сочетание «компания + единица инвентаря + задача», и оно применяется только когда единица инвентаря заполнена. Позиции без единицы инвентаря этим ограничением не связаны.

Складской учёт: списание, возврат, пересчёт

Все операции над остатком собраны в едином Сервисе складского учёта. Каждая из них выполняется в транзакции и перед изменением остатка берёт блокировку строки ресурса — то есть до конца операции другая параллельная операция не сможет одновременно изменить этот же остаток. Это защищает от гонок при одновременном списании.

Списание

Уменьшает остаток ресурса на заданное количество. Порядок проверок:
  1. Если ресурс не указан или количество не положительное — операция ничего не делает и молча завершается.
  2. Строка ресурса блокируется и перечитывается из базы (берётся актуальный остаток, а не тот, что был в памяти).
  3. Для штучного ресурса проверяется целостность количества: дробное значение приводит к ошибке «дробное количество недопустимо».
  4. Если доступный остаток меньше запрошенного — возникает ошибка «недостаточно остатка» с указанием имени ресурса, доступного и требуемого количества.
  5. Иначе остаток уменьшается на запрошенное количество и сохраняется.

Возврат

Возвращает количество в остаток. Новый остаток вычисляется как текущий остаток плюс возвращаемое количество, но не выше общего количества ресурса — то есть остаток никогда не превысит полный запас на складе. Пустой ресурс или неположительное количество игнорируются.

Пересчёт

Применяется при правке позиции: сначала выполняется возврат старого количества старого ресурса, затем списание нового количества нового ресурса. Так корректно обрабатывается и смена ресурса, и изменение количества в уже существующей строке. Обе операции идут в одной транзакции, поэтому если новое списание не проходит (не хватает остатка или дробное штучное количество), уже сделанный возврат откатывается вместе с ним.

Где и когда меняется остаток

Это ключевой и неочевидный момент раздела: списание/возврат остатка привязаны к разным точкам жизненного цикла позиции.
  • Списание и пересчёт вызываются только внутри формата при создании и правке позиции через API. То есть остаток уменьшается ровно в момент, когда позицию добавляют к задаче или редактируют через соответствующий адрес.
  • Возврат вызывается автоматически при удалении позиции. Поэтому когда удаляют саму ремонтную задачу, её позиции удаляются каскадно, для каждой срабатывает возврат, и остаток корректно возвращается на склад.
Из-за этой асимметрии создание или изменение позиции в обход API (например, напрямую в базе, через админку или из служебного кода) не спишет остаток со склада. Возврат при этом всё равно сработает при удалении, поскольку он завязан на удаление записи, а не на адрес. Меняйте позиции только через штатные адреса, иначе учёт остатка разъедется.
Помимо складского учёта, при любом сохранении и удалении позиции автоматически запускается пересчёт стоимости задачи — суммарная стоимость ремонтной задачи всегда остаётся согласованной с набором её позиций.

Обработка ошибок склада

Сервис складского учёта намеренно не знает про HTTP и бросает собственные, «фреймворко-независимые» исключения — «недостаточно остатка» и «дробное количество недопустимо». Превращение их в понятный ответ API происходит на границе формата: вызовы списания и пересчёта обёрнуты в трансляцию складских ошибок, которая переводит их в обычную ошибку валидации с текстом на русском:
  • нехватка остатка → сообщение вида «Недостаточно «название». Доступно: …, требуется: …»;
  • дробное количество для штучного ресурса → «название учитывается в целых единицах».
Такое разделение позволяет использовать один и тот же складской сервис и из веб-запросов, и из фонового/служебного кода: бизнес-логика склада не завязана на слой API, а перевод в HTTP-ответ добавляется только там, где он нужен.

Как это используется

Справочник складских ресурсов

Список и создание ресурсов доступны аутентифицированным пользователям; из выборки всегда исключены мягко удалённые записи. При создании нового ресурса действует правило: если остаток явно не задан, он приравнивается к общему количеству — «свежий» запас изначально доступен целиком. Удаление ресурса переводит его в состояние «удалён» (мягкое удаление), а не стирает физически. Для штучных ресурсов при сохранении справочника дробная часть у общего количества и остатка отбрасывается (значения приводятся к целым). Дополнительно проверяется, что остаток не отрицателен и не превышает общее количество.

Позиции задачи

Позиции читаются и создаются в контексте конкретной задачи (задача берётся из адреса запроса, а не из тела). При создании позиции, если она ссылается на единицу инвентаря, помимо стандартных проверок выполняется собственная проверка на дубликат по сочетанию «задача + единица инвентаря + группа инвентаря», и при совпадении возвращается ошибка. Для позиций без единицы инвентаря эта проверка не выполняется. После сохранения строки происходит списание остатка привязанного складского ресурса; при правке — пересчёт (возврат старого, списание нового).
Проверка дубликата при сохранении (по задаче, единице и группе инвентаря) и жёсткое ограничение уникальности в базе (по компании, единице инвентаря и задаче) — это два разных барьера с немного разным набором полей. Оба срабатывают только при заполненной единице инвентаря. Ограничение базы — окончательная гарантия целостности; проверка формата даёт более раннее и понятное сообщение об ошибке.

Связанные страницы

Глоссарий: Ресурсы и склад

Полный перечень полей складского ресурса и позиции задачи мастерской.

Мастерская (ремонтные задачи)

Обзорная страница модуля мастерской и навигация по всем его разделам.

Воронка и задачи

Kanban-воронки, этапы и ремонтные задачи с их статусами, порядком и агрегатами по списку.

Услуги

Привязка услуг из каталога к ремонтным задачам с тарифом, ценой и длительностью.

Стоимость и оплаты

Автоагрегация цены задачи из ресурсов и услуг и суммы оплат из платежей, плюс статусы оплаты.

Метрика прибыли

Проецирование прибыли по ресурсам и услугам завершённой задачи в общую таблицу метрик.

Автосмена состояния инвентаря

Автоматический перевод инвентаря в целевое состояние при движении задачи по воронке.