Owner S175 chot cach doc "sum tong" (gate TIEN): (1) truc = CHI TONG TOAN PHIEU, KHONG co
dong tong per-NCC; (2) hien 2 TONG RIENG canh nhau — Sigma gia DE XUAT ⟂ Sigma gia DUYET,
dat PHIA TREN bang. Mirror 9 CAP file sha256 khop 9/9; build XANH ca 2 app, 0 error TS.
=== O6 — bam card PE tren cay GIU NGUYEN man KHKK ===
+callback `onOpenPe` (usePipelineStages) mirror DOI XUNG `onOpenKhkk` cua leaf GD2.
KHONG truyen => giu `navigate('/purchase-evaluations?id=..&expand=1')` => 5 host cay con lai
0 doi hanh vi (da verify: chi KhkkListPage truyen).
Host: `?pe=<id>` render `PeDetailTabs readOnly mode="workspace"` ngay panel 2 — DUNG LAI
renderer that cua man PE thay vi dung ban rut gon thu hai (2 ban = 2 su that).
🔴 `pe` va `id` LOAI TRU NHAU (2 setter cung xoa key kia): panel 2 chi ve duoc mot thu, de
ca hai thi lan chon sau khong xoa duoc lan truoc.
`<lg` roi ve duong cu (khong co panel 2 thi xem-tai-cho la bat kha).
Panel 3 doi chu khi dang xem PE — cau cu "se hien khi chon phieu" la SAI (phieu DA chon roi).
=== O4 — shopdrawing / can cu ho so cho dinh kem file (FE-only) ===
🔴 0 BE MOI — rail da du: controller `[FromForm] Guid? dossierItemId` (:181) + DTO
`KhkkAttachmentDto.dossierItemId` (:241) co tu Mig 69. FE truoc day don gian la KHONG BAO GIO
gui truong do len => moi file roi ve muc PHIEU. Nay upload gui kem dossierItemId.
Chia 2 ro: file gan can cu hien ngay DUOI dong can cu do; Section 4 chi con file muc phieu —
khong chia thi file hien o CA HAI cho, nguoi dung dem 2 lan.
Dong-con file hien CA KHI read-only (nguoi duyet can xem shopdrawing, chi khong them/xoa).
=== O7 — nut edit tren card KHKK (man Thao tac) ===
But chi khuon `PeListPanel:208-232`. LUON hien roi MO DI khi khong sua duoc: nut chi-hien-khi-
hover thi tren may cam-ung khong ai tim ra; an han khi phieu khoa thi nguoi dung tuong chuc
nang khong ton tai thay vi hieu la phieu da di duyet.
🔴 Nut NGOAI the <button> cua card — long button trong button la HTML khong hop le.
Host: but chi => mo che-do SUA; than card => chi xem nhu cu. Guard readOnly Y HET
`KhkkListPage:147-150` (cung mot cau hoi "ai duoc sua" phai cho cung mot cau tra loi o moi man).
Da verify khong phai co-hoc: `KhkkDetailContent:161` `isDraft = draftPhase && !readOnly`.
=== O8 — cay 2 cap Du an > Hang muc ===
🔴 0 ENDPOINT MOI: `workItemName` DA co san trong `KhkkPeCandidateDto:423` => cap 2 la gom lai
tren du-lieu dang co. Truoc day 1 cap theo du an nen mot du an nhieu goi thau do ra danh sach
phang dai, khong thay ranh gioi hang muc.
🔴 2 muc `z` khac nhau (z-20 cha / z-10 con): cung z thi header hang muc truot DE len header
du an khi cuon, mat cap cha.
=== C3 — Field: value ve slate-900, label giu brand-600 ===
Truoc day MOI value deu to brand => mau thuong-hieu mat het suc nang: ca trang deu la mau nhan
thi khong con gi la nhan, va khong chua duoc mau cho thu THAT SU can noi (NCC trung thau,
canh bao vuot ngan sach). PE giu slate cho value dung vi ly do nay.
=== O3 — MOI LAND VE (2). VE (1) CHUA LAM — xem CON NO ===
🔴 KHONG cong gop 2 cot thanh mot so "hien hanh": de-xuat va duyet la 2 so co y nghia phap-ly
khac nhau (mot cai chua ai chot, mot cai da qua choke-point `ApplyApprovedValuesOnFinalize`);
tron lai la bia ra mot so khong ton tai trong ho-so nao.
Tong duyet chi cong phan DA co, va noi ro con bao nhieu dong chua chot.
=== 🔴 CON NO — O3 ve (1) "hang muc cho chon NHIEU" KHONG PHAI FE-ONLY ===
Spec ghi O3 = "N-ROW 0-migration". "0-migration" DUNG (unique 3 cot PlanId+SupplierId+
CatalogEntryId co tu Mig 71) NHUNG KHONG co nghia la 0 BE. Do that:
· KHONG co endpoint TAO line. Chi co `PUT /{id}/lines/{lineId}` = gan catalogEntryId cho
dong DA CO (Controller:121). Danh sach route day du: 0 route POST/DELETE line.
· Line duoc auto-sinh 1-dong-moi-NCC-trung-thau luc TAO PHIEU (`Features.cs:508-527`),
tien moi = tong bao gia DUOC CHON cua NCC do.
=> Muon 1 NCC co N hang muc phai TACH tien cua NCC do ra N phan — va KHONG CO quy tac nao
trong he thong noi chia the nao. Do la cau hoi TIEN, chua ai tra loi.
🔸 Tin tot: BE cau HD da nhan N dong san (`CreateContractFromSigningPlanFeatures:101-115`
nhan `LineIds` list, chi rao "moi HD 1 NCC"). Cai hep la FE dialog dung radio single-select
(`KhkkDetailPage:310`) — do la UI, doi duoc.
=== CON NO KHAC ===
🔴 CHUA soi mat tren prod 375/1366/1440 (5 lane NO-SCREENSHOT, gotcha #3) — moi ket luan la
DOC MA, khong phai da nhin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nguon: spec runs/2026-08-05-S174-khkk-style-audit/spec-khkk-style-dong-bo-05-08-2026.md
(ensemble /fable-clone frontend-designer 5 lane). Owner S175: "KHKK theo khuon PE".
Mirror 7 CAP file, sha256 khop 7/7. Build XANH ca 2 app, 0 error TS.
=== B1 panel Quy trinh duyet (KhkkWorkflowPanel) ===
X2 GO card-accent -> panel TRAN khuon PE (:345).
🔴 GO HAN CLASS, khong de border-0/shadow-none: .card-accent (index.css:112) nam NGOAI
@layer nen thang utility Tailwind v4 (gotcha #66).
- Tieu de h3 tran + bo icon-chip; so hieu WF padStart(2,'0') => v01 (PE :350).
- Banner ket-thuc-som violet -> EMERALD (PE :360-377). PE danh violet RIENG cho
"Duyet thay CEO"; dung violet ca 2 cho la tron 2 nghia vao mot mau.
- Chip PHONG vien-xam-nen-trang -> emerald dac (PE :406-410).
- Nut lucide -> glyph '✓ Duyet' / '← Tra lai' (PE :540); nut Xoa xam -> DO (PE :574).
- File dinh kem: +border-t pt-4 +h3 +nut <Eye> xem truoc. Route BE DA CO
ContractSigningPlansController.cs:231 (/view, cung hinh PE :317).
Nhan "Tai xuong" chuyen vao title= chu KHONG bo — giu chuoi do trong bundle
de phep verify prod con control-duong soi duoc.
- Lich su duyet: bo pill dem -> (n) trong ngoac; the bg-slate-50/60 -> bg-white p-3 text-sm;
moc-gio day phai bang justify-between.
🔴 Badge 2-nga -> 3-NGA: truoc day MOI thu khong phai Reject deu to emerald => "Tra lai"
(amber o PE) bi ve xanh nhu da-duyet, doc NGUOC nghia. toPhase da co san (khkk.ts:307).
- Lich su thay doi: BO GAP, render thang (PE PeDetailTabs.tsx:787-794).
🔴 BAT BUOC bo luon `enabled: showChangelog` cua useQuery: giu lai thi khoi luon hien
nhung query KHONG BAO GIO chay, va no hong duoi dang "Chua co thay doi nao duoc ghi."
— tuc trong y het phieu chua tung sua, KHONG phai trong nhu loi. Doi lay +1 request/phieu,
dung gia PE dang tra.
- Micro-copy: Huy / tuy chon; nhan O NHAP -> "Ghi chu" (PE :869).
🔴 CHI doi ten TRUONG — chuoi "Y kien cap duyet" o dong tro-giup la TEN THAT cua muc dich
den trong phieu (Section 5, Mig 26), doi no la tro nguoi dung toi mot muc khong ton tai.
- KHONG be chip "NS PRO"/"NS CCM" (2 co do mo quyen sua ngan sach per-goi-thau, khai niem
KHKK khong co). GIU moc chu-ky KHKK (cho KHKK tot hon PE).
=== B2 noi dung phieu (KhkkDetailContent) — 6 P0 ===
- 2 bang: table-fixed + <colgroup> + min-w-[880px] trong overflow-x-auto. Bang co gian
tu-do truoc day de cot tien nhay be rong theo do dai ten NCC => 3 cot so khong thang hang.
- O RONG -> em-dash MO (text-slate-300) gom vao helper orDash/moneyOrDash: truoc day em-dash
thua-huong dung mau chu that => luot bang thi "—" trong y nhu mot GIA-TRI.
moneyOrDash rieng vi formatVnd tra CHUOI "—" nen khong to mo duoc.
- empty-block -> text-sm italic text-slate-400 (3 cho).
- emoji 🔒 -> icon lucide <Lock>: emoji render khac nhau theo font he dieu-hanh va KHONG an
currentColor nen luon lech tong voi chu amber ben canh.
- thong nhat text-xs ca hang bang; +hover:bg-slate-50/60 cho <tr>.
=== B3 panel danh sach (KhkkListPanel + KhkkWorkListPanel + PipelineTreePanel) ===
- DAU COT: che-do CAY truoc day di thang vao than, khong tieu-de/so dem => cot "cut dau",
lech ca voi man Thao tac (Tier header) LAN voi chinh che-do phang (co dem o chan).
Mirror khuon Tier.
- Gop 2 o tim chong nhau: +prop hideSearch cho PipelineTreePanel (MAC DINH giu nguyen —
4/6 host kia khong truyen, 0 doi hanh vi). O trong cay KHONG bao gio dung toi vi o ngoai
di server-side va go vao la chuyen han sang flatMode => cay bien mat.
- Thang chu 4 nac (13/11/10/9) -> 3 nac chuan 13/11/10.
- 🔴 WCAG: text-slate-400 = 2.56:1 tren nen trang, DUOI nguong AA 4.5:1 — nang len
slate-500 (4.76:1) cho 7 dong MANG THONG TIN (6 note/empty-state + 1 so dem).
GIU slate-400 cho 2 cho TRANG TRI (icon Search, dau cham phan cach) — WCAG khong doi
tuong phan cho noi dung trang tri.
- Nut phan trang ky-tu tran ←/→ -> icon ChevronLeft/Right + aria-label: mui ten dang chu
doi hinh theo font he dieu-hanh va KHONG co ten cho trinh doc man hinh.
- Chip nhom: inline + co chu khac dong chua => hop chip cao khac chu ben canh nen tut
baseline; inline-flex items-center leading-none ghim thang hang.
=== B4 host (KhkkCreatePage) ===
- Grid 260/320 DA land tu dot A — chi con COMMENT stale ghi 300px, da va.
- 🔴 Va comment SAI SU THAT (L3 H-5): comment khai Panel 1 co "nut hanh-dong dinh day"
nhung grep -c Button KhkkWorkListPanel = 0 (control-duong PeListPanel = 8). Do la mo ta
CUA PE be nguyen sang => tu khai mot thu chua dung.
=== 2 component DUNG CHUNG — mo rong kieu MAC-DINH-GIU-NGUYEN ===
AttachmentPreviewDialog +basePath? (mac dinh /purchase-evaluations) => 6 call-site PE
KHONG phai sua dong nao (module da golive; bat sua het call-site cu = tu de rui-ro o cho
dang chay tot). PipelineTreePanel +hideSearch? y het. Da verify: 0 call-site PE bi dong.
=== CON NO ===
🔴 CHUA soi mat tren prod 375/1366/1440 — ca 5 lane deu NO-SCREENSHOT (dev-rig chan:
chi :8080 LISTENING, API :5443 khong co, trang sau ProtectedRoute — gotcha #3). Moi ket
luan la DOC MA, khong phai da nhin.
Dot C3 (Field to brand cho MOI value) + dot D (O3/O4/O6/O7/O8) chua lam. O3 con cho owner
xac nhan cach doc "sum tong" — do la tien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nguon: /fable-clone frontend-designer ensemble 5 lane (wf_016b1bed-b85, 5/5 xong, 0 chet,
755K tok). Spec day du: runs/2026-08-05-S174-khkk-style-audit/spec-khkk-style-dong-bo-05-08-2026.md
=== DOT A — LOI THAT (khong phai style) ===
A1 🔴 Nut "Tai xuong" file dinh kem KHKK BI 401 — lane L1 bat, lead verify.
Cu: <a href="/api/contract-signing-plans/.../download"> TRAN.
JWT chi gan boi interceptor axios (lib/api.ts:18); the <a> KHONG di qua axios => request
toi [Authorize(Policy="KeHoachKyKet.Read")] (ContractSigningPlansController.cs:222-223)
di TAY KHONG => 401, nut khong tai duoc.
Moi: ham downloadAttachment() dung khuon PE (PeWorkflowPanel.tsx:327-342) —
api.get(responseType:'blob') -> createObjectURL -> click ngam -> revoke.
A2 🔴 REGRESSION DO CHINH LEAD GAY RA TRONG PHIEN NAY (lane L3 bat).
Man Thao tac dung <main className="hidden ... lg:block"> => duoi lg chi con panel-1,
KHONG co form => MOBILE KHONG TAO DUOC PHIEU (bam phieu xong khong co cho bam "Tao").
Va 2 ve: (1) <main> bo hidden, hien moi kho; (2) mobile xep DOC voi panel-1 chot tran
14rem — khong chot thi danh sach dai day form roi khoi man, dung cai ngo cut vua va.
A3 Tra no da khai voi owner: trang chi tiet KHKK tren mobile mat nut Gui duyet / Xoa phieu.
Giu KhkkWorkflowPanel o DUNG lg:hidden — desktop van sach (panel-3 lo), mobile thao tac duoc.
=== OWNER CHOT VONG NAY ===
O2 "Dung bo chu sap di" — go SoonChip().
Lane L4 do: KHONG giai doan nao con dung nghia "sap" (ca 4 deu fetch API that);
SoonChip KHONG export, 8 hit noi bo => 0 host ngoai phu thuoc.
🔴 Go != "thay het bang 0". BA ca, BA cach xu dung nghia:
· 2 ca dem-duoc -> luon in SO (ke ca 0)
· ca KHOA-QUYEN -> BO TRANG (in 0 la NOI DOI: co the day phieu, chi la khong duoc
xem, va cung khong duoc phep goi API de biet). O khoa + title du.
· ca FAILED (dau —) -> GIU NGUYEN ("khong tai duoc" khac "co 0 phieu", dung gop)
Cau-4 "vay thi bo luon tu choi ~ tuong tu PE" — go nut TU CHOI khoi KhkkWorkflowPanel.
PE co code tu-choi nhung THUC TE khong hien (BE go khoi nextPhases + FE loc, UAT S60).
🔴 Chi go DUONG BAM o UI. May BE (KhkkTransitionAction.Reject, phase TuChoi=99) GIU
NGUYEN: phieu cu da tu-choi van hien dung, mo lai sau chi la them nut.
B4 Grid man Thao tac 300/340 -> 260/320 dung so cua PE (PurchaseEvaluationWorkspacePage:80).
=== HAI GIA-DINH CUA LEAD BI LANE BAC (lead da tu verify, lane DUNG) ===
- O3 hang-muc N-dong KHONG can migration. ContractSigningPlanLineConfiguration.cs:37-38
unique index 3 COT (PlanId, SupplierId, CatalogEntryId) => 1 NCC DA duoc phep N dong x
N hang muc tu Mig 71. May rong san, chi UI hep. Lead tung bao owner "nang nhat, can
migration" — SAI, da dinh chinh.
- O4 shopdrawing dinh kem = FE-only 0-BE (rail dossierItemId da land du 6 tang tu Mig 69).
Verify: build 2 app PASS; mirror SHA256 4/4 cap KHOP; control-am: <SoonChip=0 · "> Tu choi"=0
· a-href=0; control-duong: responseType=2 + downloadAttachment=2 CA HAI app.
🔴 CHUA co screenshot — ca 5 lane deu khong chup duoc (dev-rig chan: chi :8080 LISTENING,
API :5443 khong co, trang sau ProtectedRoute — gotcha #3). Phai soi that 375/1366/1440 tren
prod sau deploy truoc khi tuyen PASS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. Tang duoi man Thao tac: DANH-SACH-TRANG 3 trang-thai — anh: "cho thao tac chi hien tra
lai va nhap, tu choi thoi nhe" => {DangSoanThao, TraLai, TuChoi}. `ChoDuyet` CUNG bi loai
(khac vong-3 chi bo `DaDuyet`): phieu dang trinh duyet thi nguoi soan khong thao tac gi
duoc nua.
🔴 Doi tu LOAI-TRU sang DANH-SACH-TRANG la CO CHU DICH: loai-tru (`!== DaDuyet`) se tu
dong NHAN moi phase them ve sau; danh-sach-trang thi phase moi phai khai TUONG MINH moi
hien. O man chi nen chua viec CON SUA DUOC, im lang nhan them la hong => chon ve chat.
Tieu-de doi thanh "KHKK · nhap / tra lai / tu choi" — noi DUNG tap dang hien, de nguoi
dung biet ngay vi sao phieu vua gui duyet "bien mat".
2. Cay toan trinh trong man Danh sach KHKK: GO vo card — anh: "style cho no giong nhau dong
bo nhe, style ben KHKK co cai vien ben ngoai".
🔴 GOC RE + BAI HOC: truoc do da truyen `className="border-0 shadow-none"` voi Y DINH tat
vo card, nhung `.card-accent` khai o `index.css:112` NAM NGOAI `@layer` nen THANG utility
cua Tailwind v4 => lenh tat bi VO HIEU IM LANG. Day dung la gotcha #66 cua chinh repo
(rule tho ngoai @layer thang utility). Fix = GO CLASS theo dieu kien (`!hideHeader &&
'card-accent lg:sticky lg:top-4'`), KHONG de bang utility.
`hideHeader` nay mang dung nghia "dang NHUNG trong panel khac" => bo ca vo card lan sticky
cho lien mach voi host, y nhu cay ben Duyet NCC. 6 host con lai khong truyen => nguyen ven.
Verify: build 2 app PASS; mirror SHA256 3/3 cap KHOP; grep xac nhan `card-accent` nay nam
sau dieu kien `!hideHeader`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. Ho thu: GO `PipelineTreePanel` khoi `/inbox` — anh: "cho hop thu vut panel nay vao lam j,
tap trung vao KHKK thoi". Ho thu = hang-doi-viec-cua-toi, cay toan trinh = ban-do-toan-he
=> sai the loai. Cho trong nay tra ve dung vai: cho quy trinh duyet cua HD dang chon.
🔸 HAI trang GD3/GD4 con lai (`MyContractsPage` · `HardCopiesPage`) GIU NGUYEN — anh chi
dich danh Ho thu, khong noi go het; khong tu suy rong. (Lineage: panel vao day @S168
GD3-D theo owner slot (63) + "mo rong = ca 3 trang"; nay owner thu lai DUNG trang nay.)
2. Panel KHKK: BO nut "+ Them moi". Man nay KHONG co duong "tao tu so 0" — ke hoach LUON
sinh tu mot phieu Duyet NCC, ma cac phieu du dieu kien da nam nguyen o tang tren => nut
chi xoa form chu khong tao them duoc gi. Khac CO CHU DICH voi `PeListPanel`.
3. Tang duoi CHI giu ke hoach DANG XU LY — an phieu `DaDuyet` (anh: "da duyet bo di luon").
🔴 KHAI GIOI-HAN: loc o FE vi endpoint chi nhan `phase` DON, khong co tham so "loai tru"
=> (a) KHONG dung `total` cua server lam so hien thi (da gom phieu DaDuyet) — dem tu mang
da loc; (b) cua-so 30 phieu moi nhat co the TOAN DaDuyet => tang duoi rong du con phieu
nhap cu hon. Chap nhan o UAT; muon chuan thi BE them tham so loai-tru.
Doi luon dong chu "x/total" -> "Da an ke hoach Da duyet": ti so cu doc thanh "dang cat
bot" trong khi that ra la "dang loc bo".
4. Nhom duyet + Quy trinh duyet GAN CUNG theo leaf menu — anh: "gan cung luon theo tung nhom
ko cho User dieu chinh cho nay luon".
- `approvalGroup` khong con la state, DAN XUAT thang tu `?group=n`. Bo luon khoi dong-bo
`lastPresetGroup` + ham `firstFreeGroup()` (chung chi ton tai de tu nhay nhom = dung
hanh-vi vua bi cam).
- Quy trinh: match theo ma tu NGUON BE, khong suy tu man hinh — `DbInitializer.cs:609`
`var code = $"KHKK-N{n}"`. Khong khop => CHAN tao + noi ro thieu ma nao, TUYET DOI
khong tu roi ve quy trinh nhom khac (gan nham = phieu di sai duong duyet).
- UI: hien gia-tri co dinh, CO Y khong dung `<Select disabled>` (o xam van trong nhu
"bam duoc nhung dang khoa" va van trong tab-order).
5. Panel giua: GO Section 5 "Y kien cap duyet" (anh: "bo luon cai nay ra luon"). So-do
Buoc/Cap + chu ky da song o Panel 3 => de ca hai la ke mot chuyen hai lan tren cung man.
DTO giu nguyen (`levelOpinions`/`workflowSteps` panel 3 van dung) — chi bo cho VE.
Verify: build 2 app PASS; mirror SHA256 3/3 cap KHOP.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anh bac ban truoc: "Bo phan nay di cho duyet NCC da golive va da tot roi, y tao la dang
muon may lam giong giao dien thao tac cua Duyet NCC".
REVERT (quan trong nhat)
- `PurchaseEvaluationWorkspacePage` x2 app tro ve DUNG BYTE cua 1b3bcaa (grid 2 panel,
0 import, 0 markup thua). Verify bang sha256 vs `git show 1b3bcaa:` + control-duong
grep 0 hit ten panel trong pages/pe/.
- Bai hoc ghi vao dau file panel: "lam giong man X" != "sua man X". Ban dau doc anh 2/3
(anh khoanh o man PE) thanh "chen them cot vao man PE"; anh 7 moi lo cho dat that la
cot TRAI cua man Thao tac KHKK.
DI DOI
- `components/pe/PeKhkkSidePanel.tsx` -> `components/khkk/KhkkWorkListPanel.tsx` (git mv,
giu lich su) — no phuc vu KHKK chu khong phai PE.
MAN KHKK — THAO TAC (`/khkk/create`, 8 leaf nhom) doi tu trang-giay-cuon sang HOST 2-PANEL
- Khuon be nguyen tu `PurchaseEvaluationWorkspacePage`: header 1 dong + grid
`lg:grid-cols-[300px_1fr]` + Panel1 danh sach + nut dinh day + Panel2 noi dung.
- Panel 1 = 2 TANG dung anh anh khoanh:
tang TREN "Cot danh sach Duyet NCC" — PE da duyet CHUA lap KHKK, gom theo DU AN;
bam = nap thang vao form tao (tao luon KHKK).
tang DUOI "Cot KHKK da tao" — bam = XEM tai cho, read-only.
- 🔴 KHONG re-direct o BAT KY duong nao (anh chot "cho hien phieu do luon, chi view la dc,
ko re-direct ve phieu goc"): panel nhan CALLBACK onPickPe/onPickPlan thay vi tu navigate.
Van con nut phu "Mo trang day du" cho ai muon roi man — la lua chon, khong phai ep buoc.
- Section 1 cua form doi tu DANH SACH CHON thanh KHOI XAC NHAN phieu dang chon — danh sach
da o Panel 1, de 2 cho chon cung 1 thu = 2 nguon su that tren cung mot man.
- Tao xong KHONG navigate('/khkk/:id') nua ma mo luon phieu vua tao o Panel 2 + tra form ve
trang. Cung tinh than "khong roi man".
- Nut "Them moi" (dinh day Panel 1) = tra Panel 2 ve form trang.
- Gate `KeHoachKyKet.Read` giu nguyen trong panel (gotcha #85); thieu quyen thi NOI RA,
va nut hanh-dong VAN hien de man khong thanh ngo cut.
Verify: build 2 app PASS; mirror SHA256 3/3 cap KHOP; PE workspace 0 du luong (byte-exact
vs 1b3bcaa) + control-duong grep 0 hit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anh chot qua 6 anh khoanh do + 4 cau tra loi. 7 chinh, mirror SHA256 identical 8/8 cap.
Man DANH SACH KHKK (KhkkListPage / KhkkListPanel)
- Giu DUY NHAT header trang "Ke hoach ky ket HD / Giai doan 2".
- Bo o loc "Tat ca nhom": may loc GIU NGUYEN (query + stage2Content), nhom nay den
DUY NHAT tu URL ?group=n (8 leaf menu trai). Go luon prop onGroupChange (noUnusedLocals).
- Bo thanh "Cay toan trinh - N goi" qua prop MOI `hideHeader` opt-in tren PipelineTreePanel.
6 host con lai (PE / GD3 / GD4 / Inbox) KHONG truyen => hinh cu nguyen ven.
Nut "Hien/An cay" (mobile) song TRONG thanh vua an => hideHeader ep than cay `flex`,
neu khong `<lg` mat trang cai cay.
Man CHI TIET KHKK (KhkkDetailPage / KhkkDetailContent)
- Go khoi "Quy trinh duyet" (so-do Buoc/Cap + HANH DONG + 2 lich su) — day la BAN SAO
THU HAI, ban chinh o Panel-3 man danh sach. Anh: "bo het, no se hien thi o Menu duyet".
KHAI HE QUA: `<lg` dieu huong thang vao trang nay => mobile hien khong con Gui duyet /
Xoa phieu. Mo lai = boc `lg:hidden`.
- Section 5 "Y kien cap duyet" doi sang DUNG style Duyet NCC (PeDetailTabs.tsx:705-774):
card `rounded-lg p-3` + h4 ten cap + chip trang thai + y kien text-sm + chan ngay
border-t; thay o phang 1 dong text-[11px]. GIU o "Chua ky" (PE khong co) — vang o thi
nguoi doc khong biet con thieu chu ky cua ai.
Man TAO KHKK (KhkkCreatePage)
- Doi sang khuon "Tao phieu Duyet NCC moi" (PeWorkspaceCreateView): 1 card + divide-y,
thanh dau ke LUONG, thanh cuoi hint do liet ke DICH DANH truong con thieu thay vi chi
lam xam nut. Nut "Luu nhap" -> "Tao ke hoach".
- Ve "tao xong sang chi tiet" VON DA DUNG tu S161 (onSuccess -> navigate /khkk/:id);
dot nay KHONG dung luong do, chi doi hinh.
Man DUYET NCC — THAO TAC (PurchaseEvaluationWorkspacePage + PeKhkkSidePanel MOI)
- Them cot 3: 1 cot 2 TANG 50/50 — tren = PE da duyet CHUA lap KHKK gom theo DU AN,
duoi = KHKK da tao. Ti le 50/50 bang `min-h-0 flex-1` ca hai tang.
- 0 ENDPOINT MOI: dung lai approved-pe-awaiting-plan + contract-signing-plans.
"Chua co KHKK" = existingGroups RONG — tu K2/S164 BE KHONG con loc ho, loc o FE.
- GATE #85: ca 2 cua gac bang policy `KeHoachKyKet.Read` trong khi day la man PE =>
gate bang DUNG key policy cua endpoint (KHONG OR key menu Khkk_*), thieu quyen thi
NOI RA thay vi de cot trang (cot trang doc thanh "chua co gi").
- Cot chi moc tu `xl`; o `lg` bo cuc giu DUNG 2 panel nhu cu.
Con lai (chua lam, da khai voi anh): bam PE o tang tren moi mo /khkk/create chu chua
chon san dung phieu do — can them ?peId= vao man tao.
Verify: build 2 app PASS; mirror SHA256 8/8 cap KHOP; byte-level grep bundle xac nhan
chuoi moi co mat ca 2 app (khong tin dong "built" — bai gotcha #77).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dua man Ke hoach Ky ket HD ve dung lop kien-truc ma GD1 (PE) va GD3 (HD) dang
dung: 1 man 3-panel h-[calc(100vh-4rem)] grid [400px_1fr_360px], thay cho
list 2-panel + detail trang rieng co workflow xep chong. GD2 la mat-xich lech
duy nhat trong 4 giai-doan truoc dot nay.
BE (7/7):
- DetailDto +5 field: WorkflowVersion / EndedByLevelFinalize / FinalizeStepName
/ FinalizeLevelName / Approvals. 2 field Finalize* KHONG phai field append —
ContractSigningPlan chi co co bool, phai DAN XUAT (lam in-memory tu cay
workflow da nap, thay vi port 3 subquery cua PE).
- ContractSigningPlanApprovalDto de moi (truoc do 0 hit toan repo).
- StepDto +DepartmentName (nuoi chip phong panel 3 + header nhom Section 5).
- GET {id}/changelogs — policy KeHoachKyKet.Read.
- POST {id}/approval-attachments + enum +ApprovalAttachment=3 (append-only).
- approvals[] loc bo Decision=Pending de so (N) cung nghia voi (N) cua PE.
- KHONG chep khuon authz cua PurchaseEvaluationsController: file do la mot lo
gotcha #82 (class [Authorize] tran + changelogs khong policy).
FE (x2 app, SHA256 byte-identical 8/8):
- components/khkk/{KhkkListPanel,KhkkDetailContent,KhkkWorkflowPanel}.tsx;
KhkkWorkflowPanel doi tu pages/ sang components/.
- Panel 2: 5 Section danh so, Section 5 "Y kien cap duyet" duoi cung — khoi nay
DOI TU panel 3 sang panel 2 (owner khoanh do trong anh); luoi 2 cot + meta
"x/y cap da duyet · N NV tham gia" thay list phang 1 cot.
- Panel 3: banner ket-thuc-som, chip ten phong, badge Duyet thay CEO, lam mo
buoc sau finalize, khoi File dinh kem khi duyet, Lich su duyet, Lich su
thay doi. Chua chon phieu -> hop dashed y PE (QD owner).
4 mitigation cuong-hanh (owner chot panel 1 = PipelineTreePanel; chon HINH
khong phai chap-nhan VO):
- M-1 fallback !canPe -> danh-sach KHKK phang. Khong co no, user chi co quyen
KHKK (vd nguoi duyet nhom N5) MAT TRANG danh-sach du API van cho doc —
display-layer khoa authz-layer dang mo, mat con lai cua #82.
- M-2 onOpen thanh callback tu host; mac-dinh giu navigate cu => 0 hoi-quy
trang PE/GD3.
- B1 stage2Content di CAP voi stage2Count qua conditional-spread:
khong truyen => stage2 van nhan groups => trang PE GIU NGUYEN 8 ngan N1-N8.
- R-11 search/loc di server-side, KHONG loc-tren-cay; giu dong canh-bao
"Cay tai 200 phieu moi nhat".
Chong hoi-quy da do: build 3/3 PASS · acceptance tinh 20/20 x 2 app ·
mirror SHA256 8/8 · 0 hit path cam (components|pages)/(pe|contracts) ·
KHONG ghi filter/view/group vao URL (chong tai-sinh bug S155 sidebar sang
nham leaf) · giu set-state-in-render :97-101 (dung khuon React, refactor
thanh useEffect se tai-hien va-5).
Con no, khai thang:
- E-7 bang "cot nao chet, chuyen di dau" (8 cot + 6 KpiCard -> card 400px)
THIEU: lane FE chet o luot tra ve, ly-do bo cot mat theo. Phai soi diff
bang mat truoc UAT, neu khong dung ca M-7 mat-chuc-nang-im-lang.
- Test 643/644. 1 FLAKY Create_DifferentEvaluationType_DoesNotAffect_Other
(full-suite 2->1->1, chay rieng 0/0/0; PeWorkflowAdminTests tao
new SqliteDbFixture() rieng x6 test). Baseline tren HEAD sach CHUA do
(git worktree fail duong-dan Dropbox) => CHUA duoc noi "flake co san".
- 5 acceptance RUNTIME cho UAT: approvals co dong Tra lai + ten nguoi (DB dev
0 hang KHKK) · UAT fe-user:8080 an-danh · deep-link fe-admin:8082 (menu
KHKK bi an trong ADMIN_HIDDEN_KEYS) · negative-control go fallback canPe ·
khoi file tu-an khi rong.
- P3-12 4 return-mode = HOAN, khong phai buoc-khac: tien-de "KHKK 3 tram ngan"
do lai la SAI (DbInitializer:554 = 3 Buoc x <=2 Cap, seed tu ghi "sao tu
workflow Duyet NCC prod").
Spec + 6 artifact phan-bien: .claude/workflows/runs/2026-08-04-S171-khkk-ui-mirror-pe/
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gate PASS 0-blocker 6/6 (sub-reviewer-gate-k5.md): 8 ngan nhom tu KHKK_APPROVAL_GROUPS
+ khkkGroupMenuLabel (single-source K4a), phieu vao ngan theo approvalGroup + fallback
"(chua phan nhom)" — bat-bien Σ bao dam bang CAU TRUC (1 mang nguon / 1 phep phan hoach
/ 1 cho dem); HIEN DU 8 ke ca rong; route ngan khop URL K4b /khkk/list?group=n; GD3/GD4
regression chung-duoc-la-khong (union pe.contractId giu, cho-cam lines[].contractId
danh dau — no K7 Line.ContractId ghi-luon). SHA-pair x2 byte-identical (diff rong).
tsc -b x2 = 0 (gate tu chay) + npm x2 PASS.
Treo khong chan (gate MINOR 1-6): chu "SAP" tren ngan rong (UX owner) · a11y button-
trong-summary (khuon moi) · FE 0 ha tang test (console.warn dev-only) · toKhkkLeaf x2
lan/phieu. UAT ngay dau: prod 0 phieu KHKK => 8 ngan "SAP" rong o moi goi = DUNG SPEC.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
UAT anh Kiet FDC 07-28 "cho nay lay dia chi len luon giup anh". Do lai: co-che
auto-fill Dia diem tu Project.Location DA WIRE tu S59 va van chay dung (BE
ProjectFeatures.cs:51 project Location -> FE types master.ts:97 -> onChange
SearchableSelect). O trong vi DU AN THIEU DU LIEU: prod 57/62 du an co
Location NULL, gom ca BVN01 trong anh chup. Chi 5 du an co dia chi (CAL01,
MIDEA01, SAM01, TLB01, ZOTE01) - dung 5 dong co dia chi trong Excel S55.
=> Khong sua co-che (khong hong). Them dong canh bao amber khi da chon du an
ma du an do khong co Location: noi thang ly do + tro cho bo sung (Danh muc Du
an -> Dia diem), thay vi de o trong im lang trong nhu he thong hong.
4 file, SHA256 mirror fe-user/fe-admin giu IDENTICAL. npm build 2/2 PASS.
Backfill 57 dia chi con lai = can du lieu tu anh/FDC.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bug UAT anh bao 2026-07-27 (anh chup man hinh): bam muc "Danh sach" thi menu lai
to sang muc "Da duyet".
Goc: `phase` vua la BO LOC trang thai tren trang danh sach (nen nam trong
TRANSIENT_QUERY_KEYS tu fix 2026-05-08 — de user loc/chon dong khong mat highlight),
vua VUA-MOI tro thanh DANH TINH dieu huong cua muc "Da duyet" (`?type=N&phase=7`,
dot 2 hom nay). `queryMatches` strip `phase` khoi CA HAI ve => "Danh sach" ([type])
va "Da duyet" ([type] sau khi mat phase) thanh KHONG PHAN BIET DUOC => URL `?type=1`
khop ca hai, muc render sau thang phan hien thi.
Cung mot tham so khong the vua la thu-bi-bo-qua vua la thu-dinh-danh.
2 sua, ca 2 app (Layout khong phai file mirror byte-identical nen sua rieng tung ben):
- `queryMatches`: transient chi duoc bo qua khi MENU DICH KHONG tu ghim key do
(dich ghim => so khop NGHIEM; khong ghim => giu hanh vi cu).
- Route "Da duyet" them `&view=approved` = khoa DINH DANH rieng (trang bo qua no,
chi `phase=7` loc that). Can thiet vi URL muc "Da duyet" TRUNG HET URL ma user tu
loc trang thai tren Danh sach => khong the phan biet bang URL neu thieu khoa nay.
Kiem 4 ca duong di (khong chi ca anh bao):
?type=1 -> Danh sach SANG, Da duyet TAT
?type=1&phase=7&view=approved -> Da duyet SANG, Danh sach TAT
?type=1&phase=7 (user tu loc) -> Danh sach VAN SANG (giu fix 2026-05-08)
?type=1&deleted=1 -> Da xoa SANG, con lai TAT
tsc 0 loi x2 · npm build PASS x2.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dong ca UAT goc (Tra Sol): "lo bam sai cai goi thau, quay lai khong duoc, phai
xoa thi no moi ko co luy ke len". Dot 1 (b1bae77) da dung NEN (co F6 + lenh sua
quy trinh tai cho); dot 2 la phan nguoi dung cham vao.
- App: PeSoftDeleteFeatures.cs MOI
- DELETE /api/purchase-evaluations/{id}/by-approver — 3 rao: phase ChoDuyet
^ dung luot actor ^ matchingLevel.AllowApproverDelete (PER-NGUOI, khong g.Any)
- GET /api/purchase-evaluations/deleted — IgnoreQueryFilters() KEM .Where(IsDeleted)
(IgnoreQueryFilters GO filter chu khong DAO — thieu Where la liet ke ca phieu song)
+ tai lap khoi IDOR cua list hien hanh
- ghi vet ChangelogAction.Delete + ly do
- Api: 2 endpoint moi. 🔴 by-approver CO Y KHONG co [Authorize(Policy)] — do tren DB:
moi vai dang lam nguoi duyet (Procurement/CostControl...) deu CanDelete=0 va khong
seeder nao nang co do => gan policy la 403 voi dung nguoi can dung, ma FE gate cung
quyen nen khong thay nut khong thay loi (gotcha #44). Quyen = 3 rao handler; co F6
la admin tick dich danh tung nguoi => chat hon quyen theo vai.
- Dto: ApprovalWorkflowOptionsDto + AllowApproverDelete. Thieu field nay thi FE gate
luon sai => nut KHONG BAO GIO hien, ma BE lan FE deu build sach (reviewer bat).
- Domain/Infra: 2 menu key Pe_{code}_Approved / _Deleted (KHONG vao MenuKeys.All =>
giu 54/216) + seed tree + labelBackfill "Duyet" -> "Dang duyet" + 2 danh sach permission
- FE x2 app: nut Xoa trong PeWorkflowPanel (md5 mirror khop) + dialog ly do + Layout
regex/route cho 2 muc moi + man "Da xoa" chi-xem
- Tests: +11 (562 tong, 0 fail). T27 khoa CA LOP loi entity-co-co-ma-DTO-khong-co
(fault-inject xac nhan: go field ra => T27 do ngay)
Review: 12 FLAG, chot chan H1 (DTO thieu field) da va. Deploy KHONG co migration
nhung CO seed menu => phai restart API.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nen cho tinh nang "xoa phieu PE o man duyet" (UAT: phieu bam sai goi thau dang
an luy ke). Dot 1 lam NEN, dot 2 moi lam nut xoa.
Vi sao phai lam nen truoc: phieu dang chay pin ApprovalWorkflowId va doc workflow
SONG theo Id do. Moi POST tao workflow moi = Id MOI, nen tick co tren ban moi
KHONG BAO GIO toi duoc phieu dang treo. Phai co lenh sua-tai-cho giu nguyen Id.
- Domain: ApprovalWorkflowLevel + AllowApproverDelete (F6, per-NGUOI) + Mig
20260727033522_AddPeAllowApproverDelete (AddColumn bit default false, no new table)
- Application: UpdateAwDefinitionCommand + Validator + Handler
- 2 phep thu: (T1) khung khong doi - Order tung row bat bien
(T2) luat choi khong doi - cam CeoApprovalThreshold, AllowApproverFinalize,
Code, ApplicableType => 409 bao tao ban moi
- diff theo Level.Id (CAM delete-then-insert: no FK Restrict + mo coi LevelOpinion)
- guard 2 tang chu ky: tang 1 chu ky tren ho so CON SONG; tang 2 chu ky CON LUU
(cha xoa mem hoac opinion xoa mem) - ca hai deu CHAN, khong xoa cung
- purge chi khi ho so cha KHONG CON ROW; boc transaction (ExecuteDelete tu commit)
- KHONG tang Version, KHONG dung IsActive, KHONG doi Id
- Api: PUT /api/approval-workflows-v2/{id} + [Authorize(Policy="Workflows.Create")]
- Infra: message 403 in TEN NGUOI thay GUID tran
- FE: Designer tach "Sua tai cho" (PUT) vs "Tao phien ban moi" (POST) + o tick F6
- Tests: +21 (551 tong, 0 fail) - gom F12 chung minh phieu dang chay nhan thay doi ngay
Review: 14 FLAG, 3 HIGH da va (ExecuteDelete ngoai transaction / purge xoa cung
chu ky phieu xoa mem / endpoint khong co day FE goi toi).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nút đính kèm 'Bảng so sánh giá' (GeneralAttachmentsSection) trước gate theo readOnly MÀN HÌNH: màn Danh sách mở phiếu readOnly=true nên ẩn nút xóa/tải ở MỌI phase, kể cả Trả lại (TraLai=98) vốn đã nằm trong isEditablePhase. Đổi gate sang (isAdmin || isDrafter) && isEditablePhase(phase), chỉ tách riêng khối đính kèm — HoSoLink + winner giữ theo readOnly màn hình (không mở nhầm); SupplierAttachmentsCell giữ nguyên carve-out approverEditMode (Mig 28 F3); BE không thêm guard (giữ S78 approver-upload). Mirror fe-user + fe-admin. tsc -b + vite build sạch 2 app.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hai chỉnh từ buổi UAT anh Kiệt FDC (S117):
1) Fix hiển thị lũy kế "duyệt rồi không bắt" — root cause là DISPLAY-LIE, không phải
lũy kế bug. endsBeforeCeo cho phiếu CHƯA DaDuyet nay POINTER-AWARE: chỉ báo "kết
thúc trước CEO" khi con-trỏ còn ở/trước cấp finalize. Phiếu đã đi QUA cấp finalize
(approver bỏ tick "Cấp này KẾT THÚC" → trình tiếp CEO) → false: sơ đồ hết làm mờ
Bước CEO + hết nói dối "Kết thúc tại Cấp X" khi phiếu đang chờ CEO. 3 site (detail
in-memory + list/inbox EF-subquery). Filter lũy kế GIỮ nguyên (đúng nguyên tắc
"duyệt rồi mới bắc"). BE-only — FE là consumer thuần nên tự render đúng.
2) Feature "duyệt theo KHOẢNG Min–Max": khi PRO có cả Min & Max, người duyệt cấp cuối
chọn CẢ HAI (radio "PRO — cả Min và Max", source=ProMinMax) → Min ở ApprovedPriceAmount
+ Max ở ApprovedPriceMaxAmount (Mig 66 AddColumn nullable, no backfill, phiếu cũ
nguyên vẹn). Bất-biến Max⟺ProMinMax, Min≤Max (guard ở service + validator). HĐ.GiaTri
KHÔNG đọc giá chốt (= SUM báo giá NCC được chọn) → range thuần record-of-decision
hiển thị, 0 tác động HĐ. FE 2-app SHA-mirror (Panel + DetailTabs).
Test 495 → 501 (+1 pointer-aware repro, +5 ProMinMax). Cả 2 điều tra qua /fable-real
engine-đắt (investigator-codebase deep-pass) theo yêu cầu anh Kiệt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Bug: phiếu có cấp AllowApproverFinalize nhưng approver opt-out → lên CEO thật + CEO duyệt, nhưng sơ đồ báo sai "kết thúc tại CCM, không qua CEO" + làm mờ Bước CEO (endsBeforeCeo tính theo CONFIG workflow)
- Fix: +field runtime EndedByLevelFinalize (set true CHỈ ở nhánh level-finalize service) → EndsBeforeCeo cho phiếu DaDuyet đọc runtime thay config; ChoDuyet giữ heads-up config
- BE: entity + EF config + service + 4 projection (detail/List/Inbox/ListApproved) runtime-aware
- Mig 60 AddPeEndedByLevelFinalize + backfill (dò marker "Duyệt KẾT THÚC tại" set phiếu finalize-sớm cũ; ccm-delegation không match → giữ false)
- FE: gate làm-mờ finalizeFlowStep theo endsBeforeCeo (2 app SHA-mirror b7abbc2f)
- Test 440 PASS (repro peFinButWentCeo: config-finalize + EndedByLevelFinalize=false → false) + reviewer PASS 0-issue
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fix 1: badge cấp-sau-finalize "không trình tới" → "Kết thúc tại {finalizeLevelName}" (động, hiện "Kết thúc tại CCM") + tooltip template-literal
- Fix 2: đảo default checkbox "Cấp này KẾT THÚC quy trình" opt-out→opt-in (useState true→false + reset onSuccess) — mặc định TRÌNH TIẾP CEO, tick mới KẾT THÚC; helper text reword
- FE-only, mirror 2 app SHA-identical, no BE/mig/test; workflow implement+review PASS 0 issue
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug (anh Kiệt/team, screenshot): drafter=PRO vẫn điền được giá đề xuất Min/Max SAU khi cấp trên (chị Trà) đã duyệt — "duyệt là fix".
Root cause: UpdatePeSuggestedPricePro/CcmCommandHandler cố ý KHÔNG ràng Phase (Mig 54 "chỉnh như tài liệu sống") → sửa được mọi phase kể cả DaDuyet.
Fix:
- BE: +phase-guard — non-admin chỉ sửa giá đề xuất PRO/CCM khi phiếu ở Nháp (DangSoanThao) / Trả lại (TraLai); đã gửi duyệt (ChoDuyet) / đã duyệt (DaDuyet) / từ chối → ConflictException. Admin override (sửa-sai). Role-gate vẫn chạy trước.
- FE (2 app): SuggestedPriceRows gate canEditPro/Ccm thêm && isEditablePhase(ev.phase) → ẩn ô sửa (read-only) khi phiếu đã gửi duyệt. Đồng bộ BE.
- Test: +4 (PRO ChoDuyet->Conflict no-mutate, PRO DaDuyet->Conflict, Admin DaDuyet->override-set, CCM DaDuyet->Conflict); sửa SeedPeAsync default->DangSoanThao (spec change). 436->440 PASS.
Delete phiếu sau duyệt: ĐÃ chặn sẵn (FE nút Xóa chỉ hiện DangSoanThao + BE delete-guard block non-draft) — không cần sửa.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Go-live request anh Kiệt FDC (Zalo "user chưa tự đổi pass được hả em"): trước đây chỉ admin
Reset hộ (POST users/{id}/reset-password), user KHÔNG tự đổi được. Thêm self-service:
- BE: ChangePasswordCommand + POST /api/auth/change-password ([Authorize]). Lấy user qua
ICurrentUser.UserId; UserManager.ChangePasswordAsync verify mật khẩu HIỆN TẠI; sai →
ValidationException field CurrentPassword "Mật khẩu hiện tại không đúng."; rule mật khẩu mới
≥ 12 + khác mật khẩu cũ (validator); sau đổi vô hiệu refresh token (mirror ResetPassword).
Qualify SolutionErp...ValidationException (tránh CS0104 clash với FluentValidation).
- FE 2 app (SHA-mirror): ChangePasswordDialog (3 ô current/new/confirm + validate client:
≥12, khớp, khác cũ) wire vào menu tài khoản TopBar — "Đổi mật khẩu" trên "Đăng xuất".
- Test: +ChangePasswordCommandTests (đúng/sai pass cũ keyed-field + msg, <12 boundary, ==cũ,
chưa-auth/UserId-null/user-not-found, RefreshToken cleared). Full suite 431 PASS (45D + 386I).
Build: BE 0 warn/0 err · fe-user + fe-admin build OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trong khối "Quy trình duyệt" của phiếu (PeWorkflowPanel, sơ đồ Bước→Cấp→NV), cấp được cấu hình
AllowApproverFinalize (Mig 58) giờ hiện badge tím "⚑ Duyệt thay CEO" + tooltip; các Bước/Cấp SAU
cấp finalize (vd Ban Giám đốc / CEO) làm mờ + nhãn "không trình tới" → người xem phiếu biết NGAY
trong sơ đồ rằng quy trình DỪNG ở cấp này (duyệt thay CEO), không lên CEO (anh Kiệt FDC).
Dùng field allowApproverFinalize đã expose ở flow-level (S89 #3). first-reachable theo Step.Order→
Level.Order (khớp BE finalize). Bổ sung cho callout note (#3) = chỉ-dẫn trong chính sơ đồ flow.
FE-only, 2 app SHA-mirror, build xanh.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phiếu mà quy trình duyệt có cấp bật AllowApproverFinalize (Mig 58) sẽ kết thúc tại cấp đó,
KHÔNG trình CEO. Hiển thị note cho MỌI NGƯỜI NẮM ở 3 surface (anh Kiệt FDC). KHÔNG migration
(cột AllowApproverFinalize đã có Mig 58, prod ở Mig 59).
BE (3 file, computed DTO):
- PurchaseEvaluationDtos.cs: +bool EndsBeforeCeo +string? FinalizeStepName +string? FinalizeLevelName
trên DetailBundle + ListItem; flow-level +bool AllowApproverFinalize.
- PurchaseEvaluationFeatures.cs: detail compute first-reachable (Step.Order↑→Level.Order↑, dept-name
in-memory); list + inbox projection qua 3 subquery EF-safe (lv.Step!.Name). V1 (workflowId==null) → false.
- CreateContractFromEvaluationFeatures.cs: ListApproved projection cùng DTO → +3 subquery y hệt.
FE 2-app (fe-user + fe-admin SHA-mirror component):
- PeFinalizeChip.tsx (MỚI, chip tím) ở 3 danh sách: PeListPanel + PurchaseEvaluationsListPage (cây) + InboxPage.
- PeWorkflowPanel.tsx: note callout trong phiếu, branch DaDuyet (past-tense) / đang-chạy; copy boundary-aware
"...là KẾT THÚC, không trình CEO. Các cấp trước vẫn duyệt như thường." (tránh cấp giữa hiểu nhầm).
- UserDashboardPage.tsx (chỉ fe-user): dải "Phiếu Duyệt NCC gần đây" + chip (admin dashboard để nguyên).
- types: PeListItem/PeDetailBundle/PeApprovalFlowLevel +field.
Tests: +PeFinalizeProjectionTests (EF-translate smoke 3 subquery + first-reachable + V1-false). Full suite 421→422.
em-main self-gate sau workflow (impl-frontend return rỗng #53 → mirror fe-admin + verify 5 site recover-disk).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>