diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 6659744..fffa935 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -1145,3 +1145,10 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Sunucu yenilemesindeki toplam 80 kayıt kesintisi de kaldırıldı; yenileme sunucunun gönderdiği kayıtları/okunma durumunu esas alıp yerel uyarıları kendi sınırı içinde koruyor. Yerel bildirim kimlikleri aynı milisaniyede çakışabilen Date.now yerine UUID kullanıyor. - Doğrulama: 270 test ve build başarılı. Yeni regresyon testinde 100 sunucu kaydı üzerine 40 yerel uyarı eklendi; tüm sunucu kayıtları ve okunma değerleri korundu, son 30 yerel uyarı tekil kimlikle kaldı, sunucudan yeni okunma durumu geldiğinde yerel kayıtlar korundu. Bu sayı testi istemcinin aldığı listeyi kapsar; sunucu bildirim geçmişinin bütününü/paginasyonunu kanıtlamaz. - Bu değişiklik için gerçek tarayıcıdaki yoğun bildirim görsel kabulü henüz yapılmadı. Gerçek bildirim gönderilmedi, canlıya yayın yapılmadı. Genel plan açık. + +### 2026-09-06 — Kazananı açıklanmış ihalede hazır çıktı engeli + +- Hazırlık son kontrolü yalnız durum metni ve son teklif zamanına bakıyordu. Kaynakta kazanan firma açıklanmış, ancak durum Açık/tarih gelecek olarak kalmışsa hazır çıktı kapısı açık kalabiliyordu. Kazanan bilgisi artık bu kapıyı doğrudan kapatıyor; taslak saklama devam ediyor. +- Kazanan bilgisi teklif kaynak karşılaştırmasına dahil edildi. İlk kazananın eklenmesi veya kazanan değişimi önceki kaynak onayıyla aynı içerik sayılmıyor. Kazananı olmayan eski kayıtların imzası gereksiz yere değiştirilmedi. Kaydedilmiş fiyatlar ve taslak korunuyor; havuz dışındaki yeni anlık görüntü kazanan bilgisi taşıyor. +- Ortak kontrol hazırlık özeti, hazır durumu ve çıktı üretiminin mevcut başlangıç/ara/son kontrollerinde kullanılıyor. Doğrulama: 272 test ve build başarılı. Yeni regresyonlar açık/boş/bilinmeyen durumla gelecek tarih + kazanan, kaynak imzası değişimi, taslak korunması ve havuz dışı anlık görüntüyü kapsıyor. +- Bu değişiklik gerçek kaynakta kazanan bilgisinin doğru/güncel olduğunu kanıtlamaz. Tarayıcıdaki kazanan-güncelleme sırasında çıktı yarışı ve resmi beş dosya kabulü ayrıca doğrulanmalı. Canlı yayın yapılmadı; genel plan açık. diff --git a/src/services/bidSource.js b/src/services/bidSource.js index cf5bcf1..074ff78 100644 --- a/src/services/bidSource.js +++ b/src/services/bidSource.js @@ -33,6 +33,7 @@ export function bidSourceSnapshot(tender, results, rows = sourceRows(tender, res announcement: tender?.aciklama || '', deadline: [tender?.tarih || '', tender?.saat || ''], status: tender?.durum || '', + ...(tender?.kazananFirma ? {winner:tender.kazananFirma} : {}), documentVersion: results?.documentVersion ?? tender?.documentVersion ?? null, procurementTerms: results?.procurementTerms || null, analysis: preparationAnalysisSource(results), diff --git a/src/services/preparationDeadline.js b/src/services/preparationDeadline.js index 86faf19..bccfafd 100644 --- a/src/services/preparationDeadline.js +++ b/src/services/preparationDeadline.js @@ -2,6 +2,7 @@ import {deadlineInfo} from './tenderCalendar.js'; /** Preparation may be saved, but a current bid needs a verified future deadline. */ export function preparationDeadlineProblem(tender, now = new Date()) { + if(tender?.kazananFirma)return 'İhalenin kazananı açıklanmış. Hazır teklif çıktısı oluşturulamaz; taslağınızı kaydedebilirsiniz.'; const status=String(tender?.durum || '').toLocaleLowerCase('tr-TR'); if (/iptal|sonuçlandı|sözleşme imzalandı/.test(status)) return 'İhale iptal edilmiş veya sonuçlanmış. Hazır teklif çıktısı oluşturulamaz; taslağınızı kaydedebilirsiniz.'; diff --git a/tests/preparation-deadline.test.mjs b/tests/preparation-deadline.test.mjs index d7ea65e..9694946 100644 --- a/tests/preparation-deadline.test.mjs +++ b/tests/preparation-deadline.test.mjs @@ -14,3 +14,10 @@ test('cancelled and completed tenders do not regain readiness merely through a f for(const durum of ['İptal Edildi','Sonuçlandı','Sözleşme imzalandı']) assert.match(preparationDeadlineProblem({...tender,durum},new Date('2026-09-01')),/iptal edilmiş veya sonuçlanmış/); }); + +test('published winner blocks readiness even when the source status and date still appear open',()=>{ + const now=new Date('2026-09-01'); + for(const durum of ['Açık','',undefined]) + assert.match(preparationDeadlineProblem({...tender,durum,kazananFirma:'QA Laboratuvar'},now),/kazananı açıklanmış/); + assert.equal(preparationDeadlineProblem({...tender,kazananFirma:''},now),null); +}); diff --git a/tests/tender-source-changes.test.mjs b/tests/tender-source-changes.test.mjs index cbc463f..8d982ed 100644 --- a/tests/tender-source-changes.test.mjs +++ b/tests/tender-source-changes.test.mjs @@ -18,3 +18,15 @@ test('unknown legacy fields do not invent a change and a new saved baseline clea const result=applyTenderStates([current],{'2026/2':{snapshot:tenderSnapshot(current),data:{takipte:true}}}); assert.deepEqual(result[0].sourceChanges,[]); }); + +test('winner publication changes the reviewed bid source without destroying saved prices',()=>{ + const original={ikn:'2026/winner',tarih:'10.09.2026',saat:'10:00',durum:'Açık'}; + const draft={pricesBySource:{kit:'100'},reviewedSource:bidSourceSnapshot(original)}; + const awarded={...original,kazananFirma:'QA Kazanan'}; + const result=applyTenderStates([awarded],{[original.ikn]:{snapshot:tenderSnapshot(original),data:{bidDraft:draft,takipte:true}}})[0]; + assert.notEqual(bidSourceSnapshot(result),draft.reviewedSource); + assert.deepEqual(result.bidDraft,draft); + const restored=applyTenderStates([],{[original.ikn]:{snapshot:tenderSnapshot(awarded),data:{bidDraft:draft,takipte:true}}})[0]; + assert.equal(restored.kazananFirma,'QA Kazanan'); + assert.equal(bidSourceSnapshot(original),bidSourceSnapshot({...original,kazananFirma:''})); +});