fix: preserve Turkish decimal prices and verify preparation persistence
This commit is contained in:
@@ -0,0 +1,150 @@
|
|||||||
|
|
||||||
|
- status: Sunucuda kayıtlı taslak
|
||||||
|
- alert: Kalemler ve teklif koşulları resmi dokümanla henüz karşılaştırılmadı. Kaynağı kontrol etmeden teklif çıktısı oluşturulamaz.
|
||||||
|
- checkbox "Bu sürümün kalemlerini, miktarlarını, teklif türünü ve geçerlilik süresini resmi dokümanla karşılaştırdım."
|
||||||
|
- text: Bu sürümün kalemlerini, miktarlarını, teklif türünü ve geçerlilik süresini resmi dokümanla karşılaştırdım.
|
||||||
|
- heading "İstekli (firma) bilgileri" [level=5]
|
||||||
|
- generic: Firma ticaret unvanı *
|
||||||
|
- textbox "Firma ticaret unvanı *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: QA Laboratuvar Test Ltd
|
||||||
|
- generic: Tebligata esas adres *
|
||||||
|
- textbox "Tebligata esas adres *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: Yerel test adresi
|
||||||
|
- generic: Vergi dairesi *
|
||||||
|
- textbox "Vergi dairesi *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: Test
|
||||||
|
- generic: Vergi numarası *
|
||||||
|
- textbox "Vergi numarası *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: "0000000000"
|
||||||
|
- generic: Ticaret sicil / MERSİS no
|
||||||
|
- textbox "Ticaret sicil / MERSİS no":
|
||||||
|
- /placeholder: ""
|
||||||
|
- generic: Yetkili ad soyad *
|
||||||
|
- textbox "Yetkili ad soyad *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: Test Yetkilisi
|
||||||
|
- generic: Yetkilinin görevi *
|
||||||
|
- textbox "Yetkilinin görevi *":
|
||||||
|
- /placeholder: Örn. Genel Müdür
|
||||||
|
- text: Test Sorumlusu
|
||||||
|
- generic: Telefon *
|
||||||
|
- textbox "Telefon *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: "0000000000"
|
||||||
|
- generic: E-posta *
|
||||||
|
- textbox "E-posta *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: qa@example.test
|
||||||
|
- generic: Teklif türü
|
||||||
|
- combobox "Teklif türü":
|
||||||
|
- option "Birim fiyat" [selected]
|
||||||
|
- option "Anahtar teslim götürü bedel"
|
||||||
|
- generic: Teklif geçerlilik (gün)
|
||||||
|
- textbox "Teklif geçerlilik (gün)": "120"
|
||||||
|
- generic: Kısım (varsa)
|
||||||
|
- textbox "Kısım (varsa)":
|
||||||
|
- /placeholder: Örn. 2. kısım
|
||||||
|
- heading "Kalem fiyatlandırma" [level=5]
|
||||||
|
- paragraph: Ondalık fiyatı 125,50 veya 125.50 biçiminde yazın; binlik ayırıcı kullanmayın.
|
||||||
|
- table:
|
||||||
|
- rowgroup:
|
||||||
|
- row "Kalem Miktar Birim fiyat Tutar":
|
||||||
|
- columnheader "Kalem"
|
||||||
|
- columnheader "Miktar"
|
||||||
|
- columnheader "Birim fiyat"
|
||||||
|
- columnheader "Tutar"
|
||||||
|
- rowgroup:
|
||||||
|
- row "Sentetik test kalemi 2 adet 125,50 251,00":
|
||||||
|
- cell "Sentetik test kalemi"
|
||||||
|
- cell "2 adet"
|
||||||
|
- cell "125,50":
|
||||||
|
- textbox "0": 125,50
|
||||||
|
- cell "251,00"
|
||||||
|
- region "Belge ve ürün uygunluğu":
|
||||||
|
- heading "Belge ve ürün uygunluğu" [level=5]
|
||||||
|
- paragraph: Her kontrol için belge adı/sürümü ve kaynak maddeyi yazın. ÜTS, numune ve servis koşulları her ihalede zorunlu değildir; uygulanmıyorsa gerekçesini belirtin. Bu kayıtlar kullanıcı kontrolüdür; resmi uygunluk onayı değildir.
|
||||||
|
- group "Sentetik yetki belgesi (zorunlu)":
|
||||||
|
- generic: Sentetik yetki belgesi (zorunlu)
|
||||||
|
- paragraph: Yerel QA dokümanı madde 7
|
||||||
|
- button "Elle eklenen gereksinimi kaldır"
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı" [selected]
|
||||||
|
- text: Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde
|
||||||
|
- textbox "Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde": QA dosyası v1 — yalnız test, madde 7
|
||||||
|
- paragraph: Resmi dokümandan eksik belge gereksinimi ekle
|
||||||
|
- text: Belge adı
|
||||||
|
- textbox "Belge adı"
|
||||||
|
- text: Kaynak doküman ve madde
|
||||||
|
- textbox "Kaynak doküman ve madde"
|
||||||
|
- button "Belge gereksinimi ekle" [disabled]
|
||||||
|
- generic "Sentetik test kalemi — ürün/hizmet ve yükümlülük kontrolü" [active]
|
||||||
|
- text: Teklif edilen marka/model veya hizmet kapsamı
|
||||||
|
- textbox "Teklif edilen marka/model veya hizmet kapsamı": QA Model 1
|
||||||
|
- text: Teknik doküman/madde ve uygunluk dayanağı
|
||||||
|
- textbox "Teknik doküman/madde ve uygunluk dayanağı": Sentetik QA teknik madde 3
|
||||||
|
- text: Ürün/hizmet uygunluğu
|
||||||
|
- combobox "Ürün/hizmet uygunluğu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Uygun" [selected]
|
||||||
|
- option "Uygun değil"
|
||||||
|
- group "ÜTS / ürün kaydı":
|
||||||
|
- generic: ÜTS / ürün kaydı
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- text: Kaynak, karşılanan koşul veya uygulanmama gerekçesi
|
||||||
|
- textbox "Kaynak, karşılanan koşul veya uygulanmama gerekçesi": Yalnız yerel sentetik test; gerçek şartname kontrolü değildir.
|
||||||
|
- group "Numune / demonstrasyon":
|
||||||
|
- generic: Numune / demonstrasyon
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- text: Kaynak, karşılanan koşul veya uygulanmama gerekçesi
|
||||||
|
- textbox "Kaynak, karşılanan koşul veya uygulanmama gerekçesi": Yalnız yerel sentetik test; gerçek şartname kontrolü değildir.
|
||||||
|
- group "Kurulum / bakım / servis":
|
||||||
|
- generic: Kurulum / bakım / servis
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- text: Kaynak, karşılanan koşul veya uygulanmama gerekçesi
|
||||||
|
- textbox "Kaynak, karşılanan koşul veya uygulanmama gerekçesi": Yalnız yerel sentetik test; gerçek şartname kontrolü değildir.
|
||||||
|
- group "Teslimat ve süre":
|
||||||
|
- generic: Teslimat ve süre
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- text: Kaynak, karşılanan koşul veya uygulanmama gerekçesi
|
||||||
|
- textbox "Kaynak, karşılanan koşul veya uygulanmama gerekçesi": Yalnız yerel sentetik test; gerçek şartname kontrolü değildir.
|
||||||
|
- text: Sorumlu
|
||||||
|
- textbox "Sorumlu": QA Yetkilisi
|
||||||
|
- generic: Teklif toplamı (KDV hariç)
|
||||||
|
- generic: 251,00 TL
|
||||||
|
- paragraph: "Yazı ile: ikiyüzellibir Türk Lirası"
|
||||||
|
- heading "Hazırlanacak belgeler" [level=5]
|
||||||
|
- button "Teklif Mektubu Rakam ve yazı ile bedel Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Teklif Mektubu
|
||||||
|
- generic: Rakam ve yazı ile bedel
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- button "Birim Fiyat Teklif Cetveli 1 kalem Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Birim Fiyat Teklif Cetveli
|
||||||
|
- generic: 1 kalem
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- button "Evrak Kontrol Listesi 1 belge Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Evrak Kontrol Listesi
|
||||||
|
- generic: 1 belge
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- paragraph: Tutar, kaynak, belge ve ürün uygunluğu kontrollerini tamamlayın.
|
||||||
|
- paragraph: Belgeler taslaktır ve idarenin standart formlarının yerine geçmez. İmzalamadan önce ihale dokümanındaki form metinleriyle karşılaştırın.
|
||||||
|
- button
|
||||||
|
- button "Kullanım kılavuzu (? tuşu)":
|
||||||
|
- region "Notifications (F8)":
|
||||||
|
- list
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
|
||||||
|
- status: Sunucuda kayıtlı taslak
|
||||||
|
- alert: Kalemler ve teklif koşulları resmi dokümanla henüz karşılaştırılmadı. Kaynağı kontrol etmeden teklif çıktısı oluşturulamaz.
|
||||||
|
- checkbox "Bu sürümün kalemlerini, miktarlarını, teklif türünü ve geçerlilik süresini resmi dokümanla karşılaştırdım."
|
||||||
|
- text: Bu sürümün kalemlerini, miktarlarını, teklif türünü ve geçerlilik süresini resmi dokümanla karşılaştırdım.
|
||||||
|
- heading "İstekli (firma) bilgileri" [level=5]
|
||||||
|
- generic: Firma ticaret unvanı *
|
||||||
|
- textbox "Firma ticaret unvanı *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: QA Laboratuvar Test Ltd
|
||||||
|
- generic: Tebligata esas adres *
|
||||||
|
- textbox "Tebligata esas adres *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: Yerel test adresi
|
||||||
|
- generic: Vergi dairesi *
|
||||||
|
- textbox "Vergi dairesi *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: Test
|
||||||
|
- generic: Vergi numarası *
|
||||||
|
- textbox "Vergi numarası *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: "0000000000"
|
||||||
|
- generic: Ticaret sicil / MERSİS no
|
||||||
|
- textbox "Ticaret sicil / MERSİS no":
|
||||||
|
- /placeholder: ""
|
||||||
|
- generic: Yetkili ad soyad *
|
||||||
|
- textbox "Yetkili ad soyad *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: Test Yetkilisi
|
||||||
|
- generic: Yetkilinin görevi *
|
||||||
|
- textbox "Yetkilinin görevi *":
|
||||||
|
- /placeholder: Örn. Genel Müdür
|
||||||
|
- text: Test Sorumlusu
|
||||||
|
- generic: Telefon *
|
||||||
|
- textbox "Telefon *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: "0000000000"
|
||||||
|
- generic: E-posta *
|
||||||
|
- textbox "E-posta *":
|
||||||
|
- /placeholder: ""
|
||||||
|
- text: qa@example.test
|
||||||
|
- generic: Teklif türü
|
||||||
|
- combobox "Teklif türü":
|
||||||
|
- option "Birim fiyat" [selected]
|
||||||
|
- option "Anahtar teslim götürü bedel"
|
||||||
|
- generic: Teklif geçerlilik (gün)
|
||||||
|
- textbox "Teklif geçerlilik (gün)": "120"
|
||||||
|
- generic: Kısım (varsa)
|
||||||
|
- textbox "Kısım (varsa)":
|
||||||
|
- /placeholder: Örn. 2. kısım
|
||||||
|
- heading "Kalem fiyatlandırma" [level=5]
|
||||||
|
- table:
|
||||||
|
- rowgroup:
|
||||||
|
- row "Kalem Miktar Birim fiyat Tutar":
|
||||||
|
- columnheader "Kalem"
|
||||||
|
- columnheader "Miktar"
|
||||||
|
- columnheader "Birim fiyat"
|
||||||
|
- columnheader "Tutar"
|
||||||
|
- rowgroup:
|
||||||
|
- row "Sentetik test kalemi 2 adet 125.50 251,00":
|
||||||
|
- cell "Sentetik test kalemi"
|
||||||
|
- cell "2 adet"
|
||||||
|
- cell "125.50":
|
||||||
|
- textbox "0": "125.50"
|
||||||
|
- cell "251,00"
|
||||||
|
- region "Belge ve ürün uygunluğu":
|
||||||
|
- heading "Belge ve ürün uygunluğu" [level=5]
|
||||||
|
- paragraph: Her kontrol için belge adı/sürümü ve kaynak maddeyi yazın. ÜTS, numune ve servis koşulları her ihalede zorunlu değildir; uygulanmıyorsa gerekçesini belirtin. Bu kayıtlar kullanıcı kontrolüdür; resmi uygunluk onayı değildir.
|
||||||
|
- group "Sentetik yetki belgesi (zorunlu)":
|
||||||
|
- generic: Sentetik yetki belgesi (zorunlu)
|
||||||
|
- paragraph: Yerel QA dokümanı madde 7
|
||||||
|
- button "Elle eklenen gereksinimi kaldır"
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli" [selected]
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- text: Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde
|
||||||
|
- textbox "Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde"
|
||||||
|
- paragraph: Resmi dokümandan eksik belge gereksinimi ekle
|
||||||
|
- text: Belge adı
|
||||||
|
- textbox "Belge adı"
|
||||||
|
- text: Kaynak doküman ve madde
|
||||||
|
- textbox "Kaynak doküman ve madde"
|
||||||
|
- button "Belge gereksinimi ekle" [disabled]
|
||||||
|
- generic "Sentetik test kalemi — ürün/hizmet ve yükümlülük kontrolü"
|
||||||
|
- option "Kontrol gerekli" [selected]
|
||||||
|
- option "Uygun"
|
||||||
|
- option "Uygun değil"
|
||||||
|
- option "Kontrol gerekli" [selected]
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli"
|
||||||
|
- option "Kontrol gerekli" [selected]
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli"
|
||||||
|
- option "Kontrol gerekli" [selected]
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli"
|
||||||
|
- option "Kontrol gerekli" [selected]
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli"
|
||||||
|
- generic "6 belge/uygunluk kontrolü tamamlanmalı"
|
||||||
|
- generic: Teklif toplamı (KDV hariç)
|
||||||
|
- generic: 251,00 TL
|
||||||
|
- paragraph: "Yazı ile: ikiyüzellibir Türk Lirası"
|
||||||
|
- heading "Hazırlanacak belgeler" [level=5]
|
||||||
|
- button "Teklif Mektubu Rakam ve yazı ile bedel Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Teklif Mektubu
|
||||||
|
- generic: Rakam ve yazı ile bedel
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- button "Birim Fiyat Teklif Cetveli 1 kalem Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Birim Fiyat Teklif Cetveli
|
||||||
|
- generic: 1 kalem
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- button "Evrak Kontrol Listesi 1 belge Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Evrak Kontrol Listesi
|
||||||
|
- generic: 1 belge
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- paragraph: Tutar, kaynak, belge ve ürün uygunluğu kontrollerini tamamlayın.
|
||||||
|
- paragraph: Belgeler taslaktır ve idarenin standart formlarının yerine geçmez. İmzalamadan önce ihale dokümanındaki form metinleriyle karşılaştırın.
|
||||||
|
- button
|
||||||
|
- button "Kullanım kılavuzu (? tuşu)":
|
||||||
|
- region "Notifications (F8)":
|
||||||
|
- list
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
[level=5]
|
||||||
|
- paragraph: Ondalık fiyatı 125,50 veya 125.50 biçiminde yazın; binlik ayırıcı kullanmayın.
|
||||||
|
- table:
|
||||||
|
- rowgroup:
|
||||||
|
- row "Kalem Miktar Birim fiyat Tutar":
|
||||||
|
- columnheader "Kalem"
|
||||||
|
- columnheader "Miktar"
|
||||||
|
- columnheader "Birim fiyat"
|
||||||
|
- columnheader "Tutar"
|
||||||
|
- rowgroup:
|
||||||
|
- row "Sentetik test kalemi 2 adet 125,50 251,00":
|
||||||
|
- cell "Sentetik test kalemi"
|
||||||
|
- cell "2 adet"
|
||||||
|
- cell "125,50":
|
||||||
|
- textbox "0": 125,50
|
||||||
|
- cell "251,00"
|
||||||
|
- region "Belge ve ürün uygunluğu":
|
||||||
|
- heading "Belge ve ürün uygunluğu" [level=5]
|
||||||
|
- paragraph: Her kontrol için belge adı/sürümü ve kaynak maddeyi yazın. ÜTS, numune ve servis koşulları her ihalede zorunlu değildir; uygulanmıyorsa gerekçesini belirtin. Bu kayıtlar kullanıcı kontrolüdür; resmi uygunluk onayı değildir.
|
||||||
|
- group "Sentetik yetki belgesi (zorunlu)":
|
||||||
|
- generic: Sentetik yetki belgesi (zorunlu)
|
||||||
|
- paragraph: Yerel QA dokümanı madde 7
|
||||||
|
- button "Elle eklenen gereksinimi kaldır"
|
||||||
|
- combobox "Kontrol durumu":
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı" [selected]
|
||||||
|
- text: Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde
|
||||||
|
- textbox "Belge adı/sürümü, geçerlilik kontrolü ve kaynak madde": QA dosyası v1 — yalnız test, madde 7
|
||||||
|
- paragraph: Resmi dokümandan eksik belge gereksinimi ekle
|
||||||
|
- text: Belge adı
|
||||||
|
- textbox "Belge adı"
|
||||||
|
- text: Kaynak doküman ve madde
|
||||||
|
- textbox "Kaynak doküman ve madde"
|
||||||
|
- button "Belge gereksinimi ekle" [disabled]
|
||||||
|
- generic "Sentetik test kalemi — ürün/hizmet ve yükümlülük kontrolü"
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Uygun" [selected]
|
||||||
|
- option "Uygun değil"
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- option "Kontrol gerekli"
|
||||||
|
- option "Doğrulandı"
|
||||||
|
- option "Uygulanmıyor — gerekçe gerekli" [selected]
|
||||||
|
- generic: Teklif toplamı (KDV hariç)
|
||||||
|
- generic: 251,00 TL
|
||||||
|
- paragraph: "Yazı ile: ikiyüzellibir Türk Lirası"
|
||||||
|
- heading "Hazırlanacak belgeler" [level=5]
|
||||||
|
- button "Teklif Mektubu Rakam ve yazı ile bedel Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Teklif Mektubu
|
||||||
|
- generic: Rakam ve yazı ile bedel
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- button "Birim Fiyat Teklif Cetveli 1 kalem Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Birim Fiyat Teklif Cetveli
|
||||||
|
- generic: 1 kalem
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- button "Evrak Kontrol Listesi 1 belge Yazdır / PDF" [disabled]:
|
||||||
|
- generic: Evrak Kontrol Listesi
|
||||||
|
- generic: 1 belge
|
||||||
|
- text: Yazdır / PDF
|
||||||
|
- paragraph: Tutar, kaynak, belge ve ürün uygunluğu kontrollerini tamamlayın.
|
||||||
|
- paragraph: Belgeler taslaktır ve idarenin standart formlarının yerine geçmez. İmzalamadan önce ihale dokümanındaki form metinleriyle karşılaştırın.
|
||||||
|
- button
|
||||||
|
- button "Kullanım kılavuzu (? tuşu)":
|
||||||
|
- region "Notifications (F8)":
|
||||||
|
- list
|
||||||
@@ -63,9 +63,17 @@ Tarayıcı kanıtı: [Yenileme sonrası beş kart](evidence/qa-kanban-after-relo
|
|||||||
- Açık kalan Kanban, sağ panel ve takvim tarih etiketleri 30 saniyede bir güncellenir. Ürün aramasındaki geçmiş tarih filtresi aynı kaynak saatini kullanır; filtre etiketi bilinmeyen saati başvuruya açık garantisi gibi sunmaz.
|
- Açık kalan Kanban, sağ panel ve takvim tarih etiketleri 30 saniyede bir güncellenir. Ürün aramasındaki geçmiş tarih filtresi aynı kaynak saatini kullanır; filtre etiketi bilinmeyen saati başvuruya açık garantisi gibi sunmaz.
|
||||||
- Üç tarih testi gün farkını, Türkiye'de yıl/gece geçişini, tam kapanış anını, eksik saat ve artık günleri doğrular. Google/Outlook gerçek içe aktarımı, zeyilname güncellemesi ve bütün sunucu bildirim zamanlayıcıları henüz bu kapsamda doğrulanmadı.
|
- Üç tarih testi gün farkını, Türkiye'de yıl/gece geçişini, tam kapanış anını, eksik saat ve artık günleri doğrular. Google/Outlook gerçek içe aktarımı, zeyilname güncellemesi ve bütün sunucu bildirim zamanlayıcıları henüz bu kapsamda doğrulanmadı.
|
||||||
|
|
||||||
|
## Tarayıcı doğrulaması ve virgüllü fiyat hatası
|
||||||
|
|
||||||
|
- Geçici normal hesapla Giresun adlı sentetik kayıtta firma alanları, 120 gün, fiyat ve manuel belge gereksinimi dolduruldu; kaydetme/yenileme sonrası geri geldi. Eksik kontroller varken çıktı düğmeleri kapalı kaldı.
|
||||||
|
- Ürün modeli, teknik kaynak, belge doğrulaması, sorumlu ve dört sağlık yükümlülüğü kontrolü kaydedilip yenilemeden sonra ekrandan doğrulandı. Resmi kaynak onayı verilmedi; gerçek başvuru veya resmi uygunluk iddiası yok.
|
||||||
|
- Tarayıcı testi yeni P1 kusuru ortaya çıkardı: `125,50` girdisi virgül silinerek `12550` oluyordu. Girdi artık değiştirilmeden ortak fiyat ayrıştırıcısına gider; nokta/virgül ondalığı desteklenir, geçersiz metin reddedilir. PDF HTML'sinde de aynı ayrıştırıcı kullanılır, birim fiyat hassasiyeti korunur.
|
||||||
|
- Düzeltme sonrasında tarayıcıda `125,50 × 2 = 251,00 TL` görüldü ve yeniden yüklemede korundu. Bir otomatik test ekran hesabı/PDF HTML tutarlılığı, geçersiz sayı ve aşırı tutarı kapsar.
|
||||||
|
- Kanıtlar: [taslak yenilemesi](evidence/qa-draft-after-reload.txt), [virgüllü fiyat](evidence/qa-turkish-price-fixed.txt), [kontrol yenilemesi](evidence/qa-checks-after-reload.txt).
|
||||||
|
|
||||||
## Doğrulama
|
## Doğrulama
|
||||||
|
|
||||||
`npm test`: 30/30 geçti (son yeniden çalıştırma: 6 Eylül 2026).
|
`npm test`: 31/31 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.
|
`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.
|
`node --check server/index.js`, `node --check server/user-store.mjs`, `git diff --check`: geçti.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { bidRowTotal, validateBidRows, roundMoney } from '../services/bidValidation';
|
import { bidRowTotal, validateBidRows, roundMoney, parseBidPrice } from '../services/bidValidation';
|
||||||
import { sourceRows, bidSourceSnapshot, restoreBidPrices } from '../services/bidSource';
|
import { sourceRows, bidSourceSnapshot, restoreBidPrices } from '../services/bidSource';
|
||||||
import { PreparationChecks } from './PreparationChecks';
|
import { PreparationChecks } from './PreparationChecks';
|
||||||
import { preparationBlockers, documentKey } from '../services/preparationChecks';
|
import { preparationBlockers, documentKey } from '../services/preparationChecks';
|
||||||
@@ -91,7 +91,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
|
|||||||
else setHata('Taslak kaydedilemedi. Tekrar deneyin.');
|
else setHata('Taslak kaydedilemedi. Tekrar deneyin.');
|
||||||
};
|
};
|
||||||
|
|
||||||
const toplam = teklifTuru === 'goturu' ? (Number(goturuBedel) || 0) : kalemToplami;
|
const toplam = teklifTuru === 'goturu' ? (parseBidPrice(goturuBedel) || 0) : kalemToplami;
|
||||||
|
|
||||||
const eksikler = ALANLAR.filter((a) => a.zorunlu && !String(firma[a.k] || '').trim());
|
const eksikler = ALANLAR.filter((a) => a.zorunlu && !String(firma[a.k] || '').trim());
|
||||||
const kalemEksik = teklifTuru === 'birim' && validateBidRows(satirlar).length > 0;
|
const kalemEksik = teklifTuru === 'birim' && validateBidRows(satirlar).length > 0;
|
||||||
@@ -246,7 +246,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
|
|||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
value={goturuBedel}
|
value={goturuBedel}
|
||||||
onChange={(e) => setGoturuBedel(e.target.value.replace(/[^\d.]/g, ''))}
|
onChange={(e) => setGoturuBedel(e.target.value)}
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
className="w-full sm:w-64 text-sm font-bold px-3 py-2 rounded-lg border border-slate-300"
|
className="w-full sm:w-64 text-sm font-bold px-3 py-2 rounded-lg border border-slate-300"
|
||||||
/>
|
/>
|
||||||
@@ -254,6 +254,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
|
|||||||
) : kalemler.length > 0 ? (
|
) : kalemler.length > 0 ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h5 className="font-extrabold text-xs text-slate-900">Kalem fiyatlandırma</h5>
|
<h5 className="font-extrabold text-xs text-slate-900">Kalem fiyatlandırma</h5>
|
||||||
|
<p className="text-xs text-slate-500">Ondalık fiyatı 125,50 veya 125.50 biçiminde yazın; binlik ayırıcı kullanmayın.</p>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full text-xs min-w-[520px]">
|
<table className="w-full text-xs min-w-[520px]">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -278,7 +279,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
|
|||||||
<input
|
<input
|
||||||
value={fiyatlar[r.priceKey] ?? ''}
|
value={fiyatlar[r.priceKey] ?? ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFiyatlar((o) => ({ ...o, [r.priceKey]: e.target.value.replace(/[^\d.]/g, '') }))}
|
setFiyatlar((o) => ({ ...o, [r.priceKey]: e.target.value }))}
|
||||||
placeholder={r.estimatedUnitPrice != null ? String(r.estimatedUnitPrice) : '0'}
|
placeholder={r.estimatedUnitPrice != null ? String(r.estimatedUnitPrice) : '0'}
|
||||||
className="w-28 text-xs text-right px-2 py-1 rounded border border-slate-300"
|
className="w-28 text-xs text-right px-2 py-1 rounded border border-slate-300"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { quantityParts, bidRowTotal, validateBidRows } from './bidValidation.js';
|
import { quantityParts, parseBidPrice, bidRowTotal, validateBidRows } from './bidValidation.js';
|
||||||
/**
|
/**
|
||||||
* İhale dosyası belgeleri — teklif mektubu, birim fiyat teklif cetveli ve
|
* İhale dosyası belgeleri — teklif mektubu, birim fiyat teklif cetveli ve
|
||||||
* evrak kontrol listesi.
|
* evrak kontrol listesi.
|
||||||
@@ -162,7 +162,7 @@ export function teklifCetveliHtml(f, satirlar, toplam) {
|
|||||||
if (errors.length) throw new Error(errors.join(" "));
|
if (errors.length) throw new Error(errors.join(" "));
|
||||||
const govde = satirlar.map((r, i) => {
|
const govde = satirlar.map((r, i) => {
|
||||||
const { miktar, birim } = miktarAyristir(r.quantity);
|
const { miktar, birim } = miktarAyristir(r.quantity);
|
||||||
const bf = Number(r.birimFiyat) || 0;
|
const bf = parseBidPrice(r.birimFiyat);
|
||||||
const tutar = bidRowTotal(r);
|
const tutar = bidRowTotal(r);
|
||||||
return `<tr>
|
return `<tr>
|
||||||
<td class="s">${i + 1}</td>
|
<td class="s">${i + 1}</td>
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
// Preserve typed input; never delete a decimal separator and change its value.
|
||||||
|
export function parseBidPrice(value) {
|
||||||
|
if (typeof value === 'number') return Number.isFinite(value) && value >= 0 ? value : null;
|
||||||
|
const text = String(value ?? '').trim();
|
||||||
|
const plain = /^\d+(?:[.,]\d{1,8})?$/;
|
||||||
|
const groupedTurkish = /^\d{1,3}(?:\.\d{3})+,\d{1,8}$/;
|
||||||
|
if (!plain.test(text) && !groupedTurkish.test(text)) return null;
|
||||||
|
const normalized = text.includes(',') ? text.replace(/\./g,'').replace(',','.') : text;
|
||||||
|
const result = Number(normalized);
|
||||||
|
return Number.isFinite(result) ? result : null;
|
||||||
|
}
|
||||||
export function quantityParts(value) {
|
export function quantityParts(value) {
|
||||||
if (typeof value === 'number') return {miktar:Number.isFinite(value) ? value : null,birim:''};
|
if (typeof value === 'number') return {miktar:Number.isFinite(value) ? value : null,birim:''};
|
||||||
const match = /^\s*([+-]?[\d]+(?:[.,]\d+)*)\s+(.+?)\s*$/.exec(String(value || ''));
|
const match = /^\s*([+-]?[\d]+(?:[.,]\d+)*)\s+(.+?)\s*$/.exec(String(value || ''));
|
||||||
@@ -11,8 +22,9 @@ export function quantityParts(value) {
|
|||||||
export const roundMoney = n => Math.round((n + Number.EPSILON) * 100) / 100;
|
export const roundMoney = n => Math.round((n + Number.EPSILON) * 100) / 100;
|
||||||
export function bidRowTotal(row) {
|
export function bidRowTotal(row) {
|
||||||
const {miktar} = quantityParts(row.quantity);
|
const {miktar} = quantityParts(row.quantity);
|
||||||
const price = Number(row.birimFiyat);
|
const price = parseBidPrice(row.birimFiyat);
|
||||||
return miktar > 0 && Number.isFinite(price) && price > 0 ? roundMoney(miktar * price) : null;
|
const total = miktar * price;
|
||||||
|
return miktar > 0 && price > 0 && Number.isFinite(total) && total <= Number.MAX_SAFE_INTEGER / 100 ? roundMoney(total) : null;
|
||||||
}
|
}
|
||||||
export function validateBidRows(rows) {
|
export function validateBidRows(rows) {
|
||||||
if (!rows.length) return ['Resmi teklif cetvelindeki kalemler eksik.'];
|
if (!rows.length) return ['Resmi teklif cetvelindeki kalemler eksik.'];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {test} from 'node:test';
|
import {test} from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import {quantityParts,validateBidRows,bidRowTotal} from '../src/services/bidValidation.js';
|
import {quantityParts,validateBidRows,bidRowTotal,parseBidPrice} from '../src/services/bidValidation.js';
|
||||||
import {yaziylaTL,teklifCetveliHtml} from '../src/services/bidDocs.js';
|
import {yaziylaTL,teklifCetveliHtml} from '../src/services/bidDocs.js';
|
||||||
test('Turkish quantities, decimal quantities and units are preserved',()=>{
|
test('Turkish quantities, decimal quantities and units are preserved',()=>{
|
||||||
assert.deepEqual(quantityParts('2.138.330 puan'),{miktar:2138330,birim:'puan'});
|
assert.deepEqual(quantityParts('2.138.330 puan'),{miktar:2138330,birim:'puan'});
|
||||||
@@ -22,3 +22,15 @@ test('100 rows retain Turkish names and escaped content in printable sheet',()=>
|
|||||||
assert.equal((out.match(/Ölçüm <kit>/g)||[]).length,100);
|
assert.equal((out.match(/Ölçüm <kit>/g)||[]).length,100);
|
||||||
assert.ok(out.includes('İğdır'));assert.ok(out.includes('2.000,00'));
|
assert.ok(out.includes('İğdır'));assert.ok(out.includes('2.000,00'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Turkish decimal price is identical in row total and printable sheet',()=>{
|
||||||
|
const row={name:'Kit',quantity:'2 adet',birimFiyat:'125,50'};
|
||||||
|
assert.equal(parseBidPrice(row.birimFiyat),125.5);
|
||||||
|
assert.equal(parseBidPrice('1.250,50'),1250.5);
|
||||||
|
assert.equal(bidRowTotal(row),251);
|
||||||
|
const html=teklifCetveliHtml({},[row],251);
|
||||||
|
assert.ok(html.includes('125,50'));assert.ok(html.includes('251,00'));
|
||||||
|
assert.ok(!html.includes('25.100'));
|
||||||
|
for(const value of ['', '125,,50','1e3','125 TL','1.2.3','-20','125,']) assert.equal(parseBidPrice(value),null);
|
||||||
|
assert.equal(bidRowTotal({quantity:'1000000000 adet',birimFiyat:'999999999999999'}),null);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user