From ad7f8746a07f10dcd68ab9ae30f185de14f553b0 Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 09:50:16 +0300 Subject: [PATCH] Verify current document review before bid outputs --- docs/quality/uygulama-durumu.md | 6 ++++++ server/index.js | 11 +++++++++++ src/components/BidPackageBuilder.jsx | 5 ++++- src/services/account.js | 4 ++++ tests/http-state.test.mjs | 9 +++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 28d14b4..da15e96 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -373,3 +373,9 @@ Bu yalnız sunucu altyapısıdır. Metin düzenleme arayüzü, bu kaydın analiz Hazırlık formu inceleme kaynağını gösterir. İnceleme sürümü kaynak snapshot'ına katıldı; aynı kalemlerle yeni inceleme sonucu geldiğinde eski kaynak kontrolü geçersizleşir. 79 mevcut test ve ek snapshot testi geçti (80 ayrı test); üretim derlemesi geçti. Kaynak çözümleme testleri başka hesap/İKN, eski revizyon ve değişmiş dosyayı reddediyor. Gerçek AI sağlayıcısıyla tarayıcı kabulü ve çıktı anında sunucudan incelemenin hâlâ güncel olduğunun doğrulanması henüz açık. İnceleme kullanıcı beyanıdır, resmi doğruluk garantisi değildir. + +### İnceleme güncelliğinin çıktı öncesi doğrulanması + +PDF/yazdırma akışı, canlı belge listesi kontrolüne ek olarak analizdeki humanReview kaydını sunucuda doğruluyor. İKN, sahiplik, son revizyon, gerçek dosya SHA256 ve düzeltilmiş metin SHA256 eşleşmeden çıktı ilerlemiyor. Excel oluşturma asenkron olduğu için dosya hazırlanırken oluşabilecek inceleme değişikliği indirmeden önce tekrar kontrol ediliyor. API yanıtı düzeltme metninin tamamını taşımıyor; ağ/sürüm hatasında çıktı penceresi kapanıyor ve hata gösteriliyor. + +80 test ve üretim derlemesi geçti. HTTP doğrulama testinde güncel kayıt kabul edildi; başka hesap, yanlış İKN, yanlış metin özeti ve sonradan oluşturulan yeni revizyon karşısındaki eski kayıt reddedildi. Gerçek tarayıcıda çıktı sırasında inceleme değiştirme kabulü hâlâ açık. Bu kontrol çıktı anına aittir; çıktı alındıktan sonra resmi belge değişirse önceden indirilen dosya otomatik geri alınamaz. diff --git a/server/index.js b/server/index.js index c671882..abe7240 100644 --- a/server/index.js +++ b/server/index.js @@ -1065,6 +1065,17 @@ const server = http.createServer(async (req, res) => { return json(res, 200, { success: true, belgeler: store.listDocuments(user.email) }); } catch (e) { return json(res, 400, { success: false, error: e.message }); } } + if (url.pathname === '/api/me/documents/text-review/validate' && req.method === 'POST') { + const user=userFromAuthHeader(req.headers['authorization']); + if(!user) return json(res,401,{success:false,error:'oturum yok'}); + try { + const body=await readBody(req,8192); + const resolved=reviewedAnalysisSource(user.email,body.review,body.ikn); + if(body.review.fileHash!==resolved.humanReview.fileHash || body.review.textHash!==resolved.humanReview.textHash) + return json(res,409,{success:false,error:'Analizin dayandığı metin incelemesi güncel değil. Analizi yenileyin.'}); + return json(res,200,{success:true,review:resolved.humanReview}); + } catch(error) {return json(res,error.status || 500,{success:false,error:error.message});} + } if (url.pathname === '/api/me/documents/text-review') { const user=userFromAuthHeader(req.headers['authorization']); if(!user) return json(res,401,{success:false,error:'oturum yok'}); diff --git a/src/components/BidPackageBuilder.jsx b/src/components/BidPackageBuilder.jsx index 9823fc5..2821c91 100644 --- a/src/components/BidPackageBuilder.jsx +++ b/src/components/BidPackageBuilder.jsx @@ -1,7 +1,7 @@ import { bidValidityForForm, bidTypeForForm } from '../services/bidTerms'; import { createBidWorkbook, downloadBidWorkbook } from '../services/bidWorkbook'; import { getToken } from '../services/auth'; -import { listDocuments, downloadDocument } from '../services/account'; +import { listDocuments, downloadDocument, validateDocumentTextReview } from '../services/account'; import { tenderDateKey } from '../services/tenderCalendar'; import React, { useState, useMemo, useEffect, useRef } from 'react'; import { bidRowTotal, validateBidRows, roundMoney, parseBidPrice } from '../services/bidValidation'; @@ -153,6 +153,8 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => { printWindow.current = belgeYazdir('Dosya kontrolü', '

