fix: invalidate pending bid output when preparation changes

This commit is contained in:
2026-09-06 08:54:22 +03:00
parent 953d3b5c1e
commit 2dfd32aa9c
3 changed files with 35 additions and 10 deletions
+27 -8
View File
@@ -1,6 +1,7 @@
import { getToken } from '../services/auth';
import { listDocuments, downloadDocument } from '../services/account';
import { tenderDateKey } from '../services/tenderCalendar';
import React, { useState, useMemo, useEffect } from 'react';
import React, { useState, useMemo, useEffect, useRef } from 'react';
import { bidRowTotal, validateBidRows, roundMoney, parseBidPrice } from '../services/bidValidation';
import { sourceRows, bidSourceSnapshot, restoreBidPrices } from '../services/bidSource';
import { rowLot, selectBidLots } from '../services/bidLots';
@@ -100,9 +101,16 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
);
const kalemToplami = useMemo(() => roundMoney(satirlar.reduce((t,r) => t + (bidRowTotal(r) ?? 0),0)), [satirlar]);
const latestDraft = useRef('');
latestDraft.current = JSON.stringify([firma,teklifTuru,gecerlilikGun,kisim,goturuBedel,fiyatlar,sourceSnapshot,reviewedSource,checks,manualDocuments,partialBid]);
const mounted = useRef(true);
const printWindow = useRef(null);
const [printing,setPrinting] = useState(false);
useEffect(()=>{mounted.current=true;return ()=>{mounted.current=false;printWindow.current?.close();};},[]);
const saveDraft = async () => {
const submitted = latestDraft.current;
const saved = await mutateTender(tender.id, {bidDraft:{firma,teklifTuru,gecerlilikGun,kisim,goturuBedel,priceSchema:1,pricesBySource:fiyatlar,sourceSnapshot,reviewedSource,checks,manualDocuments,partialBid,analysisSource:{itemsAndSpecs:effectiveResults.itemsAndSpecs || [],documentChecklist:effectiveResults.documentChecklist || [],documentVersion:effectiveResults.documentVersion || null}}});
if (saved) { setDirty(false); setHata(null); }
if (saved && mounted.current) { setDirty(latestDraft.current !== submitted); setHata(null); }
else setHata('Taslak kaydedilemedi. Tekrar deneyin.');
};
@@ -127,21 +135,32 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
});
const uret = async (tip) => {
if (printWindow.current) return;
const submitted = latestDraft.current;
const session = getToken();
setHata(null);
try {
if (!hazir) throw new Error('Kalem, miktar, birim, fiyat, firma ve geçerlilik süresindeki eksikleri giderin.');
// Open during the click; async validation must not lose popup activation.
printWindow.current = belgeYazdir('Dosya kontrolü', '<p>Belgeler kontrol ediliyor…</p>');
setPrinting(true);
const currentFiles = await listDocuments();
if (!mounted.current || session !== getToken() || submitted !== latestDraft.current)
throw new Error('Form, kaynak veya oturum değişti. Güncel bilgilerle yeniden çıktı oluşturun.');
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));
if (tip === 'liste') belgeYazdir('Evrak Kontrol Listesi', evrakListesiHtml(f, documents));
if (tip === 'mektup') belgeYazdir('Teklif Mektubu', teklifMektubuHtml(f, toplam), printWindow.current);
if (tip === 'cetvel') belgeYazdir('Birim Fiyat Teklif Cetveli', teklifCetveliHtml(f, satirlar, toplam), printWindow.current);
if (tip === 'liste') belgeYazdir('Evrak Kontrol Listesi', evrakListesiHtml(f, documents), printWindow.current);
printWindow.current = null;
} catch (e) {
setHata(e.message);
}
printWindow.current?.close();
printWindow.current = null;
if(mounted.current) setHata(e.message);
} finally { if(mounted.current) setPrinting(false); }
};
return (
@@ -389,7 +408,7 @@ export const BidPackageBuilder = ({ results, tender: selectedTender }) => {
<button
key={b.t}
type="button"
disabled={!hazir}
disabled={!hazir || printing}
onClick={() => uret(b.t)}
className="text-left bg-white border border-slate-300 hover:border-emerald-500 hover:bg-emerald-50/50 disabled:opacity-40 disabled:hover:border-slate-300 disabled:hover:bg-white rounded-xl p-3 transition-colors"
>
+4 -2
View File
@@ -84,9 +84,11 @@ const STIL = `
`;
/** Belgeyi kendi penceresinde açıp yazdırma diyaloğunu tetikler. */
export function belgeYazdir(baslik, govde) {
const w = window.open('', '_blank', 'width=900,height=1000');
export function belgeYazdir(baslik, govde, existingWindow) {
const w = existingWindow || window.open('', '_blank', 'width=900,height=1000');
if (!w) throw new Error('Tarayıcı yeni pencereyi engelledi. Açılır pencere iznini verip tekrar deneyin.');
if(w.closed) throw new Error('Çıktı penceresi kapatıldı. Yeniden deneyin.');
w.document.open();
w.document.write(`<!doctype html><html lang="tr"><head><meta charset="utf-8">
<title>${kacir(baslik)}</title><style>${STIL}</style></head><body>${govde}
<div class="yazdirma" style="text-align:center;margin-top:10mm">