01System overview.
What it is
Revit CDE — plugin for Autodesk Revit, implementingCommon Data Environment (CDE)for the model approval process and project documentation. CDE principle: the model and its parts (views, sheets, schedules, and sheet sets) go through a formalized approval workflow with composition snapshots captured, an audit trail for every decision and a visible status directly in the Revit model.
Architecturally, the system consists of two major parts:
- Revit-plugin— an extension for Revit (multi-target 2021–2027), including WPF-interface, composition scanners, a status system via Shared Parameters + Extensible Storage and an HTTP client for the server.
- ASP. NET Core server— backend on. NET 8 with PostgreSQL and Entity Framework Core, JWT authorization and REST API. Implements the lifecycle of approval requests, maintains an event log, aggregates model-element statuses.
Target audience
- Authors(BIM modelers and designers) — create models and submit elements for approval.
- Approvers(GIP, GAP, client, reviewers) — make decisions based on submitted composition snapshots.
- Project managers— manage participants and approval routes.
- BIM coordinators and administrators— configure categories, protected parameters, and templates.
key implementation principles
- Composite key
<ProjectGuid, ElementUniqueId>— all Revit elements are identified by the pair «server project ID + UniqueId element». ProjectGuid is stored in Extensible Storage of the document. - Hybrid status storage— Shared Parameters (visible to users and exports) + Extensible Storage (the source of truth for the IUpdater, protection from external edits).
- Imprint snapshotis created when the item is submitted for approval — captures the exact composition on moment solution, so later model changes clearly show what was approved and what was not.
- Append-only log— all events (submission, decisions, withdrawal) are written to a non-deletable log
ApprovalEventfor audit. - Idempotent statuses— reapplying the same status is safe. This allows background syncu lose and transfer state without pain.
- license toggle— MSBuild property
License(default true), when-p:License=falsedefines the constantNO_LICENSEfor future commercial builds.
02Project structure.
solution and projects
solutionRevitCDE.slncontains six projects:
| Project | Type | TargetFramework | Purpose |
|---|---|---|---|
RevitCDE.Core | C# library | net48 / net7-net10-windows | Common models, server API client, ES-skhemy of the document, identity, settings |
RevitCDE.UI | WPF library | net48 / net7-net10-windows | WPF dialogs, MVVM, styles, controls |
RevitCDE.Localization | C# library | net48 / net7-net10-windows | Localization (resources) |
RevitCDE.Domain | library | netstandard2.0 | POCO models domain area CDE:Approval*, Status enums,ImprintItem |
RevitCDE.Server | ASP.NET Core | net8.0 | Backend: API, EF Core, JWT, BCrypt, database migrations |
RevitCDE.Addin | Revit plugin | net48 / net7-net10-windows | IExternalApplication, ribbon commands,IUpdater'y, scanners, orchestration |
Build configurations
The build is performed through MSBuild with per-Revit-version configurations:Debug_2021…Debug_2027andRelease_2021…Release_2027(plus standardDebug|x64andRelease|x64for server builds).
- Domainis built in all configurations (it targets netstandard, and is not tied to a Revit version).
- Serveris built only in standard
Debug|x64 / Release|x64. The Server is not built under Revit configurations in the.sln — on it targets net8.0, there is nothing for it to do under Revit targets. - Addin / Core / UI / Localizationare built for the selected Revit version.
Scriptsbuild-2027.batandbuild-all.batbuild only the client part (Addin); The Server is launched separately viacd SRC\RevitCDE.Server && dotno run.
Project dependencies
Addin → Core, UI, Localization, Domain (via Core) UI → Core, Localization, Domain (via Core) Core → Domain, Localization Server → Domain (only!) Domain → (nothing, only Newtonsoft. Json)
key separation: the server does NOT physically reference Core. This enforces client/server isolation: types from Core (with Revit-dependencies, identity, ES-binders) are not needed and are unavailable on the server. Common types moved to Domain (netstandard, without dependencies except Newtonsoft. Json).
Namespace Domain-classes is kept asRevitCDE.Core.Models.Approvalintentionally — so moving types from Core to Domain does not require changing all call sites.
File layout
SRC/ ├── RevitCDE. Core/ │ ├── Models/ — ApprovalRequest (legacy chat-side), AppSettings, ServerSettings,... │ ├── Identity/ — UserProfile (local identity.json) │ └── Server/ — ApiContracts (DTO), RevitCdeApiClient, TokenSession, │ DocumentProjectBinder, DocumentApprovalSyncState ├── RevitCDE. UI/ │ ├── Controls/ — stili, custom controls │ ├── Dialogs/ — WPF-okna (login, pikery) │ │ └── Server/ — Login, ProjectPicker, SubmitApproval, RouteConfiguration │ ├── Mvvm/ — ViewModel'y │ └── Windows/ — main plugin floating windows ├── RevitCDE. Localization/ — resources lokalizatsii ├── RevitCDE. Domain/ │ └── Models/Approval/ — POCO domain area: │ ApprovalRequest, ApprovalRequestStage, ApprovalRouteMode, │ ApprovalEvent/EventType, ProjectElement(Status)/Kind, │ ModelElement(Status), Snapshot/Item, Imprint*, │ ProtectedParameterRule, ElementFingerprint ├── RevitCDE. Server/ │ ├── Auth/ — JWT, BCrypt, ClaimsPrincipal extensions │ ├── Bootstrap/ — BootstrapAdminHostedService (sozdayot admin when starte) │ ├── Controllers/ — REST endpoints │ ├── Data/ — DbContext │ ├── Dtos/ — servernye DTO │ └── Entities/ — User, Project, ProjectMembership, UserRole └── RevitCDE. Addin/ ├── Commands/ — IExternalCommand: SubmitApproval, ServerLogin, ServerRefresh,... ├── Scanning/ — ImprintScanner, ControlledCategoryCatalog ├── Server/ — ServerCoordinator, SubmitApprovalWorkflow, ApprovalSyncService, │ ApplyServerStatusHandler ├── Status/ — StatusWriter, ElementStatusMirror, StatusGuardUpdater, │ StatusBindingService, SharedParamFileService, ProjectElementDirtyStore └── RevitCDEApp.cs — IExternalApplication, ribbon, lifecycle
03What has been done: phases 1–5e.
Implementation shla poetapno (commit'ami 1, 2, 3, 4a, 4b, 4c, 4d-1, 4d-2, 5a, 5b, 5c-1, 5c-2, 5d, 5e). Nizhe — detalnoe description kazhdogo sloya.
Rasterizator and fundament models
Goal: prepare infrastructure for later phases.
What has been done
RevitContentPickerDialog— WPF-content selection dialog in Revit-modeli for tests and scripts.- Rasterizatsiya: view rendering to bitmap for preview images.
UserProfilereceived the fieldPosition(user position for signatures in the log).- Created the foundation Approval-models in Domain: enums statusov and basic POCO classes.
Arkhitekturnoe solution
- Composite key
<ProjectGuid, ElementUniqueId>for element identification. ProjectGuid is stored in Extensible Storage of the document Revit (separate schemeDocumentProjectBindingwith GUIDCDE40001-0001-...). - Dolzhnost (
Position) sokhranyaetsya as snapshot kopiey in each log event, chtoby when pereimenovanii / uvolnenii userya history ostavalas chitaemoy.
Skaner composition (ImprintScanner)
Goal: collect a stable imprint (CompositeHash) composition vida, lista, spetsifikatsii or podshivki.
What has been done
ImprintScanner~220 strok — konstruktornew ImprintScanner(doc, settings), metodScanProjectElement(elementId) → ImprintResult.FilteredElementCollector(doc, viewId)sobiraet vidimye on proektnom elemente instances of controlled categories.ControlledCategoryCatalog— centralized listBuiltInCategory, which podlezhat soglasovaniyu (walls, doors, windows, structures, MEP,...).ImprintItem=UID + CategoryId + ProtectedHash(hash zaschischyonnykh parametrov element).CompositeHash=SHA256(sorted UID|ProtectedHash)— deterministic imprint of the entire composition.
Arkhitekturnoe solution
- Stable hash: when sortirovke UID khesh ne zavisit ot poryadka dobavleniya elementov in Revit. Reordering elements does not change CompositeHash.
ProtectedHashincludes only parameters fromProtectedParameterRule(width, height, material type and t.p.) — izmenenie tsveta or kommentariya ne schitaetsya «a composition change».- Category
OST_StructuralRebarandOST_SpaceTagsare excluded — oni ne suschestvuyut in Revit 2027 (Autodesk moved them). Compatibility with Revit 2021–2027 is maintained through#if REVIT2021||REVIT2022direktivy.
Statuses in Revit
Goal: pokazyvat useryu in Revit aktualnyy status soglasovaniya kazhdogo element.
What has been done
StatusParameters— five Shared Parameters:RevitCDE_Status,RevitCDE_ApprovedAt,RevitCDE_ApprovedBy,RevitCDE_ApproverPosition,RevitCDE_ServerElementId.SharedParamFileService— management faylom shared parameters (sozdayot when otsutstvii, migriruet when obnovlenii).StatusBindingService— binding parametrov k kategoriyam modeli (BindingMap).ElementStatusMirror— Extensible Storage scheme (GUIDCDE20002-0001-...) with tenevoy kopiey status. Kanon forIUpdater'a; zaschischyon ot poddelki.StatusGuardUpdater(IUpdater) — sledit za izmeneniyami parametrov and rolls back «ruchnye» pravkiRevitCDE_*-parametrov (if kto-to ikh menyaet vne CDE-flow).StatusWriter— unified tochka zapisi:SetStatus(el, status, approvedAtUtc, approvedByDisplayName, approverPosition, serverElementId),SetStatusOnly,Clear. Idempotenten.ProjectElementDirtyStore— Extensible Storage (GUIDCDE20002-0001-...,002) for flaga «gryaznyy» element proekta.ModelElementStatusLabels— dvustoronniy mappingenum ↔ stroka(en + ru lokali).
Arkhitekturnoe solution
- Gibrid Shared Params + Extensible Storage.Shared params vidny useryu in Properties-paneli, popadayut in schedule, eksportiruyutsya. Extensible Storage — closeoe khranilische pod zaschitoy
IUpdater'a, ne redaktiruetsya vruchnuyu. When raskhozhdenii kanon — ES. BindingMapfor raznykh Revit-versiy:#if REVIT2021||REVIT2022ispolzuetBuiltInParameterGroup.PG_IDENTITY_DATA, for 2023+ —GroupTypeId.IdentityData(novyy ForgeTypeId API).- «Gryaznyy» — otdelnyy bool flag, ne status. State «izmenyon after soglasovaniya» = (
Approved AND Dirty=true), this kompozitsiya, a ne alternativnyy status. Yavnoe khranenie allows legko podsvetit «trebuet perepodachi».
Server
server backend podnyat in tri commit:4a (skeleton), 4b (auth + projects), 4c(CDE dannye).
Phase 4a — Domain + skeleton server
RevitCDE.Domainvydelen as otdelnyynostandard2.0proekt with 16 POCO-faylami. Zavisimosti — only Newtonsoft.Json 13.0.3.RevitCDE.Serversozdan on ASP.NET Core 8.0.10. Slushaet port 5180. Health endpoint/health/live.- PostgreSQL via Npgsql 8.0.10. ConnectionString in
appsettings.json. - EF Core 8.0.10 + Npgsql.EFCore — DbContext
RevitCdeDbContext.
Phase 4b — Auth + projects
- BCrypt. Net-Next 4.0.3 for paroley.
- Microsoft. AspNetCore. Authentication. JwtBearer 8.0.10 + System. IdentityModel. Tokens. Jwt 8.0.2 for JWT.
BootstrapAdminHostedService— when pervom starte server sozdayot polzovatelya withIsGlobalAdmin=true. Kredy dev:admin / admin12345(fromBootstrapSettings).UserRoleenum (Viewer=0, Author=1, Approver=2, Manager=3, Admin=4) andProjectMembershipentity with unikalnym indeksom(UserId, ProjectId).- Per-project authorization:
ProjectScopedControllerBasedayotRequireReadAccessAsync,RequireWriteAccessAsync,RequireManageAccessAsync. - Endpoints:
/api/auth/login,/api/users(CRUD),/api/projects(CRUD),/api/projects/{id}/members.
Phase 4c — CDE-dannye
ProjectElement / ModelElement / ProjectElementSnapshot+Items / ApprovalEvent— all POCO in Domain, EF-konfiguratsiya in DbContext.- REST: project-elements (list, get, bulk upsert), model-elements (to zhe, bulk until 5000), snapshots (list by PE, create with rezolvom UID→ServerId), events (append-only, list with filtrami).
- If in
snapshot.itemsest UniqueId, ne zaregistrirovannyy viaPOST /model-elements, server returns 400 withCreateSnapshotProblemResponseand spiskom unresolved. Klient upsert'it nedostayuschie and povtoryaet request.
Phase 4d — Klient servernogo API
RevitCdeApiClient~330 strok — HTTP-client with JWT-inzhektsiey, obrabotkoy 401 (chistit TokenSession), kastomnymApiClientException.TokenSession— in-memory token, FIO, rol. Ne persistitsya mezhdu zapuskami plugin (namerenno — polzovatel loginitsya zanovo).DocumentProjectBinder— Extensible Storage scheme (GUIDCDE40001-0001-...) for svyazi Revit-of the document with servernym proektom.ServerCoordinator— orkestrator Login → ProjectPicker → Bind. Knopki ribbona «Privyazat k proektu» and «Vyyti».- WPF dialogs
ServerLoginDialog,ServerProjectPickerDialog. Owner ustanavlivaetsya viaWindowInteropHelper(dlg).Owner = uiapp.MainWindowHandlefor korrektnogo z-order poverkh Revit.
Workflow soglasovaniy
This samaya bolshaya faza, razbita on five kommitov (5a→5e) and sostavlyaet osnovu CDE-funktsionalnosti.
Phase 5a — Servernaya infrastruktura zayavok
ApprovalRequestentity +ApprovalRequestStatusenum (Pending/Approved/ApprovedWithComments/Rejected/Cancelled). 5 endpoint'ov: List, GetById, Create, Decide, Cancel.- Atomarnaya podacha: sozdanie snapshot + items + ApprovalRequest + ApprovalEvent (
ProjectElementSubmitted) + perevodPE.StatusinOnApproval— vsyo in one tranzaktsii BD. - On one
ProjectElementodnovremenno mozhet byt only onePending-zayavka (409 Conflict when popytke sozdat vtoruyu). - When finale with Approved/AwC:
PE.LastFingerprintServerId = SnapshotServerId,ApprovedAt = now,IsDirty = false. WhenRejectedthese polya ne trogayutsya (mogli ukazyvat on predyduschee uspeshnoe soglasovanie).
Phase 5b — Klientskaya podacha
SubmitApprovalWorkflow— orkestrator: scan → upsert PE → upsert ME (chanki by 5000) → POST /approval-requests.- Auto-retry on
SnapshotCreationFailedException: if mezhdu nashim upsert'om and sozdaniem zayavki kto-to udalil element — again registriruem nedostayuschie and probuem rovno one raz. SubmitApprovalDialog(WPF) — dialog podachi with russkoy plyuralizatsiey («1 element / 2-4 element / 5+ elementov»).SubmitApprovalCommand—IExternalCommandon ribbone «Podat on soglasovanie». Aktivnyy list/vid/speka →TryDetermineKindby tipuView/ViewSheet/ViewSchedule.- UI-project ne zavisit ot Addin:
SubmitFunc-delegat peredayotsya as callback from Command → Dialog.
Phase 5c — Fonovaya synchronizatsiya resheniy
Razbita on 5c-1 (infrastruktura) and 5c-2 (polirovka).
ApplyServerStatusHandler(IExternalEventHandler) — ochered job'ov and primenenie in one Revit-tranzaktsii via StatusWriter. Cross-document protection: checkjob.ProjectId == binding.ProjectId.ApprovalSyncService— DispatcherTimer 30 sek on UI-thread'e. Async-metody dyorgayutawait(UI otzyvchiv). Reentrant-flag zaschischaet ot nalozheniya tikov. On each tik:ListEvents (since=lastSyncAt)→ filtr finalnykh tipov →GetApprovalRequest + GetSnapshotfor kazhdoy → enqueue +ExternalEvent.Raise().- Mapping
ApprovalRequestStatus → ModelElementStatus:Approved → Approved,ApprovedWithComments → AwC,Rejected/Cancelled → nothing ne pishetsya(mogli byt soglasovany by drugomu listu — lomat nelzya). DocumentApprovalSyncState— ES-scheme (GUIDCDE40002-0001-...) for persistentlastSyncAt. Per-document state, chitaetsya on smene of the document. After restarta Revit opros ne lezet za 24h nazad vpustuyu.- TaskDialog so svodkoy resheniy after Apply: «Polucheny rezultaty soglasovaniya: • Soglasovano: 3, • Soglasovano with zamechaniyami: 1. Obnovleno elementov modeli: 47».
- Button lenty «Refresh statusy» (
ServerRefreshSyncCommand) — ruchnoy trigger vne 30-sekundnogo tika. TriggerNow()callssya after uspeshnogo login and after submit for nemedlennoy obratnoy svyazi in testirovanii.
Phase 5d — Mnogostadiynye routes Sequential + ME-agregatsiya
ApprovalRequestStageentity:ServerId, ApprovalRequestId (FK), StageIndex (0..N-1), ApproverUserId. Snapshot FIO/dolzhnosti naznachennogo. Status:Pending → Approved/AwC/Rejected. Decision-polya zapolnyayutsya when zakrytii stadii.ApprovalRequestpoluchil polyaHasStages (bool), CurrentStageIndex (int, -1 for legacy), Stages (List). Cascade-delete ot zayavki k stadiyam.- Unikalnyy indeks
(ApprovalRequestId, StageIndex)and indeks(ApproverUserId, Status)for bystrogo pending-for-me zaprosa. - Endpoint
GET /pending-for-me— zayavki gde ya approver on tekuschey active stadii. - Decide rasshiren: for multi-stage only
currentStage.ApproverUserIdor GlobalAdmin mozhet decide. Reject on lyuboy stadii → final Rejected. Approved/AwC on ne-afterdney →CurrentStageIndex++, sobytieStageApproved. On afterdney →ComputeAggregateFinalStatus(AwC if khot one stadiya with zamechaniyami, inache Approved). - ME-agregatsiya— when finale with Approved/AwC server obnovlyaet
ModelElement.Statusfor vsekh ME from snapshot'a by pravilu: AwC dominiruet (if khot one storona AwC, itog AwC, inache incoming). UserId soglasuyuschego dobavlyaetsya inApproverUserIds(without dubley). - Validatsiya stages when sozdanii: unique
approverUserId, all Approver+ project members (or GlobalAdmin).
Phase 5e — Parallelnye routes + UI piker
ApprovalRouteModeenum:Sequential(default) /Parallel.- Parallel mode: all stages are active at the same time.
CurrentStageIndexostayotsya-1. each assigned approver makes a decision independently. Any rejection — immediately finalizes the request as Rejected. When all are Approved/AwC — aggregate final status. - Decide vetvlenie: for Parallel poisk stadii = lyubaya Pending gde approver = actor (or lyubaya for GlobalAdmin). Finalizatsiya Parallel = «all stadii ne-Pending».
- PendingForMe obnovlyon: edinyy predicate
parallel || stage.StageIndex == request.CurrentStageIndex. RouteConfigurationDialog(novyy WPF-dialog) — dvukhkolonochnyy piker «Available project approvers» ↔ «Route (ordered)». Radio Sequential/Parallel. In Parallel-mode Up/Down buttons are disabled (order does not matter).- Filtering Approver+: only te project members, u kogo rol Approver, Manager or Admin (Viewer and Author will not appear).
SubmitApprovalDialogpoluchil button «Configure route…» and current configuration summary. DelegatSubmitFuncstal(comment, route, ct), novyyMembersLoaderFuncfor pikera.- Restoring missing entities: during session rebuild it was discovered that
Project.cs,ProjectMembership.csandUserRoleenum were physically missing from the file system (only referenced), because the Server project was not built viabuild-2027.bat. Restored — nowdotno runinRevitCDE.Serveractually starts.
04Architectural decisions and rationale.
4.1Why Domain is a separate project
Until Phase 4a all Approval-klassy were located inRevitCDE.Core. When the server was introduced it targets net8.0, the problem: Core references WPF, identity, ES-binders with Revit-dependencies. The server cannot and should not import all of that.
solution: extractnostandard2.0Domain project without dependencies except Newtonsoft. Json. In nego only moved there POCO-klassy (ApprovalEvent, ApprovalRequest, ProjectElement, ModelElement, Snapshot/Items, Imprint*, Statusenums). Core references Domain via ProjectReference, Server tozhe.
Namespace Domain-classes is kept asRevitCDE.Core.Models.Approvalintentionally — all callsite'y in kode (using RevitCDE.Core.Models.Approval) continue to work without changes.
4.2Why PostgreSQL
- Free and open-source, excellent support for Guid (
uuidtip). - Native array support (
uuid[],int[]— used forModelElement.ApproverUserIds). - JSON field support (JSONB) — will be useful in Phase 7 for flexible payloads'ov sobytiy.
- Good performance EF Core via Npgsql. EntityFrameworkCore. PostgreSQL.
- A standard choice for server applications on. NET Core, simpler than configuring SQL Server in Linux.
4.3Why snapshot sozdayotsya in moment podachi, a ne in moment solution
Alternative design: the approver looks at «current» view composition in the moment they click «Approve». Rejected because:
- Mezhdu podachey and resheniem days may pass — the composition may change without the approver knowing.
- Soglasuyuschiy must review and decide based on a specific fixed state — otherwise formal approval has no meaning.
CompositeHashsnimka can be compared with the currentCompositeHashvida and automatically detect «changed after submission».
4.4Why hybrid Shared Parameters + Extensible Storage
Shared Params without ES — unsafe: a user can change through the UIRevitCDE_Statusu soglasovannoy steny. Adding IUpdater does not help: Revit calls Updater only whenOnDocumentChanged, and the user sees the change immediately.
Only ES without Shared Params — invisible: the user will not see the status in Properties-paneli and cannot add a column «Status» in spetsifikatsiyu.
Gibrid: Shared Params are visible and work normally. ES — closed shadow storage, in kotoroe pishet Only StatusWriter in ramkakh authorized transactions.StatusGuardUpdatercompares Shared Params with ES and on mismatch (= manual edit) rolls back Shared Params on znachenie ES, and writes an eventStatusTamperAttemptin zhurnal.
4.5Why DispatcherTimer instead of a background task
Background-task (Task.Run) on ThreadPool — has no access to the Revit API. Revit API can only be called on the UI thread'e, and only in ramkakh tranzaktsii, and only when Revit is idle.
DispatcherTimer runs on the UI thread'e prilozheniya — my automatically on correct thread'e.async/awaitfrees the thread during the HTTP request (the UI remains responsive), continuation returns back. After pollinging'a my Enqueue jobs andRaise()ExternalEvent — Revit will call our Execute when idle.
4.6Why ME aggregation on the server instead of the client
Phase 5c-1 performed mapping on the client: poluchilirequest.Status=Approved→ pishem vsem ME from snapshot'aModelElementStatus.Approved. Problem:
- For ME, which approved in several snapshots'akh (on raznykh listakh) — needed agregatsiya. the client does not know the history of other approvals.
- If only chto odobrili snapshot A, a snapshot B (with etim zhe ME) was previously approved with comments — final
ME.Statusmust be AwC (the worst status dominates). This requires knowledge of the past. - clients of different users may receive events in different order — without server aggregation they may arrive at different results.
Phase 5d moved aggregation to the server: when finale zayavki with Approved/AwC the server iterates through all ME snapshot'a and applies the aggregation rule (AwC dominiruet). the client then simply reads finalME.Status.
4.7Why Sequential — default, Parallel — opt-in
- Sequential — this «normal» workflow soglasovaniya: lid → GIP → client.
- Parallel useful kogda nuzhno sobrat odobrenie several independent parties (naprimer MEP + konstruktiv + OV — ni one ne dolzhen zhdat drugogo).
- Default Sequential = compatibility with Phase 5d (old requests
RouteMode=0= Sequential ostayutsya workt as and ranshe).
4.8Why one user ne mozhet byt on dvukh stadiyakh one zayavki
Validatsiya inValidateAndFetchStagesAsync. Rationale:
- Sequential: «lid → lid → client» loses meaning (the user approves for themselves).
- Parallel: the same approver on two stages = either a typo, libo an attempt to obtain «double approval» ot odnogo cheloveka (meaningless).
- If potrebuetsya business scenario «one chelovek za dve roles» — we can remove validation, no this budet eskalatsiya / delegirovanie, which better modeled separately.
4.9Why cancel does not close stages
When cancel request stages remain inPending. UI otlichaet ikh byrequest.Status == Cancelled(«frozen»). Alternative — setCancelledon kazhduyu stadiyu — rejected, potomu chto:
- Cancel = withdrawal by the initiator, not an approver decision. Stadii ne «otkazali» — oni «did not have time to review».
- Semantically Pending-stadiya in Cancelled-zayavke = «had to be reviewed, no ne uspeli».
- If the author submits a new request with temi zhe soglasuyuschimi — ikh stadii budut clean
Pending.
05What remains.
Approver web portal
The most important next phase. Approvers (GIP, GAP, zakazchik) usually do NOT work in Revit — u nikh no license and no navyka. Currently oni are forced to make decisions via Swagger UI or via third-party integration.
- Separate frontend: probably React + Vite + TypeScript (could be Blazor WebAssembly if khotim. NET-only stack).
- Pages: login, my projects, dashbord (my pending requests), request card (detali + snapshot + decision buttons), history projecta, event log.
- Extensions API: veroyatno needed endpoint for preview images snapshot'ov (render vida); extended filter on
/events; bulk-decisions (Approve all pending). - SignalR-push for realtime-notifikatsiy (new request received → soglasuyuschiy sees a notification without refreshing).
- Avtorizatsiya: tot zhe JWT chto u plugin. Login flow:
/api/auth/login → token → store in localStorage / session-cookie. - In planakh takzhe: history zayavok odnogo PE (timeline vsekh stages vsekh zayavok).
CDE documents (workflow WIP / Shared / Published / Archived)
- Soglasovanie on urovne dokumentov in CDE-smysle (section 4.5 plana). PDF/DWG as artefakty, transmittals, RFI, sections documentation.
DocumentSetentity, statusy WIP → Shared → Published → Archived with perekhodami.- Versionnost dokumentov and diff mezhdu versionsi.
- Internal chat / kommentarii by of the documentm.
- This a separate area, bound k BIM-razdelu ne silno — separate modulee with its own APIs/controllers.
Extensions
- Linked models — support soglasovaniya via svyazannye modeli (RVT links). Currently
IsLinkeddokument is simply ignored. - BCF import/export — industrialnyy standart for issue communication between BIM tools.
- Power BI integration — reporting and dashboards by soglasovaniyu.
- Integratsiya with MS Teams / Outlook — notifications o novykh zayavkakh.
Not included in Phase 5 — server side
- server recalculation ME when retro-Reject. Currently if pozzhe vyyasnilos, chto the approval was wrong and zayavku rejected —
ME.Statusuzhe Approved, is not recalculated. ApproverUserIdson ModelElement rastyot without cleanup. For dolgo zhivuschikh proektov with tysyachami zayavok may grow too large. Needed TTL or periodic cleanup.- Unikalnost «one Pending on PE» is checked in kode via
AnyAsync, ne via partial unique index PostgreSQL. Under load two simultaneous submissions may slip through. Melkiy race; protection viaCREATE UNIQUE INDEX... WHERE status='Pending'dobavim in next commit. - Bulk endpoint
/api/projects/{id}/approval-requests/by-idsfor umensheniya N+1 zaprosov inApprovalSyncService(seychas on kazhdoe finalnoe sobytie — 2 GET'a).
Not included in Phase 5 — clientskaya storona
- UI approver UI in the plugin — spisok pending-for-me + button approve/reject. Questionable feature: soglasuyuschie obychno workyut in brauzere, a ne in Revit. Skoree total realizuem only in Phase 6 web-kabinoe.
- Podacha vsey podshivki tselikom — separate command «Podat podshivku», dialog with predprosmotrom kazhdogo lista.
ImprintScannerpodshivki already supports. - Beydzh pending-zayavok in ribbone («u vas 3 ozhidayut»). Trebuet kastomnykh LargeImage-tekstur, deferred.
- Persistent sokhranenie last used route in ES of the document — minor UX, not critical.
- Progress indicator for bulk-upsert ME when large sheet sets (10000+ elementov require several round trips'ov).
Mnogostadiynye routes
- Route templates — preset «standartnyy marshrut» = zaranee predefined sequence soglasuyuschikh. Nuzhno for typical projects gde marshrut odinakovyy for vsekh listov.
- Delegirovanie stadiy — Approver A peredayot svoyu stadiyu Approver'u B (vacation, illness). Trebuet separate endpoint and audit.
- Eskalatsiya — if stadiya Pending bolshe N days, automatic manager notification.
- Uslovnye routes (if-this-then-that) — naprimer «if est kriticheskie zamechaniya, dobavlyaetsya etap GIPa». Ochen complex, ne in plane.
Synchronization and infrastructure
- SignalR/WebSocket push instead of polling. Currently zaderzhka until 30 sek between approver decision and the status appearing in Revit.
- Reapply when
OnDocumentOpened. If otkryli model, in kotoroy there were pending requests long ago and za this vremya soglasuyuschiy prinyal solution — oni will be pulled in in InitialBacklog (24h). Rasshirenie via persistent state. - Background authorization error. If token expires, poller vidit 401 and TokenSession is cleared. UI-indikatsii seychas no; user uvidit when sleduyuschem clicks.
Technical debt
- Testy.Currently there are no automated tests yet. Nuzhny as minimum: integration tests for server endpointss'ov (TestHost + test database), unit tests on StatusWriter, ImprintScanner, agregatsiyu.
- Logging structured.Currently Microsoft. Extensions. Logging with defoltnym provayderom. For production needed Serilog with output in file + JSON for parsinga.
- Health checks.
/health/liveest, no no/health/ready(DB connectivity, migrations applied). - API versioning.All endpoint'y
/api/...without versions. Dobavit/api/v1/...for buduschey compatibilityi. - Rate limitingon endpoint'akh login and create-request. Protection from brute force and spam.
- Localization.UI on russkom khardkodom. For intl-projectov nuzhno perevodit via
RevitCDE.Localization.Resources.
Documentation
- Konos-userskiy gayd (PDF) — step-by-step scenario «as podat on soglasovanie» with screenshots Revit-interface.
- Gayd for soglasuyuschego — poka via Swagger; after Phase 6 — for web-kabinoa.
- DevOps-documentation — as podnyat server from iskhodnikov (Docker compose: postgres + server), as apply migrations, as configure
appsettings.jsonfor production. - API reference — Swagger UI khorosh for razrabotchikov no for integrators needed separate document with description kazhdogo endpoint'a, error codes, examples.