diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 7b55921..69291af 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -1264,3 +1264,9 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Eksiklere geçiş düğmeleri aynı veri ve olay işleyicilerini kullanıyor. Tablo/satır/hücre rolleri ve sütun başlıkları korunuyor; mobil görsel alan etiketleri ekran okuyucu için tekrar edilmiyor. Uzun kesintisiz metinler satıra bölünebiliyor. - Build ve diff kontrolü başarılı. 390/768/1440 piksel tarayıcı görsel kabulü, klavye ve gerçek ekran okuyucu doğrulaması henüz yapılmadı. Bu düzenleme tüm mobil kabulün geçtiği anlamına gelmez. Genel plan açık. - QA düzeneğine aynı özetin 390/768/1440 içerik genişliğiyle açılabildiği bir görünüm yolu eklendi ve uzun başlık/atanan sorumlu verisiyle hedef bağlantısı ölçüldü. Uygulama içi tarayıcı sekmesinin görünüm alanı yeniden boyutlandırılamadığı için bu ölçüm gerçek mobil viewport kabulü sayılmadı; mobil doğrulama hâlâ açık. + +### 2026-09-06 — ICS kaynak sürümü + +- D02 kapsamında ICS olayına `SEQUENCE` eklendi. Değer, doğrulanmış `icsSequence` veya doküman sürümünden güvenli bir negatif olmayan tamsayı olarak alınır; geçersiz değerler sıfıra düşer. İKN tabanlı UID sabit kalır, tarih değişiminde istemcilerin aynı etkinliği güncellemesi mümkün olur. +- Açıklamadaki “otomatik güncellenmez” ifadesi, kaynak sürümü ve resmi doğrulama gereğini belirten güncelleme yönlendirmesiyle değiştirildi. ICS’nin kendisi hâlâ resmi takvim servisine otomatik gönderim yapmaz. +- Regresyon testi UID sürekliliğini ve 3→4 sürüm artışını doğruluyor. 278 test ve build başarılı. Google/Outlook gerçek test takvimlerine aktarım henüz yapılmadı; genel plan açık. diff --git a/src/services/tenderCalendar.js b/src/services/tenderCalendar.js index 8bf04ab..ec43e3c 100644 --- a/src/services/tenderCalendar.js +++ b/src/services/tenderCalendar.js @@ -29,11 +29,13 @@ export function createTenderICS(t, now = new Date()) { const start = tenderDeadline(t); const uid = encodeURIComponent(String(t.ikn || t.id || '').trim()); if (!uid) throw new Error('İhale kimliği gerekli.'); + const rawSequence=Number(t.icsSequence ?? t.documentVersion ?? 0); + const sequence=Number.isSafeInteger(rawSequence)&&rawSequence>=0 ? rawSequence : 0; return ['BEGIN:VCALENDAR','VERSION:2.0','PRODID:-//Ihaleist//Tender Calendar//TR','CALSCALE:GREGORIAN', 'BEGIN:VEVENT',`UID:${uid}@ihaleist.com`,`DTSTAMP:${stamp(now)}`, - `DTSTART:${stamp(start)}`,'TRANSP:TRANSPARENT', + `SEQUENCE:${sequence}`,`DTSTART:${stamp(start)}`,'TRANSP:TRANSPARENT', `SUMMARY:${escapeICS('İhale son teklif: '+t.baslik)}`, - `DESCRIPTION:${escapeICS('İKN: '+t.ikn+' — '+t.kurum+'\nBu tek seferlik takvim kaydı otomatik güncellenmez. İhale tarihini resmi kaynaktan doğrulayın; değişiklikte takviminizdeki eski kaydı güncelleyin.')}`, + `DESCRIPTION:${escapeICS('İKN: '+t.ikn+' — '+t.kurum+'\nKaynak sürümünü ve ihale tarihini resmi kaynaktan doğrulayın; zeyilname veya tarih değişikliğinde aynı UID ve artan sürümle takvimi güncelleyin.')}`, 'END:VEVENT','END:VCALENDAR',''].map(fold).join('\r\n'); } diff --git a/tests/calendar.test.mjs b/tests/calendar.test.mjs index 753ecb1..9e1940f 100644 --- a/tests/calendar.test.mjs +++ b/tests/calendar.test.mjs @@ -55,11 +55,18 @@ test('deadline ICS is a transparent instant, preserves UID across date changes a assert.match(changed,/DTSTART:20260912T113000Z/); assert.doesNotMatch(changed,/DTEND:|DURATION:/); assert.match(changed,/TRANSP:TRANSPARENT/); - assert.match(changed.replace(/\r\n /g,''),/otomatik güncellenmez/); + assert.match(changed.replace(/\r\n /g,''),/artan sürümle takvimi güncelle/); const hostile=createTenderICS({...t,baslik:'A\rEND:VEVENT\rBEGIN:VEVENT\nB'}); assert.equal(hostile.split('\r\n').filter(line=>line==='BEGIN:VEVENT').length,1); assert.equal(hostile.split('\r\n').filter(line=>line==='END:VEVENT').length,1); }); +test('ICS exposes a stable event version for calendar updates',()=>{ + const first=createTenderICS({...t,documentVersion:3},new Date('2026-09-01T00:00:00Z')); + const changed=createTenderICS({...t,tarih:'12.09.2026',saat:'14:30',documentVersion:4},new Date('2026-09-02T00:00:00Z')); + assert.match(first,/SEQUENCE:3/); assert.match(changed,/SEQUENCE:4/); + assert.equal(first.match(/UID:[^\r\n]+/)[0],changed.match(/UID:[^\r\n]+/)[0]); + assert.match(changed,/art%C4%B1?an|artan sürüm|artan/); +}); test('calendar agenda sorts known times before unknown and preserves undated records',async()=>{ const {calendarAgenda}=await import('../src/services/tenderCalendar.js');