feat: validate preparation against owned document files

This commit is contained in:
2026-09-06 08:53:41 +03:00
parent 0d332afdb1
commit 953d3b5c1e
7 changed files with 91 additions and 11 deletions
+20 -4
View File
@@ -1,4 +1,6 @@
import React, { useState, useMemo } from 'react';
import { listDocuments, downloadDocument } from '../services/account';
import { tenderDateKey } from '../services/tenderCalendar';
import React, { useState, useMemo, useEffect } from 'react';
import { bidRowTotal, validateBidRows, roundMoney, parseBidPrice } from '../services/bidValidation';
import { sourceRows, bidSourceSnapshot, restoreBidPrices } from '../services/bidSource';
import { rowLot, selectBidLots } from '../services/bidLots';
@@ -42,6 +44,15 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
const { userProfile, mutateTender, savingCount, authUser, tenders } = useApp();
const tender = {...selectedTender, ...tenders.find(t => t.id === selectedTender?.id)};
const storedDraft = tender?.bidDraft || {};
const [vault,setVault] = useState({loading:true,error:null,documents:[]});
const [vaultRefresh,setVaultRefresh] = useState(0);
useEffect(() => {
let cancelled=false;
setVault({loading:true,error:null,documents:[]});
listDocuments().then(data=>{if(!cancelled)setVault({loading:false,error:null,documents:data.belgeler || []});})
.catch(error=>{if(!cancelled)setVault({loading:false,error:error.message,documents:[]});});
return ()=>{cancelled=true;};
},[authUser?.email,vaultRefresh]);
const [dirty, setDirty] = useState(false);
const [acik, setAcik] = useState(true);
const [hata, setHata] = useState(null);
@@ -101,7 +112,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
const kalemEksik = teklifTuru === 'birim' && validateBidRows(satirlar).length > 0;
const turCeliskisi = teklifTuru === 'goturu' && /birim fiyat/i.test(tender?.aciklama || '');
const sureEksik = !(Number(gecerlilikGun) > 0) || (kaynakGecerlilik && Number(gecerlilikGun) < Number(kaynakGecerlilik[1]));
const kontrolEksikleri = preparationBlockers(lotSelection.rows, documents, checks);
const kontrolEksikleri = preparationBlockers(lotSelection.rows, documents, checks, {...vault,deadlineDate:tenderDateKey(tender)});
const hazir = eksikler.length === 0 && toplam > 0 && !kalemEksik && !turCeliskisi && !sureEksik && !kaynakKontrolGerekli && kontrolEksikleri.length === 0 && lotSelection.errors.length === 0;
const formVerisi = () => ({
@@ -115,10 +126,15 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
ihaleTarihi: tender?.ihaleTarihi || tender?.tarih || '',
});
const uret = (tip) => {
const uret = async (tip) => {
setHata(null);
try {
if (!hazir) throw new Error('Kalem, miktar, birim, fiyat, firma ve geçerlilik süresindeki eksikleri giderin.');
const currentFiles = await listDocuments();
const currentVault = {loading:false,error:null,documents:currentFiles.belgeler || [],deadlineDate:tenderDateKey(tender)};
setVault(currentVault);
const fileErrors = preparationBlockers(lotSelection.rows, documents, checks, currentVault);
if (fileErrors.length) throw new Error(fileErrors.join(' '));
const f = formVerisi();
if (tip === 'mektup') belgeYazdir('Teklif Mektubu', teklifMektubuHtml(f, toplam));
if (tip === 'cetvel') belgeYazdir('Birim Fiyat Teklif Cetveli', teklifCetveliHtml(f, satirlar, toplam));
@@ -332,7 +348,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
)}
<PreparationChecks rows={lotSelection.rows} documents={documents} manualDocuments={manualDocuments}
setManualDocuments={value => {setManualDocuments(value);setDirty(true);}} checks={checks} setChecks={setChecks}/>
setManualDocuments={value => {setManualDocuments(value);setDirty(true);}} checks={checks} setChecks={setChecks} vault={vault} refreshVault={()=>setVaultRefresh(n=>n+1)} downloadFile={(file)=>downloadDocument(file.id,file.ad).catch(e=>setHata(e.message))}/>
{kontrolEksikleri.length > 0 && <details className="bg-amber-50 border border-amber-300 rounded-xl p-3">
<summary className="text-xs font-bold cursor-pointer">{kontrolEksikleri.length} belge/uygunluk kontrolü tamamlanmalı</summary>
<ul className="text-xs list-disc pl-5 mt-2 space-y-1">{kontrolEksikleri.map((message,i)=><li key={i}>{message}</li>)}</ul>