Hierarchie firmy: kdo co vidi, a zalozka Firma

Pohled na celou firmu dostal kazdy, kdo do ni patril: scopes.push('tenant')
se v access.ts neptalo na nic. mine byl dobrovolny filtr, ne strop, takze
resitel s roli agent poslal ?scope=tenant a dostal cely provoz.

Rozdil, na kterem to ted stoji: pohled je co chci videt, strop je co vubec smim
videt. Existoval jen pohled a klientovi se veril.

Model:

- Membership.seesAllTenant - vidi cely provoz firmy. Postaveni uctu ve firme,
  ne vlastnost resitele: clovek muze byt ve dvou firmach jednou reditel
  a jednou brigadnik
- PersonGroup.members je { personId, seesAll } misto holeho personIds. seesAll
  je vedouci sekce. Priznak visi na clenstvi, ne na cloveku a ne na skupine -
  diky tomu muze byt clovek v peti sekcich a jen ve dvou videt vsechno, coz
  role rict neumi, ta je jedna na celou firmu
- stara podoba personIds se dal cte, prevadi ji groupMembers

Vypocet stropu (visibilityFor), sjednoceni ne prunik: spravce platformy nebo
seesAllTenant vidi celou firmu, jinak svoje tickety plus vse ze sekci, kde ma
zaskrtnuto, plus jejich fronta. U zaznamu bez priznaku rozhoduje pravo
ticket.assign.others - kdo smel prehazovat cizi praci, uz stejne cely provoz
videl, takze se mu nic nebere.

Vynuceni:

- strop je povinna soucast TicketFilter, stejne jako tenantIds. Nepovinny filtr
  na prava je filtr, ktery jednou nekde chybi - takhle prekladac ukazal vsech
  trinact mist, ktera ho jeste nemela
- getWorkload a getAgentStats uz nesahaji do pole ticketu primo, jdou pres
  listTickets. Driv obchazely kazde omezeni viditelnosti
- getTicket kontroluje strop i u jednoho ticketu, bez toho by stacilo znat ID
- detail a prevzeti pocitaji strop za firmu ticketu, ne za prave prepnutou

Helpdesk se ridi toutez hierarchii, jen "moje" znamena, co jsem zalozil -
zadavatel pozadavek nikdy nema prirazeny. Ticket proto nese createdById.

Zalozka Firma: pod jednim mistem Prehled, Resitele, Sekce, Role a prava, Typy
ticketu a Pozvanky. V Nastaveni zustal Muj ucet a platformni veci. Role a Typy
jsou samostatne komponenty, sekce maji vlastni panel - u kazdeho clenstvi je
prepinac a radek na cloveka obecny EntityAdmin neumi.

Obsah ticketu se cte i v helpdesku, v seznamu jako jednoradkovy nahled.

Overeno na bezici instanci se tremi ucty nad tymiz daty: spravce platformy vidi
vsechny tri tickety vcetne neprirazeneho, Vomacka jako vedouci Servicedesku dva
(tickety obou clenu sekce), Kriz jako radovy clen jeden, jen svuj. Adresa cizho
ticketu vraci 404, vytizeni tymu ukazuje jen viditelne a po zaskrtnuti priznaku
se rozsah zmeni hned.