Belgeler kontrol ediliyor…

'); setPrinting(true); const currentFiles = await listDocuments(); + const reviewedText = effectiveResults.sourceReview?.humanReview; + if (reviewedText) await validateDocumentTextReview(reviewedText,tender.ikn); if (!mounted.current || session !== getToken() || submitted !== latestDraft.current) throw new Error('Form, kaynak veya oturum değişti. Güncel bilgilerle yeniden çıktı oluşturun.'); const currentVault = {loading:false,error:null,documents:currentFiles.belgeler || [],deadlineDate:tenderDateKey(tender)}; @@ -162,6 +164,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => { const f = formVerisi(); if (tip === 'excel') { const buffer = await createBidWorkbook(f,satirlar); + if (reviewedText) await validateDocumentTextReview(reviewedText,tender.ikn); if (!mounted.current || session !== getToken() || submitted !== latestDraft.current) throw new Error('Form veya oturum değişti; Excel çıktısını yeniden oluşturun.'); downloadBidWorkbook(buffer,tender.ikn); diff --git a/src/services/account.js b/src/services/account.js index dfc1b34..39ba36e 100644 --- a/src/services/account.js +++ b/src/services/account.js @@ -119,3 +119,7 @@ export const getDocumentTextReview = (id) => iste(`/api/me/documents/text-review export const saveDocumentTextReview = (id, data) => iste('/api/me/documents/text-review', { method:'POST', body:JSON.stringify({id,...data}), }); + +export const validateDocumentTextReview = (review, ikn) => iste('/api/me/documents/text-review/validate', { + method:'POST', body:JSON.stringify({review,ikn}), +}); diff --git a/tests/http-state.test.mjs b/tests/http-state.test.mjs index 0b014f9..c82ceb1 100644 --- a/tests/http-state.test.mjs +++ b/tests/http-state.test.mjs @@ -117,9 +117,18 @@ test('shared item extraction requires an owned tender document and matching tend assert.equal((await request('/api/me/documents/text-review','POST',correction,other)).status,404); const savedReview=await request('/api/me/documents/text-review','POST',correction); assert.equal(savedReview.status,200);assert.equal(savedReview.data.review.revision,1); + const verification={ikn:body.ikn,review:{documentId:tender.data.id,revision:1,fileHash:savedReview.data.fileHash,textHash:savedReview.data.review.text_hash}}; + assert.equal((await request('/api/me/documents/text-review/validate','POST',verification)).status,200); + assert.equal((await request('/api/me/documents/text-review/validate','POST',verification,other)).status,404); + assert.equal((await request('/api/me/documents/text-review/validate','POST',{...verification,review:{...verification.review,textHash:'wrong'}})).status,409); + assert.equal((await request('/api/me/documents/text-review/validate','POST',{...verification,ikn:'2026/999'})).status,409); + assert.equal((await request(reviewUrl)).data.review.corrected_text,correction.correctedText); assert.equal((await request('/api/me/documents/text-review','POST',correction)).status,409); assert.equal((await request('/api/me/documents/text-review','POST',{...correction,expectedRevision:1,fileHash:'old-hash'})).status,409); + assert.equal((await request('/api/me/documents/text-review','POST',{...correction,expectedRevision:1,correctedText:correction.correctedText+' İkinci inceleme.'})).status,200); + assert.equal((await request('/api/me/documents/text-review/validate','POST',verification)).status,409); + assert.equal((await request('/api/kalem/cikar','POST',{ikn:body.ikn,metin:'test'})).status,403); await request('/api/me/documents?id='+company.data.id,'DELETE');