From 04f24d78a396357d4f3f41e4e7e548cc5881620a Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 16:32:16 +0300 Subject: [PATCH] fix: separate item price analysis by unit --- docs/quality/uygulama-durumu.md | 6 ++++++ src/services/analytics.js | 10 ++++++++-- tests/review-state.test.mjs | 10 ++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 948b6ee..2ec2561 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -1324,6 +1324,12 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Analitik `parseDateTR`, JavaScript tarih yuvarlamasıyla 31 Şubat gibi imkânsız günleri geçerli tarihe dönüştürmüyordu; artık gün/ay/yıl bileşenlerini doğrulayıp geçersiz girdide `null` döndürüyor. Geçersiz tarihler yanlış hafta sonu veya kısa süre riskine dönüşmüyor. - 31.02.2026 reddi, artık yıl 29.02.2028 ve eğik çizgili geçerli tarih için regresyon testleri eklendi. 281 test ve build başarılı. Genel plan açık. +### 2026-09-06 — Fiyat analizinde birim ve miktar ayrımı + +- D08 kapsamında `itemStats` aynı ürün adının kutu, adet veya başka birimlerdeki fiyatlarını artık tek medyanda birleştirmiyor. İstatistik anahtarı ürün + birim olarak ayrıldı. +- Miktarı veya birimi kaynakta bilinmeyen kalemler fiyat analizine alınmıyor; böylece birim fiyat karşılaştırması doğrulanmamış kapsamla öneri üretmiyor. +- Kutu/adet ve bilinmeyen miktar regresyonu eklendi. 282 test ve build başarılı. Gerçek pazar verisiyle fiyat karşılaştırması ve kur kaynağı kabulü hâlâ plan maddesi; genel plan açık. + ### 2026-09-06 — Belge son kullanma tarihi doğrulaması - Belge deposundaki son kullanma tarihi göstergesi artık yalnız geçerli ISO günlerini hesaplıyor. Bozuk veya yuvarlanabilir bir tarih sessizce gün hesabına girmiyor; kullanıcıya “Tarih doğrulanmalı” uyarısı gösteriliyor. diff --git a/src/services/analytics.js b/src/services/analytics.js index ca9da64..3bf2bc1 100644 --- a/src/services/analytics.js +++ b/src/services/analytics.js @@ -83,6 +83,8 @@ export function firmStats(tenders) { }; } +import { quantityParts } from './bidValidation.js'; + // --- Kalem bazlı fiyat analizi --- export function itemStats(tenders) { const map = new Map(); @@ -92,8 +94,12 @@ export function itemStats(tenders) { const bf = parseTL(it.birimFiyat); if (bf == null) continue; itemCount++; - const key = (it.ad || 'Diğer').trim().toLocaleUpperCase('tr-TR'); - const cur = map.get(key) || { ad: it.ad, birim: it.birim, count: 0, prices: [] }; + const quantity=quantityParts(it.miktar ?? it.quantity); + const rawUnit=quantity.birim || it.birim || ''; + if (quantity.miktar == null || !String(rawUnit).trim()) { itemCount--; continue; } + const unit=String(rawUnit).trim().toLocaleLowerCase('tr-TR'); + const key = `${(it.ad || 'Diğer').trim().toLocaleUpperCase('tr-TR')}||${unit}`; + const cur = map.get(key) || { ad: it.ad, birim: unit, count: 0, prices: [] }; cur.count++; cur.prices.push(bf); map.set(key, cur); diff --git a/tests/review-state.test.mjs b/tests/review-state.test.mjs index 0fb9489..13736be 100644 --- a/tests/review-state.test.mjs +++ b/tests/review-state.test.mjs @@ -6,6 +6,16 @@ test('analytics rejects impossible Turkish calendar dates instead of rolling the assert.equal(parseDateTR('29.02.2028')?.getDate(),29); assert.equal(parseDateTR('30/04/2026')?.getMonth(),3); }); +test('item price analysis separates units and ignores unknown quantities',async()=>{ + const {itemStats}=await import('../src/services/analytics.js'); + const result=itemStats([{items:[ + {ad:'Test kiti',miktar:'2 kutu',birimFiyat:'10 TL'}, + {ad:'Test kiti',miktar:'3 adet',birimFiyat:'12 TL'}, + {ad:'Test kiti',miktar:'bilinmiyor',birimFiyat:'99 TL'}, + ]}]); + assert.equal(result.itemCount,2); assert.equal(result.uniqueItems,2); + assert.deepEqual(result.items.map(x=>x.birim).sort(),['adet','kutu']); +}); import {hizliFiltreGecer} from '../src/services/ihaleTarih.js'; import {applyTenderStates} from '../src/services/tenderState.js'; test('review counter uses the same explicit state as reviewed and unread filters',()=>{