-
Kernel
-
Plugins
-
Billing
-
Collaboration
-
Planning
-
Messaging
-
Security
-
Service
-
Telecom [RU]
-
Release Notes
Kernel
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, deviceTypeId: устройство без наименования и модели ничем не идентифицируется. Пустое значение отключает проверку.
Допустимые имена (как у полей формы): 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.
Два независимых, дополняющих друг друга механизма ограничивают, какие склады видит и может редактировать пользователь (во вкладке «Materials» процесса, на складских формах, в «Моём складе», при загрузке CSV/OData-импорте из 1С и в «Ожидающих поступлениях»):
| Механизм | Когда использовать |
|---|---|
|
|
Точечный, персональный доступ одного пользователя к одному складу на период — типовой случай: монтажник и его личный склад-«рюкзак». Управляется на карточке склада, кнопка Доступ (пользовательский экран Инвентарь → Склады, а не админка). |
|
|
Роль «кладовщик» — несколько человек одновременно управляют ОДНИМ складом (принимают/списывают/ редактируют), не обязательно являясь его персональным владельцем и не заводя каждому отдельную запись в |
Итоговый доступ пользователя к складу = владелец (store.user_id) ∪ активная запись store_access ∪ членство в группе edit_group_id этого склада. Проверяется одинаково на сервере в StoreAction/MovementAction/UserImportPendingAction/AdminSyncAction — единая логика getAccessRestriction()/getAccessibleStoreIds()/hasStoreAccess() в каждом из них, независимо от выданных прав на сами действия.
Если у пользователя нет маркерного права админ-уровня — /admin/plugin/inventoru/store:accessList (в дереве прав это Administration → Warehouses → Executor access → List) или, для sync1c-действий, /admin/plugin/inventoru/sync1c/instance:null — видны только склады, попадающие в объединение выше (может быть пустым множеством — тогда не видно ни одного склада, а не все).
Если такое право есть — считается «админ-режимом», видны все склады без ограничения. Оно же снимает ограничение и с оборудования: единица техники лежит на складе, доступ к ней определяется доступом к складу.
Право Executor access → List выглядит безобидно — «посмотреть список доступов» — но именно оно и есть маркер «видит и правит ВСЕ склады». Кладовщику его выдавать нельзя. Раньше маркером был /admin/plugin/inventoru/store:null, узел удалённого админского списка складов; в старых наборах прав его можно снимать, он больше ни на что не влияет. |
Administration → Permission Sets — создать набор прав со scoped-разрешениями (НЕ полный админский CRUD): /user/plugin/inventoru/store:{null,show,my,queueFilter,queueShow}, /user/plugin/inventoru/import_pending:{null,apply,applyAll,reject}, и если нужна загрузка CSV — отдельно /admin/plugin/inventoru/sync1c/instance:{importFileForm,importFilePreview,importFileConfirm} (эти три права независимы от остальной админки sync1c, см. sync1c). Важно НЕ включать маркерные права /admin/plugin/inventoru/store:accessList или /admin/plugin/inventoru/sync1c/instance:null — они превращают пользователя в «видит вообще все склады».
Administration → Users → Groups — создать группу, включить в неё нужных сотрудников.
Привязать набор прав из шага 1 к группе из шага 2 (там же, в редакторе группы).
Инвентарь → Склады — открыть карточку нужного склада, кнопка Доступ, выбрать эту группу в поле «Группа редактирования».
После этого все участники группы видят склад в обычном пользовательском меню (не в админке), работают с его остатками, грузят туда CSV с остатками из 1С и подтверждают/отклоняют поступления — независимо друг от друга, без необходимости заводить каждому отдельную запись store_access.
Кладовщик складом пользуется, а настраивает его администратор. Права store:{create,edit,update,delete} — админские, в набор кладовщика они не входят: иначе он заведёт склад на чужое имя или удалит тот, к которому у него есть доступ. По той же причине не входят item:{create,update,delete} — номенклатура общая для всей компании, и удаление позиции задевает всех; кладовщику достаточно item:{null,show,queueFilter,queueShow}, чтобы видеть позицию и её параметры. По оборудованию кладовщику нужны device:{null,show,update, queueFilter,queueShow} — заводить и удалять единицы техники тоже дело администратора. |
Поле «Группа редактирования» при этом остаётся осмысленным и без права на правку карточки: оно определяет доступ — какие склады сотрудник вообще видит и с остатками каких работает (StoreAccessService), а не право менять их настройки.
Очередь — настраиваемый список сущности: складов, номенклатуры, оборудования, производителей, моделей оборудования или движений ТМЦ. Заводится в оснастке Администрирование / Инвентарь / Очереди, там же выбирается сущность, которую очередь показывает. Конфигурация устроена так же, как конфигурация очереди процессов.
Кому доступна очередь, задаётся не конфигурацией, а в самой форме очереди — списками Группы пользователей и Пользователи. Действует объединение: сотрудник видит очередь, если она выдана лично ему или любой из его групп. Так же выдаются очереди процессов в ядре (user_queue и user_group_queue), с той разницей, что там это делается в карточке пользователя и группы, а здесь — в очереди. Очередь, не выданная никому, не показывается никому.
column.<ID>.value=<ЧТО ПОКАЗЫВАТЬ> column.<ID>.title=<ЗАГОЛОВОК>
Колонка, названная внешним ключом (item_id, store_id, manufacturer_id, user_id), показывает не код, а наименование того, на что ключ указывает. Колонка type у движений и status у оборудования показывает состояние словом.
Значения column.<ID>.value зависят от сущности очереди:
| Сущность | Значения |
|---|---|
|
Склады |
|
|
Номенклатура |
|
|
Оборудование |
|
|
Производители |
|
|
Модели оборудования |
|
|
Движения ТМЦ |
|
Мягкого удаления нет у производителей, моделей и движений — колонки deleted у них не бывает. Движения к тому же не имеют собственных параметров, поэтому и param:<ID> для них недоступен: такая колонка в конфигурации пропускается с ошибкой в лог.
filter.<ID>.type=<ТИП> filter.<ID>.title=<ПОДПИСЬ> # скрытый фильтр: не предлагается в списке, но применяется #filter.<ID>.show=0
Типы фильтров: title — у всех; deleted — у сущностей с мягким удалением (склады, номенклатура, оборудование); param:<ID параметра> — у всех, кроме движений ТМЦ, у которых нет своих параметров; для складов ещё status, для оборудования — equipType (выбор типов оборудования, тот самый фильтр, что раньше был единственным фильтром админского списка). Для сущностей, живущих на складе, — store; для движений ТМЦ — type (операция) и dt (диапазон дат операции). Фильтр-диапазон по дате называется именем своего поля и читает <поле>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 записывает — строки с ошибкой пропускаются, остальные применяются.
Ключ строки — серийный номер. Единица с таким серийником уже есть — обновляется, нет — создаётся. Поэтому повторная загрузка того же файла ничего не задваивает.
Заголовок обязателен, порядок колонок не важен, лишние игнорируются. Имена ищутся по нескольким принятым вариантам, поэтому типовая выгрузка грузится без настройки:
| Что | Принимаемые имена | Как разбирается |
|---|---|---|
|
Серийный номер (обязательна) |
|
Ключ строки. Строка с пустым серийником пропускается — так не спотыкается хвост файла |
|
Модель |
|
Нет такой — создаётся |
|
Производитель |
|
Нет такого — создаётся |
|
Тип оборудования |
|
Ищется по названию среди существующих; не найден — ошибка строки |
|
Склад |
|
Ищется по названию; не найден — ошибка строки |
|
Номенклатура |
|
Ищется по названию; не найдена — ошибка строки |
|
MAC-адрес |
|
Как есть |
|
Инвентарный номер |
|
Как есть |
|
Статус |
|
Только |
|
Наименование |
|
Как есть |
Разница между «создаётся» и «ошибка строки» намеренная: каталог моделей и производителей импорт как раз и должен наполнять, а склад, тип оборудования и номенклатура — это учётные сущности со своими настройками, и молча заводить их по строке файла нельзя.
Разделитель и кодировка определяются по содержимому — тот же разбор, что у импорта остатков (см. sync1c): UTF-8 и windows-1251, разделитель ;, , или табуляция, BOM снимается.
Пример файла — example_devices.csv рядом с этим документом.
Список учитываемых позиций ТМЦ (кабель, патч-корды, SFP и т.д.) — очередь с настраиваемыми колонками, фильтрами и сортировкой (см. Очереди). У каждой позиции: наименование, характеристика, тип номенклатуры, внешний ID (для интеграции с 1С — хранится как text-параметр, id параметра задаётся в настройках инстанса 1С), штрихкоды (см. Штрихкоды и сканирование).
Карточка позиции открывается из контекстного меню строки и показывает тип, характеристику и параметры, которые приносит с собой тип. Оттуда же позиция правится, удаляется (мягко) и восстанавливается.
Тип номенклатуры — обязательная часть формы: именно он даёт позиции набор параметров и вид учёта (Вид учёта: расходники и «числится за складом»). Позиция без типа не имеет ни одного параметра — значение может быть записано в базе (например, код 1С, проставленный синхронизацией) и всё равно не показываться на карточке. Позициям, которые заводит импорт остатков, тип назначается настройкой коннектора import.itemTypeId — см. sync1c. |
У типа номенклатуры задаётся accounting.kind (ItemTypeProperties): consumable (по умолчанию) — расходники, резервируются и списываются по процессу; asset — инструмент/оборудование, выданное надолго. Asset-позиции не попадают в форму резервирования вкладки «Materials» — они перемещаются только через перемещение/возврат на карточке склада, и показываются там отдельным блоком «Held by warehouse».
Склад может быть:
General warehouse — без конкретного владельца
Личный склад (монтажника) — закреплён за конкретным сотрудником, обычно создаётся автоматически при первом импорте остатков с 1С (см. sync1c)
Карточка склада показывает остатки (расходники и asset-позиции раздельно), историю движений, формы перемещения/возврата, параметры склада и кнопки его администрирования: Редактировать, Удалить (мягкое, отменяется кнопкой Восстановить) и Доступ — доступ исполнителей и группа редактирования («кладовщик», см. Доступ к складам — монтажники и «кладовщики»). Там же, у каждой позиции остатков, задаётся минимальный остаток (inventoru_min_level) — порог подсветки «заканчивается»; отсутствие записи = порог не задан. Кнопки видны по правам: правка и удаление — пользовательские узлы склада, доступ и минимальный остаток — админские.
Мобильный экран монтажника (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 журнал операций по складу — движения никогда не изменяются и не удаляются, только добавляются.
| Приход (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).
Доступный остаток проверяется на сервере при каждом резерве, причём при мульти-сабмите остаток «вычитается» локально между строками — две строки одной позиции не пройдут проверку по одному и тому же устаревшему значению.
Отчёт в плагине 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 (просмотр, receipt, adjust)
│ ├── 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/ — list, 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, 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=?;