-
Plugins
-
Billing
-
Collaboration
-
Planning
-
Messaging
-
Security
-
Service
-
Telecom [RU]
-
Release Notes
Plugins
Billing
Collaboration
Planning
Messaging
Security
Service
Telecom [RU]
Release Notes
Плагин учёта сетевого оборудования и складских остатков ТМЦ (материалов), ID — inventoru. Два основных направления:
Equipment — сетевые устройства (OLT, ONU, коммутаторы, роутеры и т.д.) с типами, производителями, статусами и атрибутами.
Складской учёт — номенклатура (ТМЦ), склады, остатки, движения (приход, списание, резерв, возврат, перемещение, корректировка), штрихкоды.
Плагин интегрируется с процессами BGERP через отдельную вкладку «Materials» — резерв и списание прямо из карточки процесса, с блокировкой формы после согласования (см. Вкладка процесса «Materials»).
Есть отдельная двусторонняя интеграция остатков с 1С — реализована как sub-package sync1c внутри этого же плагина (не отдельный плагин: таблицы inventoru_sync1c_*, свой namespace классов, но общий Plugin.java/db.sql/action.xml/l10n.xml). Полностью описана в отдельном документе: Inventory ↔ 1С (sync1c).
Склады, номенклатура и оборудование организованы в древовидные иерархии типов (StoreType/ItemType/EquipType) — по тому же паттерну, что и типы процессов в ядре: у каждого типа есть подтипы, набор параметров (param.ids) и (для складов) набор статусов (status.ids), задаваемые независимо на каждом уровне иерархии. У типов номенклатуры дополнительно задаётся вид учёта accounting.kind (см. Вид учёта: расходники и «числится за складом»). См. Паттерн иерархии типов.
Добавить в конфигурацию (config_global):
inventoru:enable=1
Требуется полный рестарт сервера — список включённых плагинов вычисляется один раз при старте, добавление ключа на работающем сервере не подхватывается (без ошибок в логе). Проверка: строка Plugin 'inventoru' … init в log/bgerp.log после рестарта.
Настраиваются через Administration → Permission Sets. Основные группы (полный список — action.xml плагина):
| Раздел | Что даёт |
|---|---|
|
Устройства |
Просмотр, карточка, создание, редактирование, удаление, восстановление, производители, типы устройств |
|
Items |
Просмотр, создание, редактирование, удаление |
|
Warehouses |
Просмотр, карточка, «My warehouse» ( |
|
Stock Movements |
Запись движения (одна/несколько), снятие резерва, вкладка процесса «Materials», перемещение между складами ( |
|
Администрирование движений ТМЦ |
Журнал движений очередью ( |
|
Администрирование складов / номенклатуры / оборудования |
Заведение, правка, удаление и восстановление — на карточке записи ( |
|
Производители и модели оборудования |
Пользовательские экраны-очереди ( |
|
Типы складов / номенклатуры / оборудования / производителей / моделей |
CRUD иерархии и свойства типа (параметры). Статусы складов — отдельный экран ( |
|
Очереди (админ + пользователь) |
Админский экран один на все сущности ( |
|
Incoming from 1C (пользователь) |
Просмотр/проведение/отклонение pending-остатков от 1С — доступно любому с доступом к складу (владелец, |
|
1C Sync |
Админка sync1c: инстансы, маппинг, импорт/экспорт, OData, штрихкоды — см. sync1c |
Включается для конкретного типа процесса (не глобально), в конфигурации типа:
inventoru:process.showTab=1
Префикс у ключа общий с остальным конфигом плагина, но лежит он не в config_global — его место в поле Конфигурация карточки типа процесса (колонка process_type.config, её разбирает TypeProperties.getConfigMap(), откуда ключ и читает вкладка). Соседняя колонка process_type.data — не то же самое: там живут create.status, status.ids, param.ids, и ключа вкладки в ней быть не должно. Дополнительно у пользователя должно быть право /user/plugin/inventoru/movement:processTab.
Что считать заполненной карточкой оборудования, зависит от учёта: где-то технику ведут по серийному номеру, где-то по инвентарному, где-то достаточно наименования и модели. Поэтому список обязательных полей задаётся конфигом (config_global):
inventoru:device.requiredFields=name, deviceTypeId, serialNumber
По умолчанию — name: устройство без наименования ничем не идентифицируется. Модель раньше тоже была обязательной, но у складской техники модель — это её позиция номенклатуры: устройство, заведённое приходом или загруженное с номенклатурой, модели не имеет, и его карточку нельзя было сохранить. Пустое значение отключает проверку.
Допустимые имена (как у полей формы): name, deviceTypeId, equipTypeId, itemId, storeId, serialNumber, macAddress, assetTag, ip, swVersion. Незнакомое имя — ошибка, а не молча выполненное условие: опечатка в конфиге иначе тихо отключила бы проверку.
Проверка выполняется на сервере, в обоих путях сохранения — админском и пользовательском. Атрибут required в HTML тут не работает в принципе: форма отправляется через $$.ajax.post, а не нативной отправкой, поэтому браузер её не проверяет. Обязательные поля помечаются в форме звёздочкой по тому же списку.
|
Статус — это просто запись в глобальном каталоге ( |
Список статусов, в которых разрешено резервировать/отменять резерв материалов — per-type ключ в том же config_global, где уже лежит sync1c:processType.<ID>.statusWriteoff (см. sync1c):
inventoru:processType.<TYPE_ID>.reserveStatuses=<ID1>,<ID2>,...
Пусто/не задано для типа — резерв разрешён в любом статусе (поведение по умолчанию, обратная совместимость). Правится в Administration → Configuration → строка плагина, применяется без пересборки и рестарта сервера (SetupChangedEvent).
Дополнительно, независимо от статуса: если по процессу уже есть успешно отправленный в 1С экспорт списания, вкладка «Materials» заблокирована в любом случае — см. sync1c.
Права в наборе прав (Administration → Permission Sets) отвечают на вопрос «ЧТО роль может делать» — править склад, удалять оборудование, делать приход. На какие именно склады это распространяется, решает плагин сам, по связи пользователя со складом. Связей две, и они дают разное:
| Роль | Как задаётся | Что даёт |
|---|---|---|
|
Монтажник — работает со складом |
владелец склада ( |
видит склад, его остатки и оборудование; резервирует, возвращает, перемещает материалы во вкладке «Materials» процесса; проводит «Ожидающие поступления» из 1С по этому складу. Склад, его параметры и оборудование не правит, даже если в наборе прав есть узлы правки. |
|
Кладовщик — управляет складом |
участник «группы редактирования» склада ( |
всё, что монтажник, и то, что разрешают его права: правка и удаление карточки склада, его параметры и минимальные остатки, оборудование на нём (в том числе массовый импорт), загрузка CSV остатков, приход и корректировка. |
|
Администратор складов |
право |
все склады без ограничения; единственный, кто заводит склады и распоряжается доступом к ним (выданный доступ, группа редактирования). |
Обе меры считаются в одном месте — StoreAccessService: storeIds() / allowed() для узлов, меняющих склад (умолчание — управляет: группа редактирования), workStoreIds() для работы с материалами (умолчание — работает: владелец ∪ доступ лично ∪ доступ на группу ∪ группа редактирования); обе читают опции своего узла, см. Опции узлов прав — где действует узел. Видимость складов в списках — accessibleStoreIds(). Своих копий у экранов нет намеренно.
Раньше мера была одна: «есть доступ к складу» — и монтажник, которому по ошибке достались узлы кладовщика, переписывал владельца склада, к которому у него был только выданный доступ, и удалял склад вместе с остатками. Узлы правки теперь безопасно давать набору прав кладовщика — сервер ограничит их его складами.
Право Executor access → List выглядит безобидно — «посмотреть список доступов» — но именно оно и есть маркер администратора складов: видит и правит ВСЕ склады, заводит склады, выдаёт доступ. Кладовщику его выдавать нельзя. Раньше маркером был /admin/plugin/inventoru/store:null, узел удалённого админского списка складов; в старых наборах прав его можно снимать. Узлы Editing group, Add, Delete, Close period без этого маркера ничего не дают: иначе держатель одного узла «группа редактирования» вписал бы свою группу в любой склад и стал бы им управлять. |
Независимо от ролей удаление отказывает, пока объекту есть что терять:
склад — пока на нём есть остаток, резерв, оборудование или не выгруженные в 1С списания;
позиция номенклатуры — пока по ней есть остаток, резерв или оборудование;
тип склада, статус склада, тип оборудования, производитель, тип производителя, тип модели — пока на них ссылаются записи; инстанс 1С — пока у него есть привязки складов (выключить его можно всегда).
Как у процессов в ядре (onlyPermittedTypes, allowOnlyGroups), у узлов плагина есть опции, которые задаются в редакторе набора прав (Administration → Permission Sets, текст под узлом описывает их там же). Умолчания — роли выше; опциями их расширяют или сужают для конкретного набора прав без правки кода.
| Опция | Значение |
|---|---|
|
|
На каких складах действует узел: |
|
|
Через запятую — типы складов, которыми узел ограничен; пусто — любые. |
|
|
Только у Return to warehouse. |
Администратор складов (Executor access → List) опциями не ограничивается: у узлов, меняющих склад, для него — все склады. В работе с материалами — наоборот: во вкладке «Materials» процесса и у возврата он, как и все, видит свои склады, пока у узла нет stores=all.
Пример: кладовщик филиала правит склады только своего типа — /user/plugin/inventoru/store:update с опциями stores=all и storeTypeIds=3.
Administration → Permission Sets — набор прав кладовщика: /user/plugin/inventoru/store:{null,show,my,queueFilter,queueShow,edit,update,delete,restore}, /user/plugin/inventoru/device:{null,show,queueFilter,queueShow,edit,update,delete,restore}, /user/plugin/inventoru/import_pending:{null,apply,applyAll,reject}, /admin/plugin/inventoru/store:minLevelUpdate, /admin/plugin/inventoru/movement:{null,queueFilter,queueShow,receipt,adjust,writeoffUnit}; если нужна загрузка CSV — /admin/plugin/inventoru/sync1c/instance:{importFileForm, importFilePreview,importFileConfirm,importFileAdd} (эти права независимы от остальной админки sync1c, см. sync1c); если он заполняет параметры складов — /user/plugin/inventoru/storeParam:update (см. Право на изменение параметров склада). Все эти узлы действуют только на склады его группы редактирования. НЕ включать /admin/plugin/inventoru/store:accessList и /admin/plugin/inventoru/sync1c/instance:null — они превращают пользователя в администратора.
Administration → Users → Groups — группа, в неё нужные сотрудники; набор прав из шага 1 привязать к группе.
Инвентарь → Склады — карточка склада, кнопка Доступ, комбо Группа редактирования — выбрать группу. Состав группы можно менять: принятый сотрудник получает склад сразу, исключённый теряет.
Выданный доступ на том же экране (сотруднику или группе, на период, закрывается кнопкой «Закрыть период» без удаления истории) — это доступ монтажника: работать со складом, а не управлять им. Годится для бригады на сезон, подмены на время отпуска.
Номенклатура — общая для всей компании: item:{create,update,delete} кладовщику не нужны, удаление позиции задевает всех; для работы достаточно item:{null,show,queueFilter,queueShow}.
Значения параметров пишет ядровой экшен /user/parameter:parameterUpdate — один на все типы объектов сразу. Право Parameters → Edit в наборе прав выдаётся, как правило, ради параметров процессов, и вместе с ними молча отдаёт параметры складов: монтажник, заполняющий свои процессы, мог переписать адрес, категорию и код 1С любого склада, к которому у него есть доступ.
Поэтому у плагина есть свой узел: Plugin Inventory → User → Warehouses → Edit parameters (/user/plugin/inventoru/storeParam:update). Он ничего не диспетчеризует — экшена storeParam не существует, — право проверяет сам плагин в двух местах:
StoreAction.show — без права карточка отдаёт параметры только на чтение (readOnly для parameter_list.jsp): ни форм правки, ни карандашей;
StoreParamAccessListener — без права запись отклоняется с сообщением, даже если запрос собран руками в обход экрана.
Склад — отдельный вопрос и проверяется сверх этого: право даёт возможность править параметры складов вообще, а каких именно — те, которыми пользователь управляет (Доступ к складам — роли).
| Отсутствие права не отбирает у пользователя просмотр параметров и ссылку «лог» — они по-прежнему видны, меняется только возможность правки. |
Очередь — настраиваемый список сущности: складов, номенклатуры, оборудования, производителей, моделей оборудования или движений ТМЦ. Заводится в оснастке Администрирование / Инвентарь / Очереди, там же выбирается сущность, которую очередь показывает. Конфигурация устроена так же, как конфигурация очереди процессов.
Кому доступна очередь, задаётся не конфигурацией, а в самой форме очереди — списками Группы пользователей и Пользователи. Действует объединение: сотрудник видит очередь, если она выдана лично ему или любой из его групп. Так же выдаются очереди процессов в ядре (user_queue и user_group_queue), с той разницей, что там это делается в карточке пользователя и группы, а здесь — в очереди. Очередь, не выданная никому, не показывается никому.
column.<ID>.value=<ЧТО ПОКАЗЫВАТЬ> column.<ID>.title=<ЗАГОЛОВОК>
Колонка, названная внешним ключом (item_id, store_id, manufacturer_id, user_id), показывает не код, а наименование того, на что ключ указывает. Колонка type у движений и status у оборудования показывает состояние словом.
Значения column.<ID>.value зависят от сущности очереди. Это логические имена, а не имена колонок БД: заголовок записи всегда title, на какую колонку он ложится — дело сущности (у оборудования это device.name, у модели device_type.model, у движения movement.comment). Незнакомое значение колонкой не становится — она пропускается, а в log/bgerp.log пишется Unknown column value '<что было>' for entity '<сущность>', available: […] со списком допустимых. Экран при этом открывается — просто без этой колонки, поэтому пропажу видно только в логе.
| Сущность | Значения |
|---|---|
|
Склады |
|
|
Номенклатура |
|
|
Оборудование |
|
|
Производители |
|
|
Модели оборудования |
|
|
Движения ТМЦ |
|
Мягкого удаления нет у производителей, моделей и движений — колонки deleted у них не бывает. Движения к тому же не имеют собственных параметров, поэтому и param:<ID> для них недоступен: такая колонка в конфигурации пропускается с ошибкой в лог.
filter.<ID>.type=<ТИП> filter.<ID>.title=<ПОДПИСЬ> # скрытый фильтр: не предлагается в списке, но применяется #filter.<ID>.show=0
Типы фильтров: title — у всех; deleted — у сущностей с мягким удалением (склады, номенклатура, оборудование); param:<ID параметра> — у всех, кроме движений ТМЦ, у которых нет своих параметров. Фильтр по типу сущности есть у каждой, у кого тип есть: storeType, itemType, equipType, manufacturerType, modelType. Для складов ещё status и user (владелец), для сущностей, живущих на складе, — store; для движений ТМЦ — type (операция), dt (диапазон дат операции) и user (кто провёл).
Фильтры по ключу и по тексту заводятся у той сущности, у которой есть соответствующее поле, — список полей у каждой сущности в таблице колонок очереди:
| Фильтр | Что отбирает |
|---|---|
|
|
Позиция номенклатуры — прежде всего в журнале движений: «что происходило с этой позицией» |
|
|
Производитель — у моделей оборудования |
|
|
Поиск подстрокой по серийному номеру: у оборудования и у движений ТМЦ (движение помнит серийник единицы, которую двигали) |
|
|
Поиск подстрокой по адресу оборудования |
|
|
Поиск подстрокой по стране производителя |
|
|
Поиск подстрокой по MAC-адресу оборудования |
|
|
Поиск подстрокой по инвентарному номеру оборудования |
Фильтр, для которого у сущности нет поля, в очередь не попадает — в лог уходит строка «Entity '…' has no field '…', filter '…' skipped».
Фильтр «владелец» на складах первой строкой предлагает Общий склад — так отбираются склады без владельца (user_id = 0), это обычный случай, а не край.
Фильтры по типу отбирают вместе с подтипами: выбранное «Оборудование» находит и позиции, заведённые под его дочерними типами. Иерархия читается в момент применения фильтра, поэтому подтип, добавленный после настройки очереди, сразу считается своим. Тип, которого у сущности нет (itemType в очереди складов), в очередь не попадает — в лог уходит строка «Filter 'itemType' is only available for nomenclature».
|
Поведение |
Имена легко спутать: type — это операция в очереди движений ТМЦ, а тип сущности называется itemType, storeType, equipType. Неизвестное имя фильтра экран не роняет — фильтр молча пропускается, в лог уходит «Unknown store queue filter type: …».
Фильтр-диапазон по дате называется именем своего поля и читает <поле>From / <поле>To, как create_date у очереди процессов.
Фильтр по параметру берёт подпись без title из названия самого параметра, а вид контрола — из типа параметра, ровно как очередь процессов:
| Тип параметра | Что показывается и как ищет |
|---|---|
|
|
Строка поиска по вхождению |
|
|
Выбор значений; отдельный пункт «Нет значения» находит записи, у которых параметр не заполнен |
|
|
Два поля «с» и «по»; верхняя граница включает весь названный день |
|
|
Диапазон «От»/«До» и флажок «Нет значения» |
|
|
Выбор города, квартала, улицы, дома и квартиры с подсказками адресной базы |
Поддержаны те же типы параметров и те же необязательные ключи фильтра, что перечислены в документации очереди процессов — orEmpty, valueFrom/valueTo=curdate, values, availableValues, defaultValues, onEmptyValues, width, fields. Контролы на экране рисует сам ядровый фрагмент, поэтому расхождений с очередью процессов не возникает.
Типы file и email фильтром не поддерживаются — как и в ядре; такой param:<ID> в конфигурации пропускается с ошибкой в лог, чтобы на экране не появлялся контрол, который ничего не отбирает.
Сортировка настраивается ровно как у процессов:
sort.mode.<ID>.columnId=<ID КОЛОНКИ> sort.mode.<ID>.title=<НАЗВАНИЕ РЕЖИМА> #sort.mode.<ID>.desc=1 sort.combo.count=<СКОЛЬКО ВЫПАДАЮЩИХ СПИСКОВ> sort.combo.<ID>.default=<РЕЖИМ ПО УМОЛЧАНИЮ>
| Параметр в колонке или фильтре должен принадлежать той же сущности, что и очередь: у параметров склада и номенклатуры значения лежат в общих таблицах, и параметр чужой сущности показал бы значение постороннего объекта. Такая колонка или фильтр пропускается с ошибкой в лог. |
Все DB-ключи лежат в одной строке config_global («Plugin Inventory», Administration → Configuration), применяются без рестарта (SetupChangedEvent), кроме enable:
| Ключ | Назначение |
|---|---|
|
|
Включение плагина — единственный ключ, требующий рестарта сервера (см. Включение плагина) |
|
|
В конфигурации типа процесса (не в config_global) — показывать вкладку «Materials» (см. Вкладка «Materials» на процессе) |
|
|
Статусы типа процесса, в которых разрешён резерв (см. В каких статусах можно резервировать материалы) |
|
|
Обязательные поля карточки оборудования (см. Обязательные поля устройства) |
|
|
Главный склад: оборудование, выведенное из эксплуатации, возвращается сюда (см. Сохранение устройства и остаток) |
|
|
Типы процессов с интеграцией 1С (см. sync1c) |
|
|
Статус, при входе в который отправляется списание в 1С |
|
|
Максимум попыток отправки экспорта (по умолчанию 3) |
|
|
Алярм админу после N подряд неуспешных импортов инстанса (0 — отключить; по умолчанию 3) |
|
|
В поле «Config» конкретного 1С-коннектора, не в config_global (см. sync1c) |
Scheduler-таски (в bgerp.properties, scheduler.start=1):
scheduler.task.sync1c_import.class=org.bgerp.plugin.inventoru.sync1c.exec.ImportPoller
scheduler.task.sync1c_import.minutes=*/30
scheduler.task.sync1c_export.class=org.bgerp.plugin.inventoru.sync1c.exec.ExportPoller
scheduler.task.sync1c_export.minutes=*/1
Плагин добавляет группу Inventory в главное меню: Оборудование, Номенклатура, Склады, Движения ТМЦ, Поступления, Мой склад.
Экран оборудования — очередь с настраиваемыми колонками, фильтрами и сортировкой (см. Очереди). Контекстное меню (▾) на строке открывает карточку устройства.
Карточка: атрибуты, параметры типа оборудования, кнопки Редактировать, Удалить (мягкое) и Восстановить у удалённого. Кнопки тулбара очереди: добавление, Производители, Модели оборудования и Импорт оборудования — загрузка списка единиц из файла, доступна по праву /admin/plugin/inventoru/device:importForm.
Удалённые единицы показываются, если в конфигурации очереди заведён фильтр deleted и он включён.
Загрузка списка единиц из CSV — Импорт оборудования на экране оборудования, право /admin/plugin/inventoru/device:importForm. Идёт в два шага: предпросмотр (importPreview, ничего не пишет) показывает, что нашлось и что нет, и только importApply записывает — строки с ошибкой пропускаются, остальные применяются.
Ключ строки — серийный номер. Единица с таким серийником уже есть — обновляется, нет — создаётся. Поэтому повторная загрузка того же файла ничего не задваивает. Серийный номер, который встречается в файле дважды, — ошибка обеих строк.
Два применения одновременно. Кнопка Применить блокируется на время запроса, но два запроса могут прийти и иначе — из двух вкладок или от человека одновременно с загрузкой остатков из 1С. Единица читается под блокировкой БД, поэтому «такой ещё нет, создаём» не может решиться дважды: один запрос проходит, второй либо обновляет уже созданную карточку, либо отклоняется базой, и тогда экран говорит «Импорт этого файла уже выполняется — подождите несколько секунд и посмотрите список оборудования, прежде чем применять снова». Ничего из отклонённого запроса в базе не остаётся. Двойной клик по Применить однажды сделал 158 карточек на 79 серийников — вот от чего защита.
Номенклатура и остатки. Если у строки указана номенклатура с поштучным учётом и склад, строка проводится через тот же учёт, что и карточка (см. Сохранение устройства и остаток): единица привязывается к непокрытому остатку склада — остатку, который пришёл раньше серийных номеров (например, из 1С), — а если его нет, приходуется; перенос числящейся единицы на другой склад — перемещение. Предпросмотр показывает под каждой строкой, что она сделает с остатком, или почему строка не будет применена. При применении строки заново разбираются на сервере: если за время между предпросмотром и применением что-то изменилось, строка с ошибкой пропускается и после применения показывается вместе с причиной, а отказ при записи отменяет всю загрузку и называет строку.
Склады, которые ведёт 1С, файл не расставляет: на складе, сопоставленном с включённым коннектором hs, серийные номера расставляет импорт остатков 1С. Строка с таким складом дополняет устройство — номенклатура, MAC, модель, инвентарный номер, статус, — а склад ему поставит 1С; предпросмотр так и пишет.
Колонка «Номенклатура» необязательна, но без неё единица остаётся вне складского учёта: она видна в разделе «Оборудование» и в блоке оборудования на карточке склада, но не входит в остатки, не резервируется под процесс и не списывается при монтаже. Если оборудование должно выдаваться абоненту, номенклатуру в файле заполняют, а у её типа включают поштучный учёт.
Как понимать колонку «Номенклатура» выбирается на форме загрузки: по названию позиции или по её коду в 1С — значению параметра внешнего кода конкретного коннектора. Название может совпадать у нескольких позиций или отличаться от 1С-ного на пробел; код — нет. Название, которое носят несколько позиций, — ошибка строки, а не «последняя из одноимённых».
Заголовок обязателен, порядок колонок не важен, лишние игнорируются. Имена ищутся по нескольким принятым вариантам, поэтому типовая выгрузка грузится без настройки:
| Что | Принимаемые имена | Как разбирается |
|---|---|---|
|
Серийный номер (обязательна) |
|
Ключ строки. Строка с пустым серийником пропускается — так не спотыкается хвост файла |
|
Модель |
|
Нет такой — создаётся; если у строки указана номенклатура — только связывается с существующей: модель складской техники — её позиция, заводить по тексту копию не нужно |
|
Производитель |
|
Нет такого — создаётся |
|
Тип оборудования |
|
Ищется по названию среди существующих; не найден — ошибка строки |
|
Склад |
|
Ищется по названию; не найден — ошибка строки |
|
Номенклатура |
|
По названию или по коду 1С — как выбрано на форме; не найдена или неоднозначна — ошибка строки |
|
MAC-адрес |
|
Как есть |
|
Инвентарный номер |
|
Как есть |
|
Статус |
|
Только |
|
Наименование |
|
Как есть |
Разница между «создаётся» и «ошибка строки» намеренная: каталог моделей и производителей импорт как раз и должен наполнять, а склад, тип оборудования и номенклатура — это учётные сущности со своими настройками, и молча заводить их по строке файла нельзя.
Разделитель и кодировка определяются по содержимому — тот же разбор, что у импорта остатков (см. sync1c): UTF-8 и windows-1251, разделитель ;, , или табуляция, BOM снимается.
Пример файла — example_devices.csv рядом с этим документом.
| Planned |
Готовится к вводу в эксплуатацию. |
| Active |
В работе. |
| Maintenance |
Временно выведено на обслуживание. |
| Decommissioned |
Выведено из эксплуатации (требует указания причины). Устройство возвращается на главный склад ( |
Если номенклатура устройства ведётся поштучно, склад и номенклатура на карточке — это учёт, а не просто поля. Сохранение карточки (а также импорт оборудования и импорт остатков из 1С) проходит через DeviceAccountingService, который решает, какое движение нужно, или отказывает — до какой-либо записи:
| Что меняется | Что происходит |
|---|---|
|
Устройство впервые встаёт на склад, у позиции там есть непокрытый остаток |
Привязка без движения: остаток уже учитывал эту единицу, не хватало её серийного номера |
|
Устройство впервые встаёт на склад, непокрытого остатка нет |
Приход на склад |
|
Устройство, числящееся на складе, переносится на другой склад |
Перемещение; отказ, если единица зарезервирована в процессе |
|
Числящемуся устройству меняют номенклатуру или серийный номер |
Отказ: номенклатуру меняют списанием и новым приходом, серийный номер назван в движениях, резервах и списании в 1С |
|
Числящееся устройство снимают со склада или удаляют |
Отказ: со склада единица уходит только списанием — кнопкой Списать со склада на её карточке |
|
Удалённое устройство правят |
Отказ: сначала восстановить. Восстановленное встаёт на свой склад — на непокрытый остаток, если он есть, иначе приходом |
|
Серийный номер уже у другого живого устройства |
Отказ |
Ошибочно заведённая единица. На карточке устройства есть кнопка Списать со склада (право /admin/plugin/inventoru/movement:writeoffUnit, опции stores/storeTypeIds — как у прихода и корректировки). Она спрашивает причину и пишет списание этой единицы по её серийному номеру: остаток позиции уменьшается на единицу, устройство уходит со склада, после чего карточка удаляется как любая другая. Это единственный путь убрать единицу, заведённую по ошибке: удаление карточки держится за склад, склад на карточке нельзя очистить, перенос на другой склад ничего не меняет, а корректировка остатка не опускает его ниже числа стоящих на складе единиц.
Кнопка показывается только там, где списание и есть выход: у позиции с поштучным учётом, стоящей на складе. Отказы:
единица под активным резервом — сначала снять резерв;
позиция не ведётся поштучно — её остаток правится корректировкой, а карточка удаляется как есть;
склад ведёт 1С — списание проводится в 1С, следующий импорт остатков уберёт единицу здесь; иначе списание отменил бы сам импорт, увидев единицу на складе у 1С.
Склады, которые ведёт 1С. Склад, сопоставленный с включённым коннектором 1С по протоколу hs, получает свою расстановку серийных номеров из 1С — через импорт остатков. На таком складе ERP сама не ставит единицу и не переносит её: следующий импорт 1С вернул бы всё как было. Карточка и импорт оборудования на таком складе отказывают с объяснением; меняются только остальные поля.
Вывод из эксплуатации. При переводе в статус «Выведено из эксплуатации» устройство переезжает на главный склад (inventoru:device.mainStoreId) — перемещением, если оно числится. Если оно стоит на складе, который ведёт 1С, статус ставится, а переезд делает 1С: карточка подсказывает вернуть устройство на главный склад документом 1С, импорт остатков его переместит. Без настроенного главного склада устройство остаётся, где стояло. Возврат на главный склад — правило, а не выбор того, кто ставит статус: монтажник, у которого нет доступа к главному складу, выводит устройство из эксплуатации наравне с остальными, и после сохранения оно уходит из его доступа.
Список учитываемых позиций ТМЦ (кабель, патч-корды, SFP и т.д.) — очередь с настраиваемыми колонками, фильтрами и сортировкой (см. Очереди). У каждой позиции: наименование, характеристика, тип номенклатуры, внешний ID (для интеграции с 1С — хранится как text-параметр, id параметра задаётся в настройках инстанса 1С), штрихкоды (см. Штрихкоды и сканирование).
Карточка позиции открывается из контекстного меню строки и показывает тип, характеристику и параметры, которые приносит с собой тип. Оттуда же позиция правится, удаляется (мягко) и восстанавливается.
Тип номенклатуры — обязательная часть формы: именно он даёт позиции набор параметров и вид учёта (Вид учёта: расходники и «числится за складом»). Позиция без типа не имеет ни одного параметра — значение может быть записано в базе (например, код 1С, проставленный синхронизацией) и всё равно не показываться на карточке. Позициям, которые заводит импорт остатков, тип назначается настройкой коннектора import.itemTypeId — см. sync1c. |
Единица измерения позиции — та, в которой её ведёт 1С («шт», «м»…); приходит из 1С при импорте или задаётся на карточке. Позиция в штуках не принимает дробного количества ни в одном движении; строки импорта в другой единице не складываются с позицией — см. sync1c.
У типа номенклатуры задаётся accounting.kind (ItemTypeProperties): consumable (по умолчанию) — расходники, резервируются и списываются по процессу; asset — инструмент/оборудование, выданное надолго. Asset-позиции не попадают в форму резервирования вкладки «Materials» — они перемещаются только через перемещение/возврат на карточке склада, и показываются там отдельным блоком «Held by warehouse».
У типа номенклатуры задаётся serial.tracked=1 (наследуется дочерними типами). Позиция такого типа ведётся поштучно: каждая единица — отдельная запись оборудования с серийным номером и ссылкой на эту позицию. Номенклатура отвечает на вопрос «что и сколько», оборудование — «какая именно единица»; так же это устроено в 1С, где у одной карточки номенклатуры много серий.
Единица числится на складе, если её позиция ведётся поштучно, у неё указан склад и она не удалена. Правила, которые держит сам журнал движений (MovementDAO.append), кто бы движение ни создавал:
Движение поштучной позиции проводится по одной единице и называет её серийный номер.
Забрать единицу со склада (списание, перемещение, корректировка в минус) можно, только если она числится на этом складе и по этой позиции. Приход, возврат и корректировка в плюс — только если единица не числится нигде: неизвестна, стоит вне склада или под позицией без поштучного учёта. Раньше устройство искалось только по серийному номеру, и списание на одном складе единицы, стоящей на другом, молча снимало её с того склада.
Серийный номер, заведённый у нескольких устройств сразу, движение не проводит — какую из единиц двигать, система не угадывает.
Остаток может опережать единицы, но не наоборот. Устройств на складе не больше остатка позиции. Разница — непокрытый остаток: количество, пришедшее раньше серийных номеров (позиция с остатком, переведённая на поштучный учёт). Серийные номера дозаполняются, и непокрытый остаток уменьшается. Устройств больше остатка — ошибка: единица посчитана дважды или взята без движения.
Корректировка без серийного номера меняет только непокрытый остаток — ниже числа устройств на складе её не провести; лишние единицы убираются списанием по серийным номерам.
Поштучный учёт включается у позиции, у которой уже могут быть остатки, единицы и резервы. Включение (флаг у типа, тип-родитель, наследование свойств, смена типа позиции) отказывается, если по какой-то из позиций, которые от этого станут поштучными:
на складе устройств больше, чем остаток, — единица посчитана дважды;
есть активный резерв без серийного номера — выгрузка в 1С списывает ровно ту единицу, которую назвал резерв, а такой резерв не называет ни одной, и списание процесса бы не прошло.
Отказ перечисляет такие строки. Остаток без единиц включению не мешает — это переходное состояние, серийные номера дозаполняются.
Посмотреть то же до попытки — кнопка Сверка остатков с единицами в свойствах типа номенклатуры (право /admin/plugin/inventoru/itemType:serialCheck): по позициям типа и его подтипов — склад, остаток, единиц, сколько без серийных номеров, лишних единиц и резервов без серийного номера; строки, которые не дают включить учёт, выделены.
Импорт остатков 1С, назначающий позиции тип по настройке коннектора (import.itemTypeId), такой тип не назначает, если позиция не проходит ту же проверку, — пишет строку в лог.
Порядок для клиента с 1С: остатки из 1С количеством → включить поштучный учёт на типе ONU → импорт 1С с серийными номерами расставляет единицы по складам → список оборудования дополняет их (MAC, модель, инвентарный номер) → сверка: без серийных номеров не осталось ничего.
Склад может быть:
General warehouse — без конкретного владельца
Личный склад (монтажника) — закреплён за конкретным сотрудником, обычно создаётся автоматически при первом импорте остатков с 1С (см. sync1c)
Владелец задаётся на форме склада полем Личный склад сотрудника — выбором сотрудника из списка, первой строкой которого идёт Общий склад (она же снимает владельца). Владение само по себе даёт человеку рабочий доступ к складу: отдельная запись inventoru_store_access для этого не нужна (см. Доступ к складам — роли). Массового назначения владельцев нет — склад заводится и правится по одному; при переносе из внешней системы владельцев проставляют скриптом по inventoru_store.user_id.
Карточка склада показывает остатки (расходники и asset-позиции раздельно), оборудование, стоящее на складе, историю движений, формы перемещения/возврата, параметры склада и кнопки его администрирования: Редактировать, Удалить (мягкое, отменяется кнопкой Восстановить) и Доступ — выданный доступ (пользователю или группе, на период) и группа редактирования («кладовщик», см. Доступ к складам — роли). Там же, у каждой позиции остатков, задаётся минимальный остаток (inventoru_min_level) — порог подсветки «заканчивается»; отсутствие записи = порог не задан. Кнопки видны по правам и по роли: правка, удаление, минимальный остаток и загрузка CSV — тому, кто складом управляет (группа редактирования или администратор складов), Доступ — администратору складов; монтажник видит карточку без них (см. Доступ к складам — роли).
Блок Оборудование на складе перечисляет единицы, стоящие на складе (inventoru_device.store_id): наименование со ссылкой на карточку устройства, серийный номер, MAC, позицию номенклатуры и статус. Единица, не связанная с номенклатурой, помечена «Без номенклатуры» — такая единица вне складского учёта: она не входит в остатки, не резервируется и не списывается при монтаже (см. Поштучный учёт (серийные номера)). Блок не зависит от поштучного учёта: оборудование, загруженное импортом без колонки номенклатуры, видно здесь сразу. Надпись «Нет остатков номенклатуры» относится только к таблице остатков — склад с оборудованием, но без остатков, пустым не считается.
Мобильный экран монтажника (Inventory → My warehouse): все склады, к которым у пользователя есть доступ по объединению из Доступ к складам — роли (владелец ∪ store_access лично или на его группу ∪ группа редактирования), с остатками (количество/резерв) и подотчётными единицами оборудования (наименование и серийный номер, ссылка на карточку устройства) по каждому. Сверху — счётчик ожидающих поступлений из 1С по этим складам (ссылка на «Incoming», показывается только при ненулевом количестве) и кнопка Scan (см. Штрихкоды и сканирование): найденная позиция подсвечивается в списке остатков. Экран read-only — действия остаются на карточке склада и вкладке «Materials».
Отдельного документа «заявка на материалы» в плагине нет и не планируется. Монтажник оформляет потребность обычным процессом BGERP — тип процесса заводится в оснастке, кодом плагина он не поддерживается. Кладовщик комплектует материалы и передаёт их монтажнику, а в ERP это попадает приходом и списанием из 1С штатной синхронизацией (см. документ по sync1c).
Резервировать материалы в момент подачи потребности нельзя: точка правды по остаткам — 1С, и резерв, созданный в ERP «под заявку», разъехался бы с ней при первой же выдаче мимо процесса. Там, где резерв под процесс действительно нужен, он делается явно на вкладке «Materials» карточки процесса (см. Вкладка процесса «Materials») — то есть по факту, а не по намерению.
Таблица inventoru_item_barcode (barcode → item_id) наполняется импортом регистра штрихкодов из 1С по OData (см. sync1c); привязка «один штрихкод — одна позиция», повторный импорт перепривязывает.
Резолв кода — POST /user/plugin/inventoru/movement.do?method=resolveCode&code=…, порядок поиска:
Штрихкод номенклатуры (inventoru_item_barcode) → type=item;
GUID 1С — значение ext_id-параметра номенклатуры по всем включённым инстансам 1С → type=item;
Серийный номер устройства → type=device, только если у пользователя есть доступ к складу устройства (то же объединение, что в Доступ к складам — роли) — иначе type=none, как для несуществующего кода: без этой проверки скан произвольного серийника раскрывал бы, на каком складе лежит любое устройство компании.
Камера-скан ($$.inventoru.scan/scanResolve в pl.inventoru.js) — браузерный BarcodeDetector API поверх getUserMedia (нужен HTTPS или localhost; нативно — Chrome/Edge на Android). Если API или камера недоступны — fallback на ручной ввод кода через prompt, флоу не ломается. Кнопки скана есть в «Моём складе» и на вкладке «Materials».
Списки складов, номенклатуры и оборудования строятся очередями — так же, как списки процессов. Колонки, фильтры и сортировка задаются в конфигурации очереди, пользователь сам выбирает, какие из настроенных фильтров показывать, и его выбор запоминается.
Полный append-only журнал операций по складу — движения никогда не изменяются и не удаляются, только добавляются. В таблице движений на карточке склада есть колонка серийного номера: движения поштучных позиций иначе неразличимы — по строке не видно, какую именно единицу двигали.
Журнал открывается двумя экранами, и оба — очередь со своими колонками, фильтрами, сортировкой и страницами: Инвентарь → Движения ТМЦ и админский /admin/plugin/inventoru/movement, где сверху ещё приход, корректировка и загрузка CSV. Админский до 29.09.2026 показывал последние 500 записей одного выбранного склада, молча обрезая остальное и не давая ни фильтра, ни страниц. Склад теперь спрашивает каждая форма отдельно: где человек берёт стоящее на складе и что он смотрит в журнале — разные вопросы, а один переключатель отвечал на оба.
|
Читать журнал и писать в него — разные права, и это намеренно. Какие движения человек видит, решает общее правило плагина — склады, с которыми он работает ( |
| Приход (receipt) |
Поступление на склад (в админке — |
| Списание (writeoff) |
Расход/списание со склада. |
| Резерв (reserve) |
Резервирование под процесс. |
| Снятие резерва (unreserve) |
Отмена резерва. |
| Возврат (return) |
Возврат — увеличивает остаток и одновременно уменьшает резерв (не использовать для «просто прихода», см. предостережение ниже). |
| Перемещение (transfer / transfer_in) |
Между складами — связанная пара движений: |
| Корректировка (adjustment) |
Ручная коррекция остатка (инвентаризация, |
|
|
«Активный резерв» — reserve-движение, на которое не ссылается ни одно unreserve/writeoff через ref_movement_id. Запись остаётся в журнале навсегда, но перестаёт попадать в выборку «активных» после того, как на неё сослались отменой/списанием. При удалении процесса все его активные резервы автоматически снимаются (ProcessRemovedListener) — иначе reserved завис бы навсегда без UI-пути снять его.
Появляется на карточке процесса, если выполнены условия: плагин включён, inventoru:process.showTab=1 в конфигурации типа процесса, у пользователя есть право /user/plugin/inventoru/movement:processTab.
Показывает:
Форму резервирования (выбор склада — только из доступных пользователю, см. Доступ к складам — роли; выбор нескольких позиций номенклатуры и количества за один сабмит; в списке только расходники с доступным остатком > 0 — asset-позиции не резервируются, см. Вид учёта: расходники и «числится за складом»; скан-кнопка для подбора позиции по штрихкоду)
Историю по процессу: активные резервы («Reserved», с кнопкой отмены), списанные позиции («Written off», серым, без кнопки), возвращённые после реверса списания («Returned to store», зелёным)
Форма скрывается (read-only) и на клиенте (JSP), и на сервере (recordMultiple/record/unreserve в MovementAction отклоняют запрос с ошибкой), если:
Текущий статус процесса не входит в inventoru:processType.<TYPE_ID>.reserveStatuses для его типа (см. В каких статусах можно резервировать материалы), или
По процессу уже есть успешно отправленный в 1С экспорт списания — независимо от текущего статуса (откат статуса процесса назад не открывает форму сам по себе, см. sync1c).
Вкладка называет причину, а не просто прячет кнопки: «списание уже проведено» или «в этом статусе материалы не изменяются». Те же слова возвращают и отказы сервера — раньше все три говорили «процесс уже выгружен в 1С» независимо от того, какая из двух причин сработала.
Списание в ERP появляется только после того, как 1С приняла экспорт (см. sync1c), поэтому у завершённого процесса резерв может стоять дальше — и раньше вкладка об этом молчала, а ответ лежал на админском экране, куда кладовщик не ходит. Теперь вкладка говорит, когда резерв кого-то ждёт:
запись очереди экспорта ждёт, а обмен с 1С выключен — сама не отправится;
попытки отправки исчерпаны — автоматически больше не пойдёт;
1С списание приняла, а в ERP оно не проведено (статус unposted) — резерв висит так же; вкладка в этом случае и закрыта отдельными словами («1С приняла списание по процессу»), а не «списание уже проведено»: иначе она сама себе противоречила бы. Закрывается пунктом «Провести в ERP»;
запись просто стоит в очереди — тогда сказано спокойно, без красного.
В каждом случае названы номер записи и инстанс, чтобы их можно было продиктовать тому, кто ведёт обмен; у кого есть право на очередь экспорта, тому же показывается ссылка — она открывает очередь отдельным окном, отфильтрованной по этому процессу, и карточка процесса остаётся на месте.
Спокойный случай («запись просто стоит в очереди, обмен включён») и выглядит спокойно — остальные три выделены красным.
Отдельно названа тишина другого рода: если у типа процесса не задан статус списания, запись не появится никогда — вкладка говорит и это, и что резерв снимается только руками. Единственным следом раньше была строка в логе сервера. Сказано именно про ненастроенный тип, а не про «записи пока нет»: процесс в любом другом нерабочем статусе ещё может дойти до списания, и утверждать обратное значило бы уверенно соврать.
Доступный остаток проверяется на сервере при каждом резерве, причём при мульти-сабмите остаток «вычитается» локально между строками — две строки одной позиции не пройдут проверку по одному и тому же устаревшему значению.
Позиции с поштучным учётом резервируются по одной единице, и единица называется сразу: списание при выгрузке в 1С создаётся позже, по статусу процесса, и берёт ровно ту единицу, которую назвал резерв. Под полем серийного номера — список свободных единиц выбранной позиции на выбранном складе (числятся там и не зарезервированы ни одним процессом) и их число; серийник выбирается из списка, вводится или сканируется. Резерв единицы, которой нет среди свободных, отклоняется ещё в форме, а сервер проверяет то же самое заново — и в самой вкладке, и в журнале движений, кто бы резерв ни создавал: единица существует, одна со своим серийником, числится на этом складе по этой позиции и не зарезервирована другим процессом. Раньше проверялось только, что серийник не пустой, и опечатка всплывала только на списании в 1С — когда исправлять её уже некому.
Отчёт в плагине report (/user/plugin/report/plugin/inventoru/stock, класс StockOverviewReportAction): суммарные количество/резерв/доступно и число складов по каждой позиции по всем складам компании. Намеренно обходит per-store ACL — право на отчёт выдавать только ролям, которым положено видеть всё (руководство), не монтажникам.
src/org/bgerp/plugin/inventoru/
├── Plugin.java — регистрация плагина (ID = inventoru)
├── Config.java — inventoru:processType.<ID>.reserveStatuses и др.
├── db.sql — схема БД + миграция inventoru_* → inventoru_*
├── l10n.xml — локализация (ru/en)
├── action.xml — права доступа
├── action/
│ ├── DeviceAction — /user/plugin/inventoru/device: очередь, карточка, правка,
│ │ удаление, восстановление, справочники производителей/моделей
│ ├── DeviceAdminAction — /admin/plugin/inventoru/device: только загрузка из файла
│ ├── ItemAction — /user/plugin/inventoru/item (админского экрана нет)
│ ├── StoreAction — /user/plugin/inventoru/store (+ my)
│ ├── StoreAdminAction — /admin/plugin/inventoru/store: выданный доступ (пользователю
│ │ или группе), группа редактирования, минимальный остаток
│ ├── StoreStatusAction — /admin/plugin/inventoru/storeStatus: каталог статусов
│ ├── MovementAction — /user/plugin/inventoru/movement
│ │ (processTab, record, recordMultiple, unreserve, transfer,
│ │ returnStock, resolveCode)
│ ├── MovementAdminAction — /admin/plugin/inventoru/movement: журнал очередью
│ │ (QueueScreenAction) + receipt, adjust, writeoffUnit
│ ├── StockOverviewReportAction — отчёт остатков (плагин report)
│ ├── ManufacturerAdminAction, DeviceTypeAdminAction — справочники
│ ├── QueueAdminAction — очереди всех сущностей (сущность выбирается при создании)
│ ├── QueueScreenAction — общий экран очереди, от него наследуются Store/Item/Device
│ └── StoreTypeAction, ItemTypeAction, EquipTypeAction, ManufacturerTypeAction,
│ ModelTypeAction — иерархии типов + свойства
├── dao/ — DAO на каждую модель + BarcodeDAO, MinLevelDAO,
│ ChangeLogDAO, queue/QueueDAO
├── model/ — Device/Item/Store/Movement/StoreAccess + *Type + *TypeProperties + queue/*
├── cache/ — StoreTypeCache (typeMap+statusMap+statusList), ItemTypeCache,
│ EquipTypeCache, QueueCache
├── event/ — ProcessRemovedListener (снятие резервов удалённого процесса)
└── sync1c/ — интеграция с 1С, см. отдельный документ
webapps/WEB-INF/jspf/user/plugin/inventoru/
├── menu_items.jsp, process_tabs.jsp — точки расширения, регистрируются в Plugin.endpoints()
├── queue/ — list, filter, show, back_button — общий экран очереди
│ для всех сущностей (см. QueueScreenAction)
├── device/ — card, edit, toolbar + type_list, manufacturer_list
├── item/ — card, edit, toolbar
├── movement/ — process_tab (вкладка «Materials» на процессе); сам журнал
│ рисует общий экран очереди
├── store/ — card, edit, toolbar, my («My warehouse»)
├── sync1c/import_pending.jsp — «Incoming» (sync1c, но лежит в основном плагине)
└── report/stock_overview.jsp — отчёт «Остатки по всем складам»
webapps/WEB-INF/jspf/admin/plugin/inventoru/ — админки: типы, справочники, очереди,
доступ к складу, импорт оборудования
webapps/js/pl.inventoru.js — камера-скан ($$.inventoru.scan/scanResolve)
| Таблица | Назначение |
|---|---|
|
|
Производители устройств |
|
|
Модели устройств (шаблон) |
|
|
Экземпляры устройств |
|
|
Файлы/фото устройств (бинарные данные — в ядровом |
|
|
Номенклатура ТМЦ |
|
|
Штрихкоды номенклатуры (barcode → item_id), см. Штрихкоды и сканирование |
|
|
Иерархии типов (id, title, parent_id, use_parent_props, data, config) |
|
|
Глобальный каталог статусов складов (см. В каких статусах можно резервировать материалы про семантику per-type) |
|
|
Склады; |
|
|
Выданный доступ к складу на период (store_id, user_id ИЛИ group_id, date_from, date_to) — строка адресована либо пользователю, либо группе |
|
|
Иерархии типов производителей и моделей |
|
|
Очереди всех сущностей плагина; колонка |
|
|
Кому выдана очередь; эффективный набор пользователя — объединение личных привязок и привязок его групп (см. Очереди) |
|
|
Текущие остатки: |
|
|
Append-only журнал движений — записи никогда не обновляются и не удаляются; |
|
|
Минимальные остатки (порог подсветки «заканчивается») |
|
|
Аудит-лог изменений (pre/post JSON snapshots) |
Soft-delete (deleted_at/deleted_by) — у Device/Item/Store.
StoreType/ItemType/EquipType — TreeItem, свойства (param.ids, для складов ещё status.ids, для номенклатуры — accounting.kind) сериализуются в поле data в формате key=value построчно (Preferences/Utils.toIntegerList), парсятся в *TypeProperties. Редактор «Properties» типа берёт список доступных для выбора параметров через ParamDAO.getParameterList(<Model>.OBJECT_TYPE, 0) — обязательно использовать OBJECT_TYPE соответствующей модели (Store/Item/Device), не Process.OBJECT_TYPE.
getChildCount() считается subquery в DAO (наивная загрузка без детей всегда даёт 0).
Наследование свойств (use_parent_props=1) — type.getProperties() возвращает СОБСТВЕННЫЕ свойства типа, пустые, если тип помечен наследовать от родителя. Разрешать наследование — <TypeName>Cache.getEffectiveProperties(typeId) (по одному статическому методу в каждом из StoreTypeCache/ItemTypeCache/EquipTypeCache): поднимается по parentId через typeMap, пока не найдёт предка с useParentProperties=false, или пока не упрётся в корень. Все реальные потребители параметров типа (показ параметров на карточке устройства/номенклатуры/склада, фильтр asset-позиций) обязаны звать getEffectiveProperties, а не type.getProperties() напрямую — иначе у типа с use_parent_props=1 список параметров молча окажется пустым.
Параметры сущностей плагина заводятся штатно, через Администрирование → Параметры: экран DirectoryAction берёт справочники ядра и добавляет по разделу на каждую сущность, объявленную включённым плагином в Plugin.getObjectTypes(). Инвентарь объявляет пять — склад, номенклатура, оборудование, производитель, модель оборудования, — и заголовки разделов приходят из его же локализатора. Редактор тот же, что у параметров процессов, ParameterCache сбрасывается самим действием.
|
До версии от 05.09.2026 точки расширения не было, и параметр этих сущностей заводился только прямым |
Привязка параметра к типу — в редакторе свойств типа (тот же механизм для всех иерархий):
POST /admin/plugin/inventoru/itemType.do?method=propertiesUpdate&id=1¶m=301
POST /admin/plugin/inventoru/storeType.do?method=propertiesUpdate&id=1¶m=302
POST /admin/plugin/inventoru/equipType.do?method=propertiesUpdate&id=1¶m=303
Множественный параметр param — можно несколько раз в query для нескольких id. После этого inventoru_item_type.data содержит param.ids=301, и карточка позиции этого типа начинает показывать поле. Позиция без типа не показывает ни одного параметра, даже если значение записано в базе.
grep "MovementAction\|MovementDAO" log/bgerp.log
grep "StoreTypeAction\|ItemTypeAction\|EquipTypeAction" log/bgerp.log
-- Баланс по складу
SELECT i.title, b.quantity, b.reserved FROM inventoru_balance b
JOIN inventoru_item i ON i.id=b.item_id WHERE b.store_id=?;
-- История движений процесса (включая списанные/возвращённые)
SELECT id, type, item_id, quantity, ref_movement_id, ext_ref, dt
FROM inventoru_movement WHERE process_id=? ORDER BY id;
-- Доступ монтажника к складам на сегодня
SELECT store_id FROM inventoru_store_access
WHERE user_id=? AND date_from<=CURDATE() AND (date_to IS NULL OR date_to>=CURDATE());
-- Штрихкод → позиция
SELECT b.barcode, i.title FROM inventoru_item_barcode b
JOIN inventoru_item i ON i.id=b.item_id WHERE b.barcode=?;