From bdc9f9dfbff52dec3b56539d997b2f8f3fff0842 Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 15:14:29 +0300 Subject: [PATCH] fix: recheck linked documents after Excel generation --- docs/quality/uygulama-durumu.md | 8 ++++++++ src/components/BidPackageBuilder.jsx | 8 +++++--- tests/browser-qa.mjs | 28 +++++++++++++++++++++------- tests/fixtures/ready-bid.mjs | 9 +++++---- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 43768a2..1820977 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -1183,3 +1183,11 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Genel toplamın tekrar eden sayfa altlığı olmaması için tfoot normal satır grubu yapıldı; imza bloğuna bölünmeme kuralı eklendi. Bu iki kuralın hesaplanan CSS değerleri doğrulandı; gerçek yazdırma sayfa geçişi ayrıca kabul gerektiriyor. - Son tarayıcı ölçümü: 657,63 px içerik/tablo genişliği, sıfır taşan hücre, 100 sıralı kalem. Her satırda 3 adet, 125,335 TL birim fiyat ve 376,01 TL tutar; genel toplam 37.601,00 TL. Türkçe metin ve uzun kod bütünüyle korundu. Kanıt `rapor/qa-print-layout.json` ve ekran görüntüsü `rapor/qa-print-layout.png`. - 275 test ve build başarılı. Desktop Commander ile denenen PDF dönüşümü üretim stilini değiştirdi ve yazdırma düğmesini PDF içine bastı; `qa-100-kalem-before.pdf` üretim çıktısının doğrulama kanıtı değildir. Bu deneme üzerinden üretim sayfalama hatası iddia edilmedi. Doğrulanan kapsam gerçek HTML görünümünde A4 genişliği/taşma/içerik kontrolüdür. Tarayıcıdan kaydedilmiş çok sayfalı PDF ve beş resmi dosya kabulü açık; genel plan tamamlanmadı. + + +### 2026-09-06 — Excel üretimi sonrasında belge listesini yeniden doğrulama + +- Excel üretiminin ilk dosya kontrolü ile dosyanın indirilmesi arasında asenkron çalışma vardı. Son aşamada portföy yeniden okunuyor fakat bağlı belge bilgileri ilk GET yanıtından kullanılıyordu. Bu arada kaldırılmış veya sürümü değişmiş belge eski onayla geçebiliyordu. +- Çalışma kitabı üretildikten sonra belge listesi ve gerekiyorsa portföy yeniden okunuyor. Oturum/form/montaj kontrolünden sonra son belge verisi ekrana aktarılıyor ve ortak belge uygunluk denetimi tekrar yapılıyor. Hata halinde indirme çağrısına geçilmiyor; belge eksikleri ekranda da güncelleniyor. +- QA düzeneğinde sentetik zorunlu firma belgesine bağlı hazır dosya kullanıldı. İlk GET başarılı bırakıldı, Excel oluşturulduktan sonraki ikinci GET görünür test kontrolüyle bekletildi. Ayrı senaryolarda belge boyutu/sürümü değiştirildi ve belge kaydı kaldırıldı. Her ikisinde ikinci GET sayacı 2/bekleyen 1 olarak doğrulandı; yanıt bırakılınca ilgili hata mesajı oluştu ve Excel düğmesi kapandı. 251,00 TL toplam korundu. Dokuz kontrol geçti: `rapor/qa-excel-final-document-check.json`. +- Bu test sentetik belge metadatası kullanır; gerçek firma dosyası silinmedi, dosya indirme dizini izlenmedi. Kanıt uygulamanın son kontrol/hata dalı ve güncellenen hazır durumudur. 275 test ve build başarılı. Genel planın gerçek beş dosya ve dış servis kabul maddeleri açık. diff --git a/src/components/BidPackageBuilder.jsx b/src/components/BidPackageBuilder.jsx index 8049e36..993ca29 100644 --- a/src/components/BidPackageBuilder.jsx +++ b/src/components/BidPackageBuilder.jsx @@ -199,11 +199,13 @@ export const BidPackageBuilder = ({ results, tender: selectedTender, initialFocu if (tip === 'excel') { const buffer = await createBidWorkbook(f,satirlar); if (reviewedText) await validateDocumentTextReview(reviewedText,tender.ikn); - const finalPortfolio=await readPortfolio(); - const finalErrors=preparationBlockers(lotSelection.rows,documents,checks,{...currentVault,healthPortfolio:finalPortfolio}); - if(finalErrors.length)throw new Error(finalErrors.join(' ')); + const [finalFiles,finalPortfolio]=await Promise.all([listDocuments(),readPortfolio()]); if (!mounted.current || session !== getToken() || submitted !== latestDraft.current) throw new Error('Form veya oturum değişti; Excel çıktısını yeniden oluşturun.'); + const finalVault={...currentVault,documents:finalFiles.belgeler || [],healthPortfolio:finalPortfolio}; + setVault(finalVault); + const finalErrors=preparationBlockers(lotSelection.rows,documents,checks,finalVault); + if(finalErrors.length)throw new Error(finalErrors.join(' ')); const finalDeadlineProblem=preparationDeadlineProblem(tender); if(finalDeadlineProblem)throw new Error(finalDeadlineProblem); downloadBidWorkbook(buffer,tender.ikn); diff --git a/tests/browser-qa.mjs b/tests/browser-qa.mjs index bb1f346..cc72f20 100644 --- a/tests/browser-qa.mjs +++ b/tests/browser-qa.mjs @@ -19,6 +19,8 @@ const keyToken=signToken(keyUser); let apiKeyListFailure=false; let briefFailure='none'; let holdDocuments=false; +let skipDocumentReads=0,qaFiles=null,outputDocumentReads=0; +const qaFile={id:'qa-owned-file',ad:'QA zorunlu firma belgesi.pdf',tur:'firma',boyut:100,olusturma:'2026-09-06T00:00:00Z',erisilebilir:true}; const pendingDocuments=new Set(); process.env.IHALEIST_USER_DATA_DIR=dir; const {createBriefOutbox}=await import('../server/brief-outbox.mjs'); @@ -104,14 +106,22 @@ const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{targ } if(req.url?.startsWith('/qa-output-documents?')&&req.method==='POST') { const mode=new URL(req.url,'http://localhost').searchParams.get('mode'); - if(mode==='hold')holdDocuments=true; - if(mode==='release'){holdDocuments=false;for(const pending of pendingDocuments){pending.writeHead(200,{'Content-Type':'application/json'});pending.end(JSON.stringify({success:true,belgeler:[]}));}pendingDocuments.clear();} - res.end('Belge yanıtı: '+mode);return; + if(mode==='hold'){holdDocuments=true;skipDocumentReads=0;outputDocumentReads=0;} + if(mode==='hold-final'){holdDocuments=true;skipDocumentReads=1;outputDocumentReads=0;} + if(mode==='revise')qaFiles=qaFiles?.map(file=>({...file,boyut:file.boyut+1})); + if(mode==='remove')qaFiles=[]; + if(mode==='release'){holdDocuments=false;for(const pending of pendingDocuments){pending.writeHead(200,{'Content-Type':'application/json'});pending.end(JSON.stringify({success:true,belgeler:qaFiles || []}));}pendingDocuments.clear();} + res.end('Belge yanıtı: '+mode+'; okunan: '+outputDocumentReads+'; bekleyen: '+pendingDocuments.size);return; } - if(req.url==='/api/me/documents'&&req.method==='GET'&&holdDocuments){pendingDocuments.add(res);res.on('close',()=>pendingDocuments.delete(res));return;} - if(req.url==='/qa-source-update'||req.url==='/qa-source-update?ready=1') { - const selected=req.url.endsWith('?ready=1')?readyBid(tenders[0]):tenders[0]; - if(selected.bidDraft){const summary=preparationSummary(selected,{loading:false,error:null,documents:[]});if(summary.issues.length)throw new Error('QA ready fixture: '+JSON.stringify(summary.issues));} + if(req.url==='/api/me/documents'&&req.method==='GET'){ + if(holdDocuments){outputDocumentReads++;if(skipDocumentReads>0)skipDocumentReads--;else{pendingDocuments.add(res);res.on('close',()=>pendingDocuments.delete(res));return;}} + if(qaFiles){res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify({success:true,belgeler:qaFiles}));return;} + } + if(req.url==='/qa-source-update'||req.url==='/qa-source-update?ready=1'||req.url==='/qa-source-update?ready=1&file=1') { + const query=new URL(req.url,'http://localhost').searchParams; + qaFiles=query.has('file')?[{...qaFile}]:null; + const selected=query.has('ready')?readyBid(tenders[0],qaFiles?.[0]):tenders[0]; + if(selected.bidDraft){const summary=preparationSummary(selected,{loading:false,error:null,documents:qaFiles || []});if(summary.issues.length)throw new Error('QA ready fixture: '+JSON.stringify(summary.issues));} const html=`