hazirlik ozetinde belge sorumlularini goster
Quality gates / test-build (push) Canceled after 0s
Quality gates / container (push) Canceled after 0s

This commit is contained in:
2026-09-06 21:33:30 +03:00
parent 6436dcb34b
commit 821df474ea
4 changed files with 16 additions and 1 deletions
+6
View File
@@ -2412,3 +2412,9 @@ Doğrulama: `npm test` 305/305, `npm run build` ve `npm run qa:browser` başarı
Hazırlık ekranındaki her belge gereksinimine, ürün/servis kontrollerindeki sorumlu alanıyla aynı biçimde kişi veya ekip atama alanı eklendi. Sorumlu bilgisi taslak içindeki `checks.documents` kaydında saklanıyor; mevcut kayıtları geriye dönük zorunlu kılmıyor ve belge doğrulama koşullarını gevşetmiyor. Böylece beş dosyada eksik belge ile işi yapacak kişi aynı akışta görülebiliyor. Hazırlık ekranındaki her belge gereksinimine, ürün/servis kontrollerindeki sorumlu alanıyla aynı biçimde kişi veya ekip atama alanı eklendi. Sorumlu bilgisi taslak içindeki `checks.documents` kaydında saklanıyor; mevcut kayıtları geriye dönük zorunlu kılmıyor ve belge doğrulama koşullarını gevşetmiyor. Böylece beş dosyada eksik belge ile işi yapacak kişi aynı akışta görülebiliyor.
Doğrulama: `npm test` 305/305, `npm run build` ve `npm run qa:browser` başarılı. Gitea `main`e gönderim ve Coolify rollout kimlik doğrulaması ayrıca bekliyor. Doğrulama: `npm test` 305/305, `npm run build` ve `npm run qa:browser` başarılı. Gitea `main`e gönderim ve Coolify rollout kimlik doğrulaması ayrıca bekliyor.
### 2026-09-06 · Hazırlık özetinde belge sorumluları
Belge gereksinimlerine eklenen sorumlu bilgisi hazırlık dosyaları özetine taşındı. Her ihalenin özetinde belge adı ile atanmış kişi/ekip veya `Atanmadı` durumu birlikte görülüyor; eksik belge sayısı ve sıradaki kontrol akışı korunuyor. Atanmamış kayıtlar otomatik tamamlanmış sayılmıyor.
Doğrulama: `npm test` 306/306, `npm run build` ve `npm run qa:browser` başarılı. Gitea `main`e gönderim sonrası Coolify API kimlik doğrulaması yeniden denenmeli.
+1
View File
@@ -37,6 +37,7 @@ function Overview({tenders,now}) {
<td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">İhale</span><button type="button" onClick={()=>open(tender,'source')} className="text-blue-800 underline text-left">{tender.ikn} · {tender.baslik}</button><p className="text-xs mt-1">{summary.issues.length ? `${summary.issues.length} kontrol eksik` : 'Kayıtlı kontroller tamam; son kontrol gerekli'}</p></td> <td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">İhale</span><button type="button" onClick={()=>open(tender,'source')} className="text-blue-800 underline text-left">{tender.ikn} · {tender.baslik}</button><p className="text-xs mt-1">{summary.issues.length ? `${summary.issues.length} kontrol eksik` : 'Kayıtlı kontroller tamam; son kontrol gerekli'}</p></td>
<td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">Sorumlu</span>{summary.owner}</td> <td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">Sorumlu</span>{summary.owner}</td>
<td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">Belge eksikleri</span><details><summary className="cursor-pointer">{summary.documentIssues.length ? `${summary.documentIssues.length} belge kontrolü` : 'Kayıtlı belgelerde eksik bulunmadı'}</summary> <td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">Belge eksikleri</span><details><summary className="cursor-pointer">{summary.documentIssues.length ? `${summary.documentIssues.length} belge kontrolü` : 'Kayıtlı belgelerde eksik bulunmadı'}</summary>
{summary.documentOwners.length > 0 && <p className="text-xs text-slate-600 mt-2">Belge sorumluları: {summary.documentOwners.map(item=>`${item.document}: ${item.owner}`).join(' · ')}</p>}
<ul className="space-y-2 mt-2">{summary.documentIssues.map((text,i)=><li key={i}><button type="button" onClick={()=>open(tender,'checks')} className="text-blue-800 underline text-left">{text}</button></li>)}</ul> <ul className="space-y-2 mt-2">{summary.documentIssues.map((text,i)=><li key={i}><button type="button" onClick={()=>open(tender,'checks')} className="text-blue-800 underline text-left">{text}</button></li>)}</ul>
</details></td> </details></td>
<td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">Sıradaki kontrol</span><button type="button" onClick={()=>open(tender,summary.next.target)} className="text-blue-800 underline text-left">{summary.next.text}</button> <td role="cell" className="block md:table-cell p-2"><span aria-hidden="true" className="block md:hidden text-xs font-bold mb-1">Sıradaki kontrol</span><button type="button" onClick={()=>open(tender,summary.next.target)} className="text-blue-800 underline text-left">{summary.next.text}</button>
+2 -1
View File
@@ -37,5 +37,6 @@ export function preparationSummary(tender,vault,now=new Date()) {
const checkedVault={...vault,documents:Array.isArray(vault?.documents) ? vault.documents : [],deadlineDate:tenderDateKey(tender),tenderIkn:tender.ikn}; const checkedVault={...vault,documents:Array.isArray(vault?.documents) ? vault.documents : [],deadlineDate:tenderDateKey(tender),tenderIkn:tender.ikn};
const documentIssues=preparationBlockers([],documents,draft.checks,checkedVault); const documentIssues=preparationBlockers([],documents,draft.checks,checkedVault);
preparationBlockers(selection.rows,documents,draft.checks,checkedVault).forEach(text=>add('checks',text)); 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.'}}; const documentOwners=documents.map(document=>({document:document.document,owner:draft.checks?.documents?.[documentKey(document)]?.owner?.trim() || 'Atanmadı'}));
return {issues,documentIssues,documentOwners,owner:tender.atananPersonel || 'Atanmadı',next:issues[0] || {target:'source',text:'Dosyayı açıp son kontrolleri yapın.'}};
} }
+7
View File
@@ -24,6 +24,13 @@ test('document revision and vault failures invalidate a previously verified save
assert.ok(preparationSummary({...tender,bidDraft:draft},{...vault,documents:[{...file,boyut:21}]},now).documentIssues.some(text=>text.includes('değişti'))); assert.ok(preparationSummary({...tender,bidDraft:draft},{...vault,documents:[{...file,boyut:21}]},now).documentIssues.some(text=>text.includes('değişti')));
assert.ok(preparationSummary({...tender,bidDraft:draft},{...vault,error:'offline'},now).documentIssues.some(text=>text.includes('doğrulanamadı'))); assert.ok(preparationSummary({...tender,bidDraft:draft},{...vault,error:'offline'},now).documentIssues.some(text=>text.includes('doğrulanamadı')));
}); });
test('preparation summary exposes document owners without hiding unassigned work',()=>{
const doc={document:'Yetki belgesi'};
const assigned={manualDocuments:[doc],checks:{documents:{[documentKey(doc)]:{owner:' Uyum ekibi '}}}};
const missing={manualDocuments:[doc],checks:{documents:{}}};
assert.deepEqual(preparationSummary({...tender,bidDraft:assigned},vault,now).documentOwners,[{document:'Yetki belgesi',owner:'Uyum ekibi'}]);
assert.deepEqual(preparationSummary({...tender,bidDraft:missing},vault,now).documentOwners,[{document:'Yetki belgesi',owner:'Atanmadı'}]);
});
test('expired source takes priority over completion or manually moved Kanban status',()=>{ test('expired source takes priority over completion or manually moved Kanban status',()=>{
const summary=preparationSummary({...tender,tarih:'01.09.2026',kanbanStatus:'teklif_verildi'},vault,now); const summary=preparationSummary({...tender,tarih:'01.09.2026',kanbanStatus:'teklif_verildi'},vault,now);
assert.match(summary.next.text,/zamanı geçti/); assert.match(summary.next.text,/zamanı geçti/);