901 lines
48 KiB
TypeScript
901 lines
48 KiB
TypeScript
import { markRaw } from 'vue'
|
||
import type { DesktopWalletCard } from 'src/shared/lib/types/desktop-wallet'
|
||
import { MarketplaceCatalogPage } from 'src/pages/Marketplace/MarketplaceCatalog'
|
||
import { MarketplaceOfferDetailPage } from 'src/pages/Marketplace/MarketplaceOfferDetail'
|
||
import { CartPage } from 'src/pages/Marketplace/Cart'
|
||
import { OrderConfirmationPage } from 'src/pages/Marketplace/OrderConfirmation'
|
||
import { CreateMarketplaceOfferPage } from 'src/pages/Marketplace/CreateMarketplaceOffer'
|
||
import { MyOrdersPage } from 'src/pages/Marketplace/MyOrders'
|
||
import { OperatorTrustedPersonsPage } from 'src/pages/Marketplace/OperatorTrustedPersons'
|
||
import { OperatorBranchEconomyPage } from 'src/pages/Marketplace/OperatorBranchEconomy'
|
||
import { AdminMarketEconomyPage } from 'src/pages/Marketplace/AdminMarketEconomy'
|
||
import { OperatorIssuancePage } from 'src/pages/Marketplace/OperatorIssuance'
|
||
import { OrdererOrderDetailPage } from 'src/pages/Marketplace/OrdererOrderDetail'
|
||
import { OrdererReceiveCodePage } from 'src/pages/Marketplace/OrdererReceiveCode'
|
||
import { OrdererReturnClaimsPage } from 'src/pages/Marketplace/OrdererReturnClaims'
|
||
import { OperatorReturnClaimsPage } from 'src/pages/Marketplace/OperatorReturnClaims'
|
||
import { OperatorReceptionPage } from 'src/pages/Marketplace/OperatorReception'
|
||
import { OperatorInventoryLabelingPage } from 'src/pages/Marketplace/OperatorInventoryLabeling'
|
||
import { OffererPendingAplReceptionsPage } from 'src/pages/Marketplace/OffererPendingAplReceptions'
|
||
import { OffererSupplyPreparationPage } from 'src/pages/Marketplace/OffererSupplyPreparation'
|
||
import { OffererShipPartyPage } from 'src/pages/Marketplace/OffererShipParty'
|
||
import { OffererPaymentHistoryPage } from 'src/pages/Marketplace/OffererPaymentHistory'
|
||
import { AdminWriteoffsPage } from 'src/pages/Marketplace/AdminWriteoffs'
|
||
import { PvzWriteoffsPage } from 'src/pages/Marketplace/PvzWriteoffs'
|
||
import { ChairmanModerationPage } from 'src/pages/Marketplace/ChairmanModeration'
|
||
import { AdminOrdersPage } from 'src/pages/Marketplace/AdminOrders'
|
||
import { AdminOffersPage } from 'src/pages/Marketplace/AdminOffers'
|
||
import { AdminIssuancePointsPage } from 'src/pages/Marketplace/AdminIssuancePoints'
|
||
import { OperatorOwnWarehousePage } from 'src/pages/Marketplace/OperatorOwnWarehouse'
|
||
import { AdminWarehouseSummaryPage } from 'src/pages/Marketplace/AdminWarehouseSummary'
|
||
import { EcosystemRegistryPage } from 'src/pages/Marketplace/EcosystemRegistry'
|
||
import { OnboardingCoopAcceptCppPage } from 'src/pages/Marketplace/OnboardingCoopAcceptCpp'
|
||
import { OrdererConsolidatedPage } from 'src/pages/Marketplace/OrdererConsolidated'
|
||
import { OffererIncomingOrdersPage } from 'src/pages/Marketplace/OffererIncomingOrders'
|
||
import { OffererMyOffersPage } from 'src/pages/Marketplace/OffererMyOffers'
|
||
import { ChairmanCategoryWhitelistPage } from 'src/pages/Marketplace/ChairmanCategoryWhitelist'
|
||
import { BoardPayoutsReadonlyPage } from 'src/pages/Marketplace/BoardPayoutsReadonly'
|
||
import { OnboardingMemberPickCppPage } from 'src/pages/Marketplace/OnboardingMemberPickCpp'
|
||
import { SupplierOnboardingPage } from 'src/pages/Marketplace/SupplierOnboarding'
|
||
import { SupplierRegistryPage } from 'src/pages/Marketplace/SupplierRegistry'
|
||
import type { IWorkspaceConfig } from 'src/shared/lib/types/workspace'
|
||
import { agreementsBase } from 'src/shared/lib/consts/workspaces'
|
||
import { registerGlobalOverlay } from 'src/shared/lib/overlays'
|
||
import { registerRealtimeSubscription } from 'src/shared/lib/realtime'
|
||
import {
|
||
OnsiteSignatureGateOverlay,
|
||
createMarketplaceEventsSubscription,
|
||
} from 'src/widgets/Marketplace/OnsiteSignatureGate'
|
||
import { registerMarketplaceProcessInfoHandlers } from './app/extensions'
|
||
|
||
/**
|
||
* Расширение «Стол заказов» предоставляет ЧЕТЫРЕ отдельных рабочих стола
|
||
* (workspace), распределённых по ролям пайщика:
|
||
*
|
||
* 1. `market` — Стол заказчика
|
||
* 2. `market-supplier` — Стол поставщика
|
||
* 3. `market-pvz` — Стол ПВЗ
|
||
* 4. `market-admin` — Стол администратора
|
||
*
|
||
* ВАЖНО: каждый из этих `name` ОБЯЗАН быть объявлен в backend-реестре
|
||
* `components/controller/src/extensions/extensions.registry.ts`
|
||
* (`AppRegistry['market'].desktops`). Маршруты ниже привязываются к workspace
|
||
* по `name` через `DesktopStore.setRoutes`; если backend не объявил workspace,
|
||
* его маршруты молча теряются (см. desktop.interactor + init-installed-extensions).
|
||
*
|
||
* ── Канон авторизации столов (grants) ──────────────────────────────────────
|
||
* Видимость столов и страниц НЕ задаётся здесь ролями. Каждый маршрут объявляет
|
||
* требуемое право `meta.requires` (capability вида «Resource:action» из
|
||
* marketplace access-matrix). Backend (`MarketplaceDesktopGrantsProvider`)
|
||
* выдаёт текущему пользователю набор прав (`DesktopWorkspace.grants`), а фронт
|
||
* показывает стол/страницу, только если `requires` входит в этот набор
|
||
* (DesktopStore.isPageVisible / isWorkspaceVisible). Настоящий enforcement —
|
||
* на резолверах бэкенда; здесь — UI-видимость + навигационный гард.
|
||
*
|
||
* Соответствие столов правам (как воспроизведена прежняя видимость по ролям):
|
||
* - market → `Order:create` (orderer-эксклюзивный грант,
|
||
* НЕ выдаётся admin/board — иначе председатель видел бы рабочие страницы
|
||
* заказчика до подписи своей персональной оферты, см. ниже)
|
||
* - market-supplier → `Offer:create:own` (offerer-эксклюзивный грант;
|
||
* НЕ `Offer:read` — это право есть и у orderer, и у admin (каталог/модерация),
|
||
* поэтому им нельзя гейтить рабочие страницы поставщика — председатель без
|
||
* одобренного допуска видел бы «Мои предложения» и т.д., см. ниже)
|
||
* - market-pvz → `Warehouse:read:own-KU` (оператор КУ + админ)
|
||
* - market-admin → `Order:read:all` (совет + председатель),
|
||
* кроме `Whitelist:manage` (только председатель) и `Extension:configure`
|
||
* (страница подключения ЦПП — единственное право до принятия ЦПП).
|
||
*
|
||
* Онбординг до принятия ЦПП обеспечивается грантами: пока совет не принял ЦПП,
|
||
* у председателя только `Extension:configure` → виден лишь Стол администратора
|
||
* со страницей подключения; у остальных прав нет → столы скрыты. Никакого
|
||
* отдельного гейта во фронте не требуется — всё из backend grants.
|
||
*/
|
||
export default async function (): Promise<IWorkspaceConfig[]> {
|
||
registerMarketplaceProcessInfoHandlers()
|
||
|
||
// Глобальный гейт подписи на месте (Фаза 1) — расширение само вкладывает свой
|
||
// оверлей в универсальный реестр ядра, как и process-info-хендлеры выше. App
|
||
// его не импортирует; он сам решает свою видимость (поставщик очно / заказчик
|
||
// на получении).
|
||
registerGlobalOverlay('marketplace:onsite-signature-gate', OnsiteSignatureGateOverlay)
|
||
|
||
// Фаза 2: расширение вкладывает свою realtime-подписку в канал ядра (как и
|
||
// оверлей выше). Ядро откроет её при авторизации и будет дёргать catch-up;
|
||
// события персонального канала пайщика обновляют гейт без поллинга.
|
||
registerRealtimeSubscription(createMarketplaceEventsSubscription())
|
||
|
||
return [
|
||
// ───────────────────────── Стол заказчика ─────────────────────────
|
||
{
|
||
workspace: 'market',
|
||
extension_name: 'market',
|
||
title: 'Стол заказчика',
|
||
icon: 'fa-solid fa-cart-shopping',
|
||
defaultRoute: 'marketplace-catalog',
|
||
routes: [
|
||
{
|
||
meta: {
|
||
title: 'Стол заказчика',
|
||
icon: 'fa-solid fa-cart-shopping',
|
||
},
|
||
path: '/:coopname/market',
|
||
name: 'market',
|
||
children: [
|
||
{
|
||
// Эпик 1 / Story 1.4 + 1.11: L3 онбординг пайщика — gate ЦПП
|
||
// «Стол заказов». Объявлен ПЕРВЫМ и требует выделенного права
|
||
// `Onboarding:orderer`, которое backend выдаёт только пока пайщик
|
||
// не подписал персональную оферту (requires_gate=true). Тогда это
|
||
// единственная видимая страница стола заказчика — рабочие страницы
|
||
// (Каталог и т.д.) требуют orderer-прав, которых до подписи нет.
|
||
// После подписи маркер исчезает, страница авто-скрывается, а
|
||
// дефолт стола (Каталог) и остальные пункты открываются.
|
||
path: 'onboarding/member-cpp',
|
||
name: 'marketplace-onboarding-member-cpp',
|
||
component: markRaw(OnboardingMemberPickCppPage),
|
||
meta: {
|
||
title: 'Подключение к Столу заказов',
|
||
icon: 'fa-solid fa-handshake-angle',
|
||
requires: 'Onboarding:orderer',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
path: 'catalog',
|
||
name: 'marketplace-catalog',
|
||
component: markRaw(MarketplaceCatalogPage),
|
||
meta: {
|
||
title: 'Каталог',
|
||
icon: 'fa-solid fa-store',
|
||
// Рабочие страницы стола заказчика гейтятся подпиской оферты
|
||
// ЦПП: требуем orderer-эксклюзивный грант `Order:create`. Его
|
||
// НЕ выдают роли admin/board, и `Order:read:all` в него НЕ
|
||
// разворачивается (нет `:all`-формы) — поэтому admin-гранты
|
||
// председателя (или совета) не «протекают» на стол заказчика
|
||
// до его персональной подписи. Реальный enforcement резолверов
|
||
// использует узкие права (`Offer:read`/`Order:read:own`) как
|
||
// прежде; здесь — только видимость стола.
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 15: страница полного описания предложения. Скрыта из меню
|
||
// (hidden) — открывается по клику на карточку в каталоге. Несёт
|
||
// полное описание, участки поставки, гарантию и галерею; карточка
|
||
// показывает лишь категорию + поставщика + краткое описание.
|
||
path: 'offer/:offerId',
|
||
name: 'marketplace-offer-detail',
|
||
component: markRaw(MarketplaceOfferDetailPage),
|
||
meta: {
|
||
title: 'Предложение',
|
||
icon: 'fa-solid fa-box',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
hidden: true,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 16 / Story 16.1-16.2: корзина заказчика и оформление.
|
||
// Точка оформления: позиции (одна корзина — один КУ), правка
|
||
// количества, «Оформить заказ» → заказ-агрегат под общим
|
||
// checkout_id на текущий КУ. Требует orderer-грант Order:create.
|
||
path: 'cart',
|
||
name: 'marketplace-cart',
|
||
component: markRaw(CartPage),
|
||
meta: {
|
||
title: 'Корзина',
|
||
icon: 'shopping_cart',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Финальный экран после оформления: сводка заказа-агрегата
|
||
// (созданные заказы + непрошедший остаток). Отдельная страница,
|
||
// чтобы не показывать итог в опустевшей корзине. Скрыт из меню —
|
||
// открывается редиректом после «Оформить заказ».
|
||
path: 'order-confirmation',
|
||
name: 'marketplace-order-confirmation',
|
||
component: markRaw(OrderConfirmationPage),
|
||
meta: {
|
||
title: 'Заказ оформлен',
|
||
icon: 'task_alt',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
hidden: true,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
path: 'my-orders',
|
||
name: 'marketplace-my-orders',
|
||
component: markRaw(MyOrdersPage),
|
||
meta: {
|
||
title: 'Мои заказы',
|
||
icon: 'fa-solid fa-cart-shopping',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 4 / Story 4.4: сводный обзор заказов пайщика, сгруппированных
|
||
// по партиям (cycle_id). Дополняет «Мои заказы» — там плоский список,
|
||
// здесь — партии time_based/volume_based/open_subscription с этапом
|
||
// партии и суммарной стоимостью. Канон OrderCard для отдельных заказов.
|
||
path: 'consolidated',
|
||
name: 'marketplace-consolidated',
|
||
component: markRaw(OrdererConsolidatedPage),
|
||
meta: {
|
||
title: 'Коллективный заказ',
|
||
icon: 'fa-solid fa-layer-group',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// «Получить заказ» — отдельный пункт меню с одним account-bound
|
||
// QR-кодом на всю страницу. Вынесен в меню (а не в действие шапки)
|
||
// намеренно: код должен быть очевидно findable, пайщику не нужно
|
||
// объяснять, где его искать на пункте выдачи.
|
||
path: 'receive-code',
|
||
name: 'marketplace-receive-code',
|
||
component: markRaw(OrdererReceiveCodePage),
|
||
meta: {
|
||
title: 'Получить заказ',
|
||
icon: 'fa-solid fa-qrcode',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Детальная страница заказа. Скрыта из меню (hidden) —
|
||
// открывается по клику на карточку в «Моих заказах». Управление
|
||
// (отмена, «Подписать и получить») живёт в самой карточке и здесь;
|
||
// отдельной страницы «Готово к получению» больше нет — статус
|
||
// READY_TO_RECEIVE стал обычным этапом общего списка заказов.
|
||
path: 'orders/:orderId',
|
||
name: 'marketplace-order-detail',
|
||
component: markRaw(OrdererOrderDetailPage),
|
||
meta: {
|
||
title: 'Заказ',
|
||
icon: 'fa-solid fa-receipt',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
hidden: true,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 7 / Story 7.1: orderer-стол гарантийных возвратов. Здесь
|
||
// пайщик подаёт заявление по выданному заказу в гарантийный срок,
|
||
// прилагает фото товара и подписывает заявление (registry_id=1104).
|
||
path: 'returns',
|
||
name: 'marketplace-returns',
|
||
component: markRaw(OrdererReturnClaimsPage),
|
||
meta: {
|
||
title: 'Гарантийные возвраты',
|
||
icon: 'fa-solid fa-rotate-left',
|
||
requires: 'Order:create',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
|
||
// ──────────────────────── Стол поставщика ─────────────────────────
|
||
{
|
||
workspace: 'market-supplier',
|
||
extension_name: 'market',
|
||
title: 'Стол поставщика',
|
||
icon: 'fa-solid fa-store',
|
||
defaultRoute: 'marketplace-my-offers',
|
||
routes: [
|
||
{
|
||
meta: {
|
||
title: 'Стол поставщика',
|
||
icon: 'fa-solid fa-store',
|
||
},
|
||
path: '/:coopname/market-supplier',
|
||
name: 'market-supplier',
|
||
children: [
|
||
{
|
||
// Онбординг поставщика: backend выдаёт `Onboarding:offerer`
|
||
// пайщику без одобренного допуска в реестре — тогда виден только
|
||
// этот экран (выбор модели + реквизиты договора + заявка). После
|
||
// одобрения offerer-роль даёт `Offer:create:own` и открывает стол.
|
||
// Зеркало L3-гейта заказчика (marketplace-onboarding-member-cpp).
|
||
path: 'onboarding',
|
||
name: 'marketplace-onboarding-supplier',
|
||
component: markRaw(SupplierOnboardingPage),
|
||
meta: {
|
||
title: 'Подключение к Столу поставщика',
|
||
icon: 'storefront',
|
||
requires: 'Onboarding:offerer',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 3 / Story 3.4: «Мои предложения» — поставщик видит все
|
||
// свои Offer'ы во всех 4 статусах (PENDING_MODERATION / ACTIVE /
|
||
// REJECTED / WITHDRAWN). Канон CatalogOfferCard, client-side
|
||
// фильтр + поиск, polling 30s (статус меняет модерация).
|
||
path: 'my-offers',
|
||
name: 'marketplace-my-offers',
|
||
component: markRaw(OffererMyOffersPage),
|
||
meta: {
|
||
title: 'Мои предложения',
|
||
icon: 'fa-solid fa-clipboard-list',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Скрыт из меню (hidden) — кнопка «Создать предложение» живёт по
|
||
// канону в правом верхнем углу шапки (телепорт через
|
||
// useHeaderActions со стола «Мои предложения»).
|
||
path: 'create-offer',
|
||
name: 'marketplace-create-offer',
|
||
component: markRaw(CreateMarketplaceOfferPage),
|
||
meta: {
|
||
title: 'Создать предложение',
|
||
icon: 'fa-solid fa-plus-circle',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
hidden: true,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Редактирование своего предложения. Скрыт из меню (hidden) —
|
||
// открывается из «Мои предложения» по кнопке «Редактировать».
|
||
path: 'edit-offer/:offerId',
|
||
name: 'marketplace-edit-offer',
|
||
component: markRaw(CreateMarketplaceOfferPage),
|
||
meta: {
|
||
title: 'Редактирование предложения',
|
||
icon: 'fa-solid fa-pen',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
hidden: true,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 4 / Story 4.5: «Входящие заказы» — поставщик видит заказы,
|
||
// по которым он supplier. Канон OrderCard с role='offerer'.
|
||
path: 'incoming-orders',
|
||
name: 'marketplace-incoming-orders',
|
||
component: markRaw(OffererIncomingOrdersPage),
|
||
meta: {
|
||
title: 'Входящие заказы',
|
||
icon: 'fa-solid fa-inbox',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 5 / Story 5.5: offerer-стол подготовки отгрузки. Поставщик
|
||
// видит сформированные сводные заказы (статус CONFIRMED → SHIPPING)
|
||
// и подтверждает готовность к отгрузке через `marketplaceMarkSupplyReady`.
|
||
path: 'supply-prep',
|
||
name: 'marketplace-supply-prep',
|
||
component: markRaw(OffererSupplyPreparationPage),
|
||
meta: {
|
||
title: 'Подготовка отгрузки',
|
||
icon: 'fa-solid fa-truck-ramp-box',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// «Отгрузить партию» — отдельный пункт меню с одним account-bound
|
||
// Pickup-QR на всю страницу. Тот же код, что в действии шапки
|
||
// «Подготовки отгрузки», но вынесен явным пунктом сразу после неё —
|
||
// чтобы поставщик не пропустил, где взять код на приёмке (зеркало
|
||
// пункта «Получить заказ» у заказчика).
|
||
path: 'ship-party',
|
||
name: 'marketplace-ship-party',
|
||
component: markRaw(OffererShipPartyPage),
|
||
meta: {
|
||
title: 'Отгрузить партию',
|
||
icon: 'fa-solid fa-qrcode',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 5 / Story 5.7: offerer-стол ожидающих подписи актов приёмки.
|
||
// Поставщик первой подписью signapl1 подтверждает факт приёмки
|
||
// партии ПВЗ — после этого ПВЗ закрывает акт второй подписью.
|
||
path: 'apl-receptions',
|
||
name: 'marketplace-apl-receptions',
|
||
component: markRaw(OffererPendingAplReceptionsPage),
|
||
meta: {
|
||
title: 'Подпись приёмки',
|
||
icon: 'fa-solid fa-file-signature',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 5 / Story 5.9: offerer-стол «Выплаты». Настройка
|
||
// «выплаты получаю на…» (реквизиты ядра) + история выплат
|
||
// MarketplaceOutgoingPaymentRequest по закрытым актам приёмки.
|
||
path: 'payments',
|
||
name: 'marketplace-payments',
|
||
component: markRaw(OffererPaymentHistoryPage),
|
||
meta: {
|
||
title: 'Выплаты',
|
||
icon: 'fa-solid fa-money-bill-transfer',
|
||
requires: 'Offer:create:own',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
|
||
// ──────────────────────────── Стол ПВЗ ────────────────────────────
|
||
{
|
||
workspace: 'market-pvz',
|
||
extension_name: 'market',
|
||
title: 'Стол ПВЗ',
|
||
icon: 'fa-solid fa-map-location-dot',
|
||
defaultRoute: 'marketplace-pvz-reception',
|
||
routes: [
|
||
{
|
||
// Стол ПВЗ — рабочее место оператора КОНКРЕТНОГО кооперативного
|
||
// участка (marketplace-роль `operator` → `Warehouse:read:own-KU`):
|
||
// председателя КУ (trustee) либо его доверенного лица (trusted).
|
||
// Активный КУ определяется через `marketplaceWhoAmI.branches`
|
||
// (entities/OperatorBranch), коды участков в UI не показываются.
|
||
// Управление сетью ПВЗ кооператива (создание/геокодинг/статусы) —
|
||
// на столе администратора (AdminIssuancePoints), не здесь.
|
||
// Гейтинг — через grants, отдельный per-route guard не нужен.
|
||
meta: {
|
||
title: 'Стол ПВЗ',
|
||
icon: 'fa-solid fa-map-location-dot',
|
||
},
|
||
path: '/:coopname/market-pvz',
|
||
name: 'market-pvz',
|
||
children: [
|
||
{
|
||
// Поток IV: единый operator-стол «Ожидаемые поставки» — лента партий,
|
||
// направленных на КУ оператора (own-KU scoping через
|
||
// marketplaceListShipmentsByBraname + isMemberOfBranch), их состав
|
||
// («что/когда/кому везут») И приёмка по QR-коду на одной странице.
|
||
// Отдельный стол «Приёмка партии» слит сюда: оператор не прыгает
|
||
// между «жду» и «принимаю» (ревью 2026-06-01).
|
||
path: 'reception',
|
||
name: 'marketplace-pvz-reception',
|
||
component: markRaw(OperatorReceptionPage),
|
||
meta: {
|
||
title: 'Ожидаемые поставки',
|
||
icon: 'fa-solid fa-truck-arrow-right',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Стол раскладки/маркировки: принятое имущество (склад) — назначить
|
||
// полку, разложить позицию по нескольким полкам, наклеить штрих-код.
|
||
path: 'labeling',
|
||
name: 'marketplace-pvz-labeling',
|
||
component: markRaw(OperatorInventoryLabelingPage),
|
||
meta: {
|
||
title: 'Раскладка и маркировка',
|
||
icon: 'fa-solid fa-tag',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Эпик 9 / Story 9.1: operator-стол «Склад моего КУ». Идёт сразу
|
||
// после раскладки/маркировки — принятое имущество разложено и
|
||
// видно на складе перед выдачей.
|
||
path: 'warehouse',
|
||
name: 'marketplace-pvz-warehouse',
|
||
component: markRaw(OperatorOwnWarehousePage),
|
||
meta: {
|
||
title: 'Склад моего КУ',
|
||
icon: 'fa-solid fa-boxes-stacked',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Эпик 8: operator-стол подтверждения списания со склада. Совет
|
||
// одобрил проект списания — председатель КУ подтверждает
|
||
// фактическое выбытие имущества, подписав Служебную записку 1111.
|
||
path: 'writeoffs',
|
||
name: 'marketplace-pvz-writeoffs',
|
||
component: markRaw(PvzWriteoffsPage),
|
||
meta: {
|
||
title: 'Списание со склада',
|
||
icon: 'fa-solid fa-trash-can-arrow-up',
|
||
requires: 'Writeoff:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Эпик 6 / Story 6.6: operator-стол выдачи имущества пайщику на ПВЗ.
|
||
path: 'issuance',
|
||
name: 'marketplace-issuance',
|
||
component: markRaw(OperatorIssuancePage),
|
||
meta: {
|
||
title: 'Выдача заказов',
|
||
icon: 'fa-solid fa-handshake',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Эпик 7 / Story 7.2-7.4: operator-стол гарантийных возвратов.
|
||
path: 'returns',
|
||
name: 'marketplace-pvz-returns',
|
||
component: markRaw(OperatorReturnClaimsPage),
|
||
meta: {
|
||
title: 'Гарантийные возвраты',
|
||
icon: 'fa-solid fa-clipboard-check',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Управление доверенными лицами КУ: председатель участка (trustee)
|
||
// добавляет/снимает доверенных (core addTrustedAccount/
|
||
// deleteTrustedAccount) — они получают те же операционные права
|
||
// по Столу ПВЗ. Видна всем операторам КУ; правки — только
|
||
// председателю кооператива (auth мутаций = chairman).
|
||
path: 'trusted-persons',
|
||
name: 'marketplace-pvz-trusted-persons',
|
||
component: markRaw(OperatorTrustedPersonsPage),
|
||
meta: {
|
||
title: 'Доверенные лица',
|
||
icon: 'group',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// Экономика участка (requirement b6): ставка кооператива,
|
||
// отсечка и веса распределения членских взносов (правки —
|
||
// председателю КУ), персональные кошельки доверенных с
|
||
// переводом в Стол заказов и материальной помощью.
|
||
path: 'economy',
|
||
name: 'marketplace-pvz-economy',
|
||
component: markRaw(OperatorBranchEconomyPage),
|
||
meta: {
|
||
title: 'Экономика участка',
|
||
icon: 'savings',
|
||
requires: 'Economy:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
},
|
||
{
|
||
// «Сканировать QR» — сквозной универсальный считыватель. Пункт меню
|
||
// НЕ открывает страницу (нет component), а вызывает действие
|
||
// `marketplaceUniversalScan` (всплывающий сканер, как кнопка
|
||
// «Поддержка»; держатель — UniversalScannerHost в layout). Оператор
|
||
// сканирует ЛЮБОЙ код, и система по его виду ведёт на нужный стол:
|
||
// код поставщика/ТТН → приёмка, код заказчика → выдача. Стоит
|
||
// последним — это сквозное действие, а не раздел.
|
||
path: 'scan',
|
||
name: 'marketplace-pvz-scan',
|
||
meta: {
|
||
title: 'Сканировать QR',
|
||
icon: 'qr_code_scanner',
|
||
action: 'marketplaceUniversalScan',
|
||
requires: 'Warehouse:read:own-KU',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
|
||
// ──────────────────────── Стол администратора ─────────────────────
|
||
{
|
||
workspace: 'market-admin',
|
||
extension_name: 'market',
|
||
title: 'Стол администратора',
|
||
icon: 'fa-solid fa-shield-halved',
|
||
// Реестр всех заказов кооператива — центральный обзорный экран стола;
|
||
// открывается первым.
|
||
defaultRoute: 'marketplace-admin-orders',
|
||
routes: [
|
||
{
|
||
meta: {
|
||
title: 'Стол администратора',
|
||
icon: 'fa-solid fa-shield-halved',
|
||
},
|
||
path: '/:coopname/market-admin',
|
||
name: 'market-admin',
|
||
children: [
|
||
{
|
||
// Единый реестр всех заказов кооператива с текущими статусами
|
||
// (`Order:read:all` есть у board_readonly и admin). Раскрытие заказа
|
||
// показывает его состояние (таймлайн) и детализацию процесса
|
||
// p.mkt.supply — документы + операции + проводки по order_hash —
|
||
// через общий виджет ProcessDetailCard, без ссылок на стол
|
||
// бухгалтера (у администратора/совета может не быть к нему доступа).
|
||
path: 'orders',
|
||
name: 'marketplace-admin-orders',
|
||
component: markRaw(AdminOrdersPage),
|
||
meta: {
|
||
title: 'Реестр заказов',
|
||
icon: 'receipt_long',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Реестр поставщиков: модель работы, договор (номер + дата),
|
||
// статус допуска. Администратор видит реестр и добавляет поставщика
|
||
// напрямую (`Supplier:manage`); одобрение/отклонение заявок —
|
||
// действие председателя (кнопки в строке видны только ему).
|
||
path: 'suppliers',
|
||
name: 'marketplace-suppliers',
|
||
component: markRaw(SupplierRegistryPage),
|
||
meta: {
|
||
title: 'Реестр поставщиков',
|
||
icon: 'storefront',
|
||
requires: 'Supplier:manage',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Реестр всех предложений кооператива любого статуса
|
||
// (опубликованные/снятые/отклонённые/на модерации), всех
|
||
// поставщиков (`Offer:read:all` — у admin и board_readonly).
|
||
// Отдельно от «Модерации» (там только ждущие решения); на эти
|
||
// карточки ведёт переход «Открыть предложение» из реестра заказов.
|
||
path: 'offers',
|
||
name: 'marketplace-admin-offers',
|
||
component: markRaw(AdminOffersPage),
|
||
meta: {
|
||
title: 'Реестр предложений',
|
||
icon: 'storefront',
|
||
requires: 'Offer:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 3 / Story 3.6: admin-стол модерации offer'ов. Виден совету
|
||
// и председателю (`Order:read:all` есть у board_readonly и admin).
|
||
path: 'moderation',
|
||
name: 'marketplace-moderation',
|
||
component: markRaw(ChairmanModerationPage),
|
||
meta: {
|
||
title: 'Модерация',
|
||
icon: 'fa-solid fa-clipboard-check',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 15: полная карточка предложения для модератора —
|
||
// открывается по клику на карточку в «Модерации». Та же
|
||
// страница, что и в каталоге, но на столе администратора
|
||
// (`Order:read:all`), без перехода на стол заказчика.
|
||
// `readonly: true` → страница прячет кнопку «Заказать» и ведёт
|
||
// назад в «Модерацию», а не в каталог.
|
||
path: 'offer/:offerId',
|
||
name: 'marketplace-admin-offer-detail',
|
||
component: markRaw(MarketplaceOfferDetailPage),
|
||
meta: {
|
||
title: 'Предложение',
|
||
icon: 'fa-solid fa-box',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
hidden: true,
|
||
readonly: true,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 3 / Story 3.x: chairman-настройка whitelist'а категорий.
|
||
// Только председатель (`Whitelist:manage` есть лишь у admin).
|
||
path: 'economy',
|
||
name: 'marketplace-admin-economy',
|
||
component: markRaw(AdminMarketEconomyPage),
|
||
meta: {
|
||
title: 'Экономика',
|
||
icon: 'percent',
|
||
requires: 'Economy:set-fee',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
path: 'category-whitelist',
|
||
name: 'marketplace-category-whitelist',
|
||
component: markRaw(ChairmanCategoryWhitelistPage),
|
||
meta: {
|
||
title: 'Доступные категории',
|
||
icon: 'fa-solid fa-filter',
|
||
requires: 'Whitelist:manage',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 2: admin-стол «Пункты выдачи заказов». Председатель
|
||
// подключает кооперативные участки (core) как ПВЗ Стола заказов
|
||
// (адрес/контакты/режим работы + геокодинг) и управляет их
|
||
// статусом. Видна совету и председателю (`Order:read:all`);
|
||
// управляющие действия внутри — только председателю (isChairman),
|
||
// что совпадает с бэкенд-авторизацией мутаций (chairman-only).
|
||
path: 'issuance-points',
|
||
name: 'marketplace-issuance-points',
|
||
component: markRaw(AdminIssuancePointsPage),
|
||
meta: {
|
||
title: 'Пункты выдачи заказов',
|
||
icon: 'pin_drop',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 8 / Story 8.7: admin-стол списания скоропорта.
|
||
path: 'writeoffs',
|
||
name: 'marketplace-writeoffs',
|
||
component: markRaw(AdminWriteoffsPage),
|
||
meta: {
|
||
title: 'Списания скоропорта',
|
||
icon: 'fa-solid fa-trash-can-arrow-up',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 9 / Story 9.2: admin-стол сводного склада кооператива.
|
||
path: 'warehouse-summary',
|
||
name: 'marketplace-warehouse-summary',
|
||
component: markRaw(AdminWarehouseSummaryPage),
|
||
meta: {
|
||
title: 'Сводный склад',
|
||
icon: 'fa-solid fa-warehouse',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 5 / Story 5.x: read-only лента выплат поставщикам для совета.
|
||
path: 'payouts',
|
||
name: 'marketplace-board-payouts',
|
||
component: markRaw(BoardPayoutsReadonlyPage),
|
||
meta: {
|
||
title: 'Выплаты — совет',
|
||
icon: 'fa-solid fa-coins',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 9 / Story 9.4: раздел экосистемы — список controller'ов
|
||
// других кооперативов с расширением «Стол заказов».
|
||
path: 'ecosystem',
|
||
name: 'marketplace-ecosystem',
|
||
component: markRaw(EcosystemRegistryPage),
|
||
meta: {
|
||
title: 'Экосистема',
|
||
icon: 'fa-solid fa-network-wired',
|
||
requires: 'Order:read:all',
|
||
requiresAuth: true,
|
||
// Скрыта из меню/поиска до публикации раздела (страница и
|
||
// маршрут остаются — снять hidden, когда раздел готов).
|
||
hidden: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
{
|
||
// Эпик 1 / Story 1.9-1.10: L1 онбординг — приём кооперативом ЦПП
|
||
// «Стол заказов». Председатель подписывает оферту ЦПП. ЭТА страница
|
||
// — единственная видимая до принятия ЦПП (право `Extension:configure`
|
||
// у председателя есть и до, и после; остальные admin-права
|
||
// появляются только после принятия).
|
||
path: 'onboarding/coop-cpp',
|
||
name: 'marketplace-onboarding-coop-cpp',
|
||
component: markRaw(OnboardingCoopAcceptCppPage),
|
||
meta: {
|
||
title: 'Подключение ЦПП',
|
||
icon: 'fa-solid fa-handshake',
|
||
requires: 'Extension:configure',
|
||
requiresAuth: true,
|
||
agreements: agreementsBase,
|
||
},
|
||
children: [],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
]
|
||
}
|
||
|
||
/**
|
||
* Кошельки, которые «Стол заказов» приносит на стол пайщика (путь B).
|
||
* Членский кошелёк программы (`w.mkt.member`) — туда зачисляются возвратные
|
||
* членские средства стола заказов. Главный членский ЦК (`w.wal.member`) и
|
||
* резерв под заказ (`w.mkt.order`) здесь НЕ показываем.
|
||
*/
|
||
export const walletCards: DesktopWalletCard[] = [
|
||
{
|
||
wallet_name: 'w.mkt.member',
|
||
label: 'Членский кошелёк',
|
||
description: 'Стол заказов',
|
||
accent: 'wallet',
|
||
icon: 'card_membership',
|
||
},
|
||
]
|