Co z toho plyne: neprirazeny ticket bez skupiny nevidi nikdo krome toho, kdo
vidi celou firmu. Prichozi praci musi nekdo smerovat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
JiriUhlir
2026-09-02 12:15:47 +02:00
co-authored by Claude Opus 5
parent a6722f9379
commit 6bcd7f845c
24 changed files with 951 additions and 195 deletions
+93
View File
@@ -2,6 +2,99 @@
Nejnovejsi nahore.
## 2026-09-02 - Hierarchie firmy: kdo co vidi, a zalozka Firma
Pohled na celou firmu dostal kazdy, kdo do ni patril: `scopes.push('tenant')`
se v `access.ts` neptalo na nic. `mine` byl dobrovolny filtr, ne strop, takze
resitel s roli `agent` poslal `?scope=tenant` a dostal cely provoz.
Rozdil, na kterem to ted stoji: **pohled je co chci videt, strop je co vubec
smim videt.** Existoval jen pohled a klientovi se veril.
### Model
Priznak visi na **clenstvi**, ne na cloveku a ne na skupine. Diky tomu muze byt
clovek v peti sekcich a jen ve dvou z nich videt vsechno - to role rict neumi,
ta je jedna na celou firmu (`Membership` je dvojice firma a role).
- `Membership.seesAllTenant` - vidi cely provoz firmy. Postaveni uctu ve firme,
ne vlastnost resitele: clovek muze byt ve dvou firmach jednou reditel
a jednou brigadnik.
- `PersonGroup.members` je `{ personId, seesAll }` misto holeho `personIds`.
`seesAll` je vedouci sekce. Vedoucich muze byt vic a jeden clovek muze vest
vic sekci, proto to neni zvlastni pole na skupine.
- Stara podoba `personIds` se dal cte, prevadi ji `groupMembers` - jedine misto,
kde se to deje, aby skupina ulozena driv neprisla o cleny.
### Vypocet stropu
`visibilityFor` v `data/access.ts`, sjednoceni ne prunik:
```text
spravce platformy nebo seesAllTenant -> cela firma
jinak -> moje tickety
+ vse ze sekci, kde mam zaskrtnuto
+ fronta techhle sekci
```
`seesAllTenant === undefined` jsou zaznamy ulozene driv. Tam rozhoduje pravo
`ticket.assign.others`: kdo dosud smel prehazovat cizi praci, uz stejne cely
provoz videl, takze se mu nic nebere. Bezny resitel timhle sitem neprojde
a spadne na svoje sekce, coz je prave ta zmena, o kterou jde.
### Kde se to vynucuje
**Strop je povinna soucast `TicketFilter`**, stejne jako `tenantIds`.
Nepovinny filtr na prava je filtr, ktery jednou nekde chybi - takhle prekladac
ukaze kazde misto, ktere ho jeste nema. Pri zavedeni jich naslo trinact.
- `listTickets` filtruje pres `withinVisibility`
- `getWorkload` a `getAgentStats` uz nesahaji do pole ticketu primo, jdou pres
`listTickets`. Driv obchazely kazde omezeni viditelnosti
- `getTicket` kontroluje strop i u jednoho ticketu. Bez toho by stacilo znat ID:
seznam by ho schoval, ale adresa detailu vydala
- detail a prevzeti pocitaji strop **za firmu ticketu**, ne za prave prepnutou -
odkaz z pohledu "vse" muze vest do jine firmy uzivatele
### Helpdesk
Ridi se toutez hierarchii, jen "moje" znamena neco jineho: zadavatel pozadavek
nikdy nema prirazeny, resi ho nekdo u dodavatele. Proto ma ticket nove
`createdById` a v helpdesku plati: kdo vidi celou firmu, vidi vsechny jeji
pozadavky, ostatni jen ty svoje.
### Zalozka Firma
Bylo to roztazene mezi dve mista. Nove je pod **Firma** vsechno, co se firmy
tyka: Prehled, Resitele, Sekce, Role a prava, Typy ticketu, Pozvanky.
V Nastaveni zustal Muj ucet a platformni veci, ktere klient nevidi.
Role a Typy jsou ted samostatne komponenty (`RolesAdmin`, `TicketTypesAdmin`),
ne kus stranky nastaveni.
Sekce maji vlastni panel misto obecneho `EntityAdmin`: u kazdeho clenstvi je
prepinac "vidi vse ze sekce" a radek na cloveka obecny editor s poli neumi.
### Overeno na bezici instanci
Tri ucty nad tymiz daty:
| Kdo | Vidi |
| -------------------------- | --------------------------------------- |
| spravce platformy | vsechny 3 tickety vcetne neprirazeneho |
| Vomacka, vede Servicedesk | 2, tedy tickety obou clenu sekce |
| Kriz, radovy clen tehoz | 1, jen svuj |
K tomu: adresa cizho ticketu vraci 404, vytizeni tymu ukazuje jen viditelne,
a po zaskrtnuti priznaku u clenstvi se rozsah zmeni hned.
### Co z toho plyne
Neprirazeny ticket **bez skupiny** nevidi nikdo krome toho, kdo vidi celou
firmu. Je to spravne podle modelu - takovy ticket nepatri do zadne sekce - ale
znamena to, ze prichozi praci musi nekdo smerovat, jinak radovym clenum nikdy
nedorazi.
## 2026-09-02 - Obsah ticketu se na detailu cte
`body` je jeden retezec, ale co v nem stoji, urcuje ten, kdo ho naplnil. Krok