Add saved bid preparation overview with actionable missing checks

This commit is contained in:
2026-09-06 13:26:29 +03:00
parent 2e98e5ad4f
commit 30ce6e3a6c
9 changed files with 168 additions and 13 deletions
+11
View File
@@ -0,0 +1,11 @@
export const bidFirmFields = [
{ k: 'firmaUnvan', l: 'Firma ticaret unvanı', zorunlu: true, genis: true },
{ k: 'adres', l: 'Tebligata esas adres', zorunlu: true, genis: true },
{ k: 'vergiDairesi', l: 'Vergi dairesi', zorunlu: true },
{ k: 'vergiNo', l: 'Vergi numarası', zorunlu: true },
{ k: 'sicilNo', l: 'Ticaret sicil / MERSİS no', zorunlu: false },
{ k: 'yetkiliAd', l: 'Yetkili ad soyad', zorunlu: true },
{ k: 'yetkiliGorev', l: 'Yetkilinin görevi', zorunlu: true, ph: 'Örn. Genel Müdür' },
{ k: 'telefon', l: 'Telefon', zorunlu: true },
{ k: 'eposta', l: 'E-posta', zorunlu: true },
];
+41
View File
@@ -0,0 +1,41 @@
import {bidFirmFields} from './bidFirmFields.js';
import {sourceRows,bidSourceOptions,bidSourceSnapshot,restoreBidPrices} from './bidSource.js';
import {selectBidLots} from './bidLots.js';
import {bidTotal,bidAmount,validateBidRows} from './bidValidation.js';
import {bidTypeForForm,bidValidityForForm} from './bidTerms.js';
import {documentKey,preparationBlockers} from './preparationChecks.js';
import {preparationDeadlineProblem} from './preparationDeadline.js';
import {tenderDateKey} from './tenderCalendar.js';
// Saved draft diagnostics only. A clean summary still requires live output checks.
export function preparationSummary(tender,vault,now=new Date()) {
const draft=tender.bidDraft || {},analysis=draft.analysisSource || {},issues=[];
const add=(target,text)=>issues.push({target,text});
const deadline=preparationDeadlineProblem(tender,now);
if(deadline)add('source',deadline);
const documents=[...new Map([...(analysis.documentChecklist || []),...(draft.manualDocuments || [])].map(d=>[documentKey(d),d])).values()];
const itemSource=draft.itemSource || {},partial=draft.partialBid || {enabled:false,source:'',selected:[],assignments:{}};
const options=bidSourceOptions(tender,analysis,itemSource),rows=sourceRows(tender,analysis,options.selected || 'tender');
const selection=selectBidLots(rows,partial);
const snapshot=JSON.stringify([bidSourceSnapshot(tender,{...analysis,documentChecklist:documents},rows),partial,itemSource,options.signature]);
if(draft.reviewedSource!==snapshot)add('source','Resmi kaynak karşılaştırması gerekli.');
if(analysis.sourceReview?.status==='partial')add('analysis','Analiz kısmi; kaynak kontrolünü tamamlayın.');
if(options.needsSelection)add('itemSource','Resmi cetvele uygun kalem listesini seçin.');
for(const field of bidFirmFields)if(field.zorunlu&&!String(draft.firma?.[field.k] || '').trim())add(`firm:${field.k}`,`${field.l} kayıtlı taslakta eksik.`);
const type=bidTypeForForm(tender.aciklama,analysis.procurementTerms?.bidType),bidType=draft.teklifTuru || type.type || '';
if(!['birim','goturu'].includes(bidType)||type.status==='conflict'||(type.type&&type.type!==bidType))add('bidType','Teklif türünü doğrulayın.');
const validity=bidValidityForForm(tender.aciklama,analysis.procurementTerms?.bidValidity),days=Number(draft.gecerlilikGun || validity.days || '');
if(!Number.isInteger(days)||days<=0||validity.status==='conflict'||(validity.days&&days<validity.days))add('validity','Teklif geçerlilik süresini doğrulayın.');
selection.errors.forEach(text=>add('lots',text));
const prices=restoreBidPrices(draft),priced=selection.rows.map(row=>({...row,birimFiyat:prices[row.priceKey]}));
if(bidType==='birim') {
if(!priced.length)add('pricing','Resmi teklif kalemleri eksik.');
priced.forEach((row,i)=>{if(validateBidRows([row]).length)add(`price:${row.priceKey}`,`${i+1}. kalemin miktar, birim veya fiyatını tamamlayın.`);});
if(priced.length&&!validateBidRows(priced).length&&bidTotal(priced)===null)add('pricing','Genel toplam geçersiz veya hesaplama sınırını aşıyor.');
}
if(bidType==='goturu'&&bidAmount(draft.goturuBedel)===null)add('lumpSum','Toplam teklif bedelini kontrol edin.');
const checkedVault={...vault,deadlineDate:tenderDateKey(tender),tenderIkn:tender.ikn};
const documentIssues=preparationBlockers([],documents,draft.checks,checkedVault);
preparationBlockers(selection.rows,documents,draft.checks,checkedVault).forEach(text=>add('checks',text));
return {issues,documentIssues,owner:tender.atananPersonel || 'Atanmadı',next:issues[0] || {target:'source',text:'Dosyayı açıp son kontrolleri yapın.'}};
}