diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 24322d5..d71a4f3 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -27,9 +27,16 @@ Planın tamamı henüz bitmedi. Canlı yayın yapılmadı. Tarayıcı kanıtı: [Yenileme sonrası beş kart](evidence/qa-kanban-after-reload.png), [ekran metni](evidence/qa-kanban-after-reload.txt). Bu kanıt gerçek beş ihale dokümanıyla başvuru kabulü değildir. +## Kaynak değişikliği ve fiyat koruması + +- Fiyatlar dizi sırasından bağımsız, kalem kimliği/içeriğine bağlı saklanır. Sıralama değişimi fiyatı başka ürüne taşımaz; miktar veya teknik özellik değişince eski fiyat yeni satıra uygulanmaz. +- Kaynak ilan, tarih/saat, kalemler ve belge gereksinimleri için karşılaştırılabilir bir içerik kaydı tutulur. Kaynak değişince resmi dokümanla karşılaştırma onayı geçersiz olur, çıktı yeniden kontrol bekler. +- Eski konumsal fiyatlar kaynak satırları olmadan güvenle eşlenemediği için otomatik taşınmaz; kullanıcıya yeniden giriş gerektiği gösterilir. Bu davranış yeni taslak kaydında da korunur. +- Üç regresyon testi sıralama, içerik değişikliği ve eski taslak senaryolarını doğrular. Henüz gerçek belge yükleme/zeyilname ve tarayıcı form kabulü tamamlanmadı. Bu kontrol tüm belge/AI kaynak sürüm modelinin yerine geçmez. + ## Doğrulama -`npm test`: 19/19 geçti (son yeniden çalıştırma: 6 Eylül 2026). +`npm test`: 22/22 geçti (son yeniden çalıştırma: 6 Eylül 2026). `npm run build`: geçti; mevcut büyük paket/dinamik import uyarıları sürüyor. `node --check server/index.js`, `node --check server/user-store.mjs`, `git diff --check`: geçti. diff --git a/src/components/BidPackageBuilder.jsx b/src/components/BidPackageBuilder.jsx index f0f880d..0e1a5f9 100644 --- a/src/components/BidPackageBuilder.jsx +++ b/src/components/BidPackageBuilder.jsx @@ -1,5 +1,6 @@ import React, { useState, useMemo } from 'react'; import { bidRowTotal, validateBidRows, roundMoney } from '../services/bidValidation'; +import { sourceRows, bidSourceSnapshot, restoreBidPrices } from '../services/bidSource'; import { useApp } from '../context/AppContext'; import { FolderCheck, ChevronDown, ChevronUp, FileSignature, Table2, ListChecks, @@ -58,10 +59,12 @@ export const BidPackageBuilder = ({ results, tender }) => { const [kisim, setKisim] = useState(storedDraft.kisim || ''); const [goturuBedel, setGoturuBedel] = useState(storedDraft.goturuBedel || ''); - const kalemler = tender?.items?.length - ? tender.items.map(k => ({name:k.ad,quantity:`${k.miktar} ${k.birim || ''}`,keySpecs:[]})) - : (Array.isArray(results?.itemsAndSpecs) ? results.itemsAndSpecs : []); - const [fiyatlar, setFiyatlar] = useState(storedDraft.fiyatlar || {}); + const kalemler = sourceRows(tender, results); + const sourceSnapshot = bidSourceSnapshot(tender, results, kalemler); + const [reviewedSource, setReviewedSource] = useState(storedDraft.reviewedSource || null); + const [fiyatlar, setFiyatlar] = useState(() => restoreBidPrices(storedDraft)); + const kaynakKontrolGerekli = reviewedSource !== sourceSnapshot; + const eskiFiyatlar = storedDraft.priceSchema !== 1 && Object.keys(storedDraft.fiyatlar || {}).length > 0; const alanYaz = (k, v) => { const yeni = { ...firma, [k]: v }; @@ -70,13 +73,13 @@ export const BidPackageBuilder = ({ results, tender }) => { }; const satirlar = useMemo( - () => kalemler.map((k, i) => ({ ...k, birimFiyat: fiyatlar[i] })), + () => kalemler.map(k => ({ ...k, birimFiyat: fiyatlar[k.priceKey] })), [kalemler, fiyatlar], ); const kalemToplami = useMemo(() => roundMoney(satirlar.reduce((t,r) => t + (bidRowTotal(r) ?? 0),0)), [satirlar]); const saveDraft = async () => { - const saved = await mutateTender(tender.id, {bidDraft:{firma,teklifTuru,gecerlilikGun,kisim,goturuBedel,fiyatlar}}); + const saved = await mutateTender(tender.id, {bidDraft:{firma,teklifTuru,gecerlilikGun,kisim,goturuBedel,priceSchema:1,pricesBySource:fiyatlar,sourceSnapshot,reviewedSource}}); if (saved) { setDirty(false); setHata(null); } else setHata('Taslak kaydedilemedi. Tekrar deneyin.'); }; @@ -87,7 +90,7 @@ export const BidPackageBuilder = ({ results, tender }) => { const kalemEksik = teklifTuru === 'birim' && validateBidRows(satirlar).length > 0; const turCeliskisi = teklifTuru === 'goturu' && /birim fiyat/i.test(tender?.aciklama || ''); const sureEksik = !(Number(gecerlilikGun) > 0) || (kaynakGecerlilik && Number(gecerlilikGun) < Number(kaynakGecerlilik[1])); - const hazir = eksikler.length === 0 && toplam > 0 && !kalemEksik && !turCeliskisi && !sureEksik; + const hazir = eksikler.length === 0 && toplam > 0 && !kalemEksik && !turCeliskisi && !sureEksik && !kaynakKontrolGerekli; const formVerisi = () => ({ ...firma, @@ -142,6 +145,19 @@ export const BidPackageBuilder = ({ results, tender }) => { {dirty ? 'Kaydedilmemiş değişiklikler var' : storedDraft.firma ? 'Sunucuda kayıtlı taslak' : 'Henüz kaydedilmedi'} +
+ {reviewedSource ? 'İlan, kalem veya belge bilgisi değişti. Önceki kontrol bu sürüm için geçerli değil.' : 'Kalemler ve teklif koşulları resmi dokümanla henüz karşılaştırılmadı.'} + {' '}Kaynağı kontrol etmeden teklif çıktısı oluşturulamaz. +
} + {eskiFiyatlar &&Eski taslak fiyatları satır sırasına göre kaydedilmiş. Yanlış ürüne aktarılmaması için fiyatları yeniden girin.
} + +