This commit is contained in:
2026-02-17 14:41:58 -06:00
parent 52dcdce8bb
commit 03e0516c08
4 changed files with 267 additions and 3 deletions

View File

@@ -1201,4 +1201,65 @@ async function checkQboOverdue() {
btn.innerHTML = originalText;
btn.disabled = false;
}
}
}
async function importFromQBO() {
if (!confirm(
'Alle unbezahlten Rechnungen aus QBO importieren?\n\n' +
'• Bereits importierte werden übersprungen\n' +
'• Nur Kunden die lokal verknüpft sind\n\n' +
'Fortfahren?'
)) return;
const btn = document.querySelector('button[onclick="importFromQBO()"]');
const resultDiv = document.getElementById('qbo-import-result');
const originalText = btn.innerHTML;
btn.innerHTML = '⏳ Importiere aus QBO...';
btn.disabled = true;
resultDiv.classList.add('hidden');
try {
const response = await fetch('/api/qbo/import-unpaid', { method: 'POST' });
const result = await response.json();
resultDiv.classList.remove('hidden');
if (response.ok) {
let html = `<div class="p-4 rounded-lg ${result.imported > 0 ? 'bg-green-50 border border-green-200' : 'bg-blue-50 border border-blue-200'}">`;
html += `<p class="font-semibold text-gray-800 mb-2">Import abgeschlossen</p>`;
html += `<ul class="text-sm text-gray-700 space-y-1">`;
html += `<li>✅ <strong>${result.imported}</strong> Rechnungen importiert</li>`;
if (result.skipped > 0) {
html += `<li>⏭️ <strong>${result.skipped}</strong> bereits vorhanden (übersprungen)</li>`;
}
if (result.skippedNoCustomer > 0) {
html += `<li>⚠️ <strong>${result.skippedNoCustomer}</strong> übersprungen — Kunde nicht verknüpft:</li>`;
html += `<li class="ml-4 text-xs text-gray-500">${result.skippedCustomerNames.join(', ')}</li>`;
}
html += `</ul></div>`;
resultDiv.innerHTML = html;
// Invoice-Liste aktualisieren
if (result.imported > 0) {
loadInvoices();
}
} else {
resultDiv.innerHTML = `<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="font-semibold text-red-800">Import fehlgeschlagen</p>
<p class="text-sm text-red-600 mt-1">${result.error}</p>
</div>`;
}
} catch (error) {
console.error('Import Error:', error);
resultDiv.classList.remove('hidden');
resultDiv.innerHTML = `<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="text-red-600">Netzwerkfehler beim Import.</p>
</div>`;
} finally {
btn.innerHTML = originalText;
btn.disabled = false;
}
}

View File

@@ -147,8 +147,23 @@
<div id="upload-status" class="mt-4"></div>
<hr class="my-8 border-gray-200">
<hr class="my-8 border-gray-200">
<h3 class="text-xl font-semibold mb-4 text-gray-800">QBO Rechnungs-Import</h3>
<p class="text-gray-600 mb-2">
Importiert alle <strong>unbezahlten</strong> Rechnungen aus QuickBooks Online in dein lokales System.
</p>
<ul class="text-sm text-gray-500 mb-4 list-disc list-inside">
<li>Bereits importierte Rechnungen werden übersprungen</li>
<li>Nur Kunden die lokal mit QBO verknüpft sind</li>
<li>Line Items (Labor/Parts) werden mit importiert</li>
</ul>
<button onclick="importFromQBO()" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-semibold shadow-md flex items-center">
<span class="mr-2">📥</span> Unbezahlte Rechnungen importieren
</button>
<div id="qbo-import-result" class="mt-4 hidden"></div>
<h3 class="text-xl font-semibold mb-4 text-gray-800">QuickBooks Online Authorization</h3>
<p class="text-gray-600 mb-4">
Wenn der Token abgelaufen ist oder die Verbindung fehlschlägt,