// 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) { if (typeof value === 'number') return {miktar:Number.isFinite(value) ? value : null,birim:''}; const match = /^\s*([+-]?[\d]+(?:[.,]\d+)*)\s+(.+?)\s*$/.exec(String(value || '')); if (!match) return {miktar:null,birim:''}; const raw = match[1]; const normalized = raw.includes(',') ? raw.replace(/\./g,'').replace(',','.') : /^\d{1,3}(?:\.\d{3})+$/.test(raw) ? raw.replace(/\./g,'') : raw; const number = Number(normalized); return {miktar:Number.isFinite(number) ? number : null,birim:match[2]}; } export const roundMoney = n => Math.round((n + Number.EPSILON) * 100) / 100; export function bidRowTotal(row) { const {miktar} = quantityParts(row.quantity); const price = parseBidPrice(row.birimFiyat); 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) { if (!rows.length) return ['Resmi teklif cetvelindeki kalemler eksik.']; return rows.flatMap((row,i) => { const {miktar,birim} = quantityParts(row.quantity); return (!row.name?.trim() || !(miktar > 0) || !birim || bidRowTotal(row) === null) ? [`${i+1}. kalemin adı, miktarı, birimi veya fiyatı eksik/geçersiz.`] : []; }); }