{% extends "base.html" %} {% block title %}Panel Audyt KRS - Norda Biznes Partner{% endblock %} {% block extra_css %} {% endblock %} {% block content %}

Panel Audyt KRS

Ekstrakcja danych z odpisow KRS (Krajowy Rejestr Sadowy)

{% if krs_audit_available %} {% else %} Usluga audytu niedostepna {% endif %}
{{ stats.total_with_krs }} Firm z KRS
{{ stats.audited_count }} Przeaudytowane
{{ stats.not_audited_count }} Oczekujace
{{ stats.no_krs_count }} Bez KRS (JDG)
{{ stats.with_capital }} Z kapitalem
{{ stats.with_people }} Z zarzadem
{{ stats.with_pkd }} Z PKD
Audyt w toku...
0%
Przygotowywanie...
{% if companies %}
{% for company in companies %} {% endfor %}
Firma KRS Kapital Zarzad PKD Status Ostatni audyt Akcje
{{ company.name }} {{ company.krs }} {% if company.capital_amount %} {{ "{:,.0f}".format(company.capital_amount|float).replace(",", " ") }} PLN {% else %} - {% endif %} {% if company.people_count > 0 %} {{ company.people_count }} {% else %} - {% endif %} {% if company.pkd_codes %}
{% for pkd in company.pkd_codes %} {% if pkd.is_primary %} ★ {{ pkd.code }} {% elif loop.index <= 2 %} {{ pkd.code }} {% endif %} {% endfor %} {% if company.pkd_count > 2 %} +{{ company.pkd_count - 2 }} więcej {% endif %}
{% else %} - {% endif %}
{% if company.krs_last_audit_at %} OK {% else %} Oczekuje {% endif %} {% if company.krs_last_audit_at %} {{ company.krs_last_audit_at|local_time('%d.%m.%Y') }} {% else %} Nigdy {% endif %}
{% if company.krs_pdf_path %} {% endif %} {% if krs_audit_available %} {% endif %}
{% else %}

Brak firm z KRS

Nie znaleziono firm z numerem KRS do audytu.

{% endif %} {% endblock %} {% block extra_js %} const csrfToken = '{{ csrf_token() }}'; let pendingModalAction = null; let auditInProgress = false; let currentSort = { column: null, direction: 'asc' }; let activePkdTooltip = null; // === TABLE SORTING === function initTableSorting() { const headers = document.querySelectorAll('.krs-table th.sortable'); headers.forEach(header => { header.addEventListener('click', () => { const sortKey = header.dataset.sort; sortTable(sortKey, header); }); }); } function sortTable(sortKey, headerElement) { const tbody = document.getElementById('krsTableBody'); const rows = Array.from(tbody.querySelectorAll('tr')); // Determine sort direction let direction = 'asc'; if (currentSort.column === sortKey && currentSort.direction === 'asc') { direction = 'desc'; } // Update header classes document.querySelectorAll('.krs-table th.sortable').forEach(th => { th.classList.remove('sort-asc', 'sort-desc'); }); headerElement.classList.add(direction === 'asc' ? 'sort-asc' : 'sort-desc'); // Sort rows rows.sort((a, b) => { let valA, valB; switch (sortKey) { case 'name': valA = a.dataset.name || ''; valB = b.dataset.name || ''; break; case 'krs': valA = a.dataset.krs || ''; valB = b.dataset.krs || ''; break; case 'capital': valA = parseFloat(a.dataset.capital) || 0; valB = parseFloat(b.dataset.capital) || 0; break; case 'zarzad': valA = parseInt(a.dataset.zarzad) || 0; valB = parseInt(b.dataset.zarzad) || 0; break; case 'pkd': valA = parseInt(a.dataset.pkd) || 0; valB = parseInt(b.dataset.pkd) || 0; break; case 'status': valA = a.dataset.status === 'audited' ? 1 : 0; valB = b.dataset.status === 'audited' ? 1 : 0; break; case 'date': valA = a.dataset.date || '00000000'; valB = b.dataset.date || '00000000'; break; default: valA = ''; valB = ''; } // Compare if (typeof valA === 'number' && typeof valB === 'number') { return direction === 'asc' ? valA - valB : valB - valA; } else { const comparison = String(valA).localeCompare(String(valB)); return direction === 'asc' ? comparison : -comparison; } }); // Re-append sorted rows rows.forEach(row => tbody.appendChild(row)); // Update current sort state currentSort = { column: sortKey, direction }; } // === PKD TOOLTIP === function showPkdTooltip(element, pkdCodes) { // Close existing tooltip if (activePkdTooltip) { activePkdTooltip.remove(); activePkdTooltip = null; } // Create tooltip const tooltip = document.createElement('div'); tooltip.className = 'pkd-tooltip active'; let html = '
'; pkdCodes.forEach(pkd => { html += `
${pkd.is_primary ? '★ ' : ''}${pkd.code}
${pkd.description || '-'}
`; }); html += '
'; tooltip.innerHTML = html; // Position tooltip const rect = element.getBoundingClientRect(); tooltip.style.position = 'fixed'; tooltip.style.top = (rect.bottom + 5) + 'px'; tooltip.style.left = rect.left + 'px'; // Add to document document.body.appendChild(tooltip); activePkdTooltip = tooltip; // Close on click outside setTimeout(() => { document.addEventListener('click', closePkdTooltipOnClickOutside); }, 10); } function closePkdTooltipOnClickOutside(e) { if (activePkdTooltip && !activePkdTooltip.contains(e.target) && !e.target.classList.contains('pkd-more')) { activePkdTooltip.remove(); activePkdTooltip = null; document.removeEventListener('click', closePkdTooltipOnClickOutside); } } // Initialize sorting on page load document.addEventListener('DOMContentLoaded', initTableSorting); // Modal functions function showModal(title, body, onConfirm) { document.getElementById('modalTitle').textContent = title; document.getElementById('modalBody').textContent = body; pendingModalAction = onConfirm; document.getElementById('confirmModal').classList.add('active'); } function closeModal() { document.getElementById('confirmModal').classList.remove('active'); pendingModalAction = null; } function confirmModalAction() { if (pendingModalAction) { pendingModalAction(); } closeModal(); } function showResultModal(title, body, success = true) { document.getElementById('resultTitle').textContent = title; document.getElementById('resultBody').textContent = body; const icon = document.getElementById('resultIcon'); icon.className = 'modal-icon ' + (success ? 'success' : 'warning'); document.getElementById('resultModal').classList.add('active'); } function closeResultModal() { document.getElementById('resultModal').classList.remove('active'); location.reload(); } // Close modal on backdrop click document.getElementById('confirmModal')?.addEventListener('click', (e) => { if (e.target.id === 'confirmModal') closeModal(); }); document.getElementById('resultModal')?.addEventListener('click', (e) => { if (e.target.id === 'resultModal') closeResultModal(); }); // Filter functions function applyFilters() { const status = document.getElementById('filterStatus').value; const search = document.getElementById('filterSearch').value.toLowerCase(); const rows = document.querySelectorAll('#krsTableBody tr'); rows.forEach(row => { let show = true; // Status filter if (status && row.dataset.status !== status) { show = false; } // Search filter if (search && show) { const name = row.dataset.name || ''; const krs = row.dataset.krs || ''; if (!name.includes(search) && !krs.includes(search)) { show = false; } } row.style.display = show ? '' : 'none'; }); } function resetFilters() { document.getElementById('filterStatus').value = ''; document.getElementById('filterSearch').value = ''; applyFilters(); } // Audit functions async function runSingleAudit(companyId, companyName) { showModal( 'Uruchom audyt KRS', `Czy chcesz uruchomic audyt KRS dla firmy "${companyName}"?`, async () => { // Show progress section const progressSection = document.getElementById('progressSection'); const progressBar = document.getElementById('progressBar'); const progressMessage = document.getElementById('progressMessage'); const progressLog = document.getElementById('progressLog'); progressSection.classList.add('active'); progressLog.innerHTML = ''; // Stage 1: Searching for PDF progressBar.style.width = '10%'; progressBar.textContent = '10%'; progressMessage.textContent = `Wyszukiwanie pliku PDF dla ${companyName}...`; addLogEntry(progressLog, `Rozpoczynam audyt KRS dla: ${companyName}`, 'info'); await sleep(300); // Stage 2: Loading PDF progressBar.style.width = '25%'; progressBar.textContent = '25%'; progressMessage.textContent = 'Pobieranie danych z pliku PDF...'; addLogEntry(progressLog, 'Znaleziono plik PDF, wczytuję...', 'info'); await sleep(200); // Stage 3: Parsing progressBar.style.width = '40%'; progressBar.textContent = '40%'; progressMessage.textContent = 'Parsowanie odpisu KRS...'; addLogEntry(progressLog, 'Ekstrakcja danych z PDF...', 'info'); try { const response = await fetch('/admin/krs-api/audit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ company_id: companyId }) }); // Stage 4: Processing response progressBar.style.width = '80%'; progressBar.textContent = '80%'; progressMessage.textContent = 'Zapisywanie danych do bazy...'; const data = await response.json(); if (response.ok && data.success) { // Stage 5: Complete progressBar.style.width = '100%'; progressBar.textContent = '100%'; progressMessage.textContent = 'Audyt zakończony pomyślnie!'; addLogEntry(progressLog, `KRS: ${data.data?.krs || '-'}`, 'success'); addLogEntry(progressLog, `Kapitał: ${data.data?.kapital?.toLocaleString() || '-'} PLN`, 'success'); addLogEntry(progressLog, `Zarząd: ${data.data?.zarzad_count || 0} osób`, 'success'); addLogEntry(progressLog, `Wspólnicy: ${data.data?.wspolnicy_count || 0} osób`, 'success'); addLogEntry(progressLog, `PKD: ${data.data?.pkd_count || 0} kodów`, 'success'); await sleep(1500); progressSection.classList.remove('active'); showResultModal( 'Audyt zakończony', `Pomyślnie wyciągnięto dane dla ${companyName}.\n\n` + `Kapitał: ${data.data?.kapital?.toLocaleString() || '-'} PLN\n` + `Zarząd: ${data.data?.zarzad_count || 0} osób\n` + `Wspólnicy: ${data.data?.wspolnicy_count || 0} osób\n` + `PKD: ${data.data?.pkd_count || 0} kodów`, true ); // Refresh page to show updated data setTimeout(() => location.reload(), 2000); } else { progressBar.style.width = '100%'; progressBar.style.background = '#ef4444'; progressMessage.textContent = 'Błąd audytu'; addLogEntry(progressLog, `Błąd: ${data.error || 'Nieznany błąd'}`, 'error'); await sleep(1500); progressSection.classList.remove('active'); progressBar.style.background = ''; showResultModal('Błąd', data.error || 'Wystąpił nieznany błąd', false); } } catch (error) { progressBar.style.width = '100%'; progressBar.style.background = '#ef4444'; progressMessage.textContent = 'Błąd połączenia'; addLogEntry(progressLog, `Błąd: ${error.message}`, 'error'); await sleep(1500); progressSection.classList.remove('active'); progressBar.style.background = ''; showResultModal('Błąd połączenia', 'Nie udało się połączyć z serwerem: ' + error.message, false); } } ); } function addLogEntry(logElement, message, type) { const entry = document.createElement('div'); entry.className = 'progress-log-entry ' + type; entry.textContent = message; logElement.appendChild(entry); logElement.scrollTop = logElement.scrollHeight; } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function runBatchAudit() { showModal( 'Uruchom audyt wszystkich firm', 'Czy chcesz uruchomić audyt KRS dla wszystkich firm z numerem KRS? Każda firma będzie przetwarzana osobno.', async () => { auditInProgress = true; const btn = document.getElementById('batchAuditBtn'); btn.disabled = true; btn.innerHTML = 'Audyt w toku...'; const progressSection = document.getElementById('progressSection'); progressSection.classList.add('active'); const progressBar = document.getElementById('progressBar'); const progressMessage = document.getElementById('progressMessage'); const progressLog = document.getElementById('progressLog'); progressBar.style.width = '0%'; progressBar.textContent = '0%'; progressMessage.textContent = 'Pobieranie listy firm...'; progressLog.innerHTML = ''; // Get all companies with KRS from the table const rows = document.querySelectorAll('table tbody tr[data-company-id]'); const companies = []; rows.forEach(row => { const krs = row.dataset.krs; if (krs && krs.trim() !== '') { companies.push({ id: parseInt(row.dataset.companyId), name: row.querySelector('td:first-child a')?.textContent?.trim() || row.dataset.name || 'Nieznana', krs: krs }); } }); if (companies.length === 0) { progressSection.classList.remove('active'); showResultModal('Brak firm', 'Nie znaleziono firm z numerem KRS do audytu.', false); auditInProgress = false; btn.disabled = false; btn.innerHTML = ` Uruchom audyt wszystkich `; return; } addLogEntry(progressLog, `Znaleziono ${companies.length} firm z KRS do audytu`, 'info'); let success = 0; let failed = 0; let skipped = 0; for (let i = 0; i < companies.length; i++) { const company = companies[i]; const percent = Math.round(((i + 1) / companies.length) * 100); progressBar.style.width = `${percent}%`; progressBar.textContent = `${percent}%`; progressMessage.textContent = `[${i + 1}/${companies.length}] Przetwarzanie: ${company.name}...`; try { const response = await fetch('/admin/krs-api/audit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ company_id: company.id }) }); const data = await response.json(); if (response.ok && data.success) { success++; const d = data.data || {}; addLogEntry(progressLog, `✓ ${company.name} (KRS: ${company.krs})`, 'success'); addLogEntry(progressLog, ` 📄 NIP: ${d.nip || '-'}, REGON: ${d.regon || '-'}`, 'detail'); addLogEntry(progressLog, ` 💰 Kapitał: ${d.kapital?.toLocaleString() || '-'} PLN, Udziały: ${d.liczba_udzialow || '-'}`, 'detail'); addLogEntry(progressLog, ` 👥 Zarząd: ${d.zarzad_count || 0}, Wspólnicy: ${d.wspolnicy_count || 0}, Prokurenci: ${d.prokurenci_count || 0}`, 'detail'); addLogEntry(progressLog, ` 🏷️ PKD: ${d.pkd_count || 0} kodów`, 'detail'); } else { if (data.error && data.error.includes('nie ma numeru KRS')) { skipped++; addLogEntry(progressLog, `⊘ ${company.name} - Brak KRS`, 'skipped'); } else if (data.error && data.error.includes('Nie znaleziono pliku PDF')) { skipped++; addLogEntry(progressLog, `⊘ ${company.name} (${company.krs}) - Brak pliku PDF`, 'skipped'); } else { failed++; addLogEntry(progressLog, `✗ ${company.name} (${company.krs}) - ${data.error || 'Błąd'}`, 'error'); } } } catch (error) { failed++; addLogEntry(progressLog, `✗ ${company.name} (${company.krs}) - Błąd połączenia`, 'error'); } // Small delay between requests to not overwhelm the server await sleep(100); } progressBar.style.width = '100%'; progressBar.textContent = '100%'; progressMessage.textContent = `Audyt zakończony! Sukces: ${success}, Błędy: ${failed}, Pominięte: ${skipped}`; addLogEntry(progressLog, `─────────────────────────────`, 'info'); addLogEntry(progressLog, `PODSUMOWANIE: Sukces: ${success}, Błędy: ${failed}, Pominięte: ${skipped}`, 'info'); showResultModal( 'Audyt zakończony', `Przetworzono ${companies.length} firm.\n\nSukces: ${success}\nBłędy: ${failed}\nPominięte: ${skipped}`, success > 0 ); auditInProgress = false; btn.disabled = false; btn.innerHTML = ` Uruchom audyt wszystkich `; // Refresh page after a delay to show updated data setTimeout(() => location.reload(), 3000); } ); } function cancelAudit() { // Note: Currently can't cancel - just hide progress section document.getElementById('progressSection').classList.remove('active'); } {% endblock %}