fix: separate item price analysis by unit
This commit is contained in:
@@ -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.
|
- 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.
|
- 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ı
|
### 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.
|
- 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.
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ export function firmStats(tenders) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { quantityParts } from './bidValidation.js';
|
||||||
|
|
||||||
// --- Kalem bazlı fiyat analizi ---
|
// --- Kalem bazlı fiyat analizi ---
|
||||||
export function itemStats(tenders) {
|
export function itemStats(tenders) {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
@@ -92,8 +94,12 @@ export function itemStats(tenders) {
|
|||||||
const bf = parseTL(it.birimFiyat);
|
const bf = parseTL(it.birimFiyat);
|
||||||
if (bf == null) continue;
|
if (bf == null) continue;
|
||||||
itemCount++;
|
itemCount++;
|
||||||
const key = (it.ad || 'Diğer').trim().toLocaleUpperCase('tr-TR');
|
const quantity=quantityParts(it.miktar ?? it.quantity);
|
||||||
const cur = map.get(key) || { ad: it.ad, birim: it.birim, count: 0, prices: [] };
|
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.count++;
|
||||||
cur.prices.push(bf);
|
cur.prices.push(bf);
|
||||||
map.set(key, cur);
|
map.set(key, cur);
|
||||||
|
|||||||
@@ -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('29.02.2028')?.getDate(),29);
|
||||||
assert.equal(parseDateTR('30/04/2026')?.getMonth(),3);
|
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 {hizliFiltreGecer} from '../src/services/ihaleTarih.js';
|
||||||
import {applyTenderStates} from '../src/services/tenderState.js';
|
import {applyTenderStates} from '../src/services/tenderState.js';
|
||||||
test('review counter uses the same explicit state as reviewed and unread filters',()=>{
|
test('review counter uses the same explicit state as reviewed and unread filters',()=>{
|
||||||
|
|||||||
Reference in New Issue
Block a user