src/org/bgerp/plugin/inventory/sync1c/
├── Config.java — sync1c:enable, processTypeIds, processType.<id>.statusWriteoff, export.maxAttempts
├── action/
│ ├── AdminSyncAction.java — /admin/plugin/sync1c/instance (инстансы, маппинг, импорт/экспорт для
│ │ обоих протоколов); importFileForm/importFilePreview/importFileConfirm
│ │ (протокол file) + getAccessRestriction() — та же схема доступа
│ │ (store_access ∪ владелец ∪ edit_group_id), маркер "полный админ" —
│ │ владение /admin/plugin/sync1c/instance:null
│ └── UserImportPendingAction.java — /user/plugin/inventory/import_pending; getAccessibleStoreIds()
│ (владелец ∪ store_access ∪ edit_group_id) вместо жёсткой привязки
│ к EngineerWarehouse.userId
├── dao/
│ ├── InstanceDAO, EngineerWarehouseDAO, SyncLogDAO, ExportLogDAO
│ ├── ImportPendingDAO — upsert/get/getListForStores/getListForInstance/apply/applyAll/reject/
│ │ getKnownItemIds; apply() дополнительно пишет audit-строку в
│ │ inventory_movement (type=adjustment), см. <<usage-import>>
│ └── ExportQueueDAO — add/get/getPendingByInstance/getPendingByProtocol/isPending/finalizePendingStatus/hasSuccessExport/cancelPendingByProcessId/markReversed
├── event/
│ └── Sync1cExportListener.java — ProcessChangedEvent → enqueue() при входе в writeoff-статус, reverseWriteoffMovements() при выходе с success-экспортом
├── exec/
│ ├── ImportPoller.java — не используется по расписанию сейчас, импорт триггерится вручную
│ └── ExportPoller.java — scheduler task, раз в минуту, только protocol='hs' (getPendingByProtocol)
├── service/
│ ├── Api1cClient.java — HTTP-клиент (/stocks, /writeoff), Basic Auth — только протокол hs
│ ├── ImportService.java — syncEngineerWarehouse() (hs, вызывает Api1cClient) и importFromRows() (протокол-агностичное ядро: upsert item/store, import_pending) — оба протокола делегируют сюда
│ ├── ExportService.java — enqueue/sendExportQueue/buildWriteoffItems (переиспользуется CSV-экспортом)/createWriteoffMovements/reverseWriteoffMovements
│ └── CsvUtil.java — минимальный RFC4180 parse/write (UTF-8 + BOM), протокол file
└── model/
├── Sync1cConnector — + protocol (PROTOCOL_HS/PROTOCOL_FILE, "epf" зарезервирован), EngineerWarehouse, ImportLog
├── ImportPending, ExportQueue (STATUS_PENDING/SUCCESS/FAILED/CANCELLED/REVERSED), ExportLog
└── Api1cStock — @JsonIgnoreProperties(ignoreUnknown=true), маппинг ответа /stocks (и CSV-строк для протокола file)
webapps/WEB-INF/jspf/user/plugin/inventory/
├── import_pending.jsp — «Ожидающие поступления» (доступ по store_access/владелец/edit_group_id)
└── store_card.jsp — кнопка "Загрузить CSV" в разделе "Остатки на складе" (лежит в
основном плагине, не в sync1c, ссылается на форму ниже с storeId+userTier)
webapps/WEB-INF/jspf/admin/plugin/sync1c/
├── instance_list.jsp, instance_edit.jsp (+ поле protocol)
├── mapping_list.jsp (импорт-пункт меню зависит от protocol), mapping_edit.jsp — кнопки загрузки CSV
│ здесь больше нет, см. <<setup-protocol-file>> про новые точки входа
├── import_file_upload.jsp — файл→склад→превью→подтверждение (протокол file); параметр
│ storeId — предвыбор склада, userTier — куда вести после
│ подтверждения (user-tier "Ожидающие поступления" или admin)
├── import_pending.jsp, import_log.jsp
├── export_queue.jsp — + "Скачать CSV"/"Подтвердить"/"Подтвердить всё" для протокола file
└── export_log.jsp — история попыток экспорта (оба протокола), см. <<dev-db>>
Кнопка "Загрузить CSV" продублирована также на admin/plugin/inventory/store/balance.jsp и
admin/plugin/inventory/movement/list.jsp (основной плагин, не sync1c) — три равнозначные точки
входа на одну и ту же форму, см. <<setup-protocol-file>>.