fix: vendor info + rerun button on supplier invoices v2.2.21
- Fix get_files_by_status query: LATERAL join to get latest extraction
per file, returning vendor_name, vendor_match_confidence, total_amount
- Fix renderUnhandledFiles: use best_vendor_name, convert confidence
0-1 to %, use total_amount field
- Add rerun button for all files (not just failed) via rerunSingleFile()
- rerunSingleFile() calls /reprocess/{file_id} and reloads unhandled tab
- Fix retry_extraction endpoint to actually run extraction immediately
This commit is contained in:
parent
ea4905ef8a
commit
744b405142
@ -339,10 +339,22 @@ async def get_files_by_status(status: Optional[str] = None, limit: int = 100):
|
|||||||
SELECT f.file_id, f.filename, f.file_path, f.file_size, f.mime_type,
|
SELECT f.file_id, f.filename, f.file_path, f.file_size, f.mime_type,
|
||||||
f.status, f.uploaded_at, f.processed_at, f.detected_cvr,
|
f.status, f.uploaded_at, f.processed_at, f.detected_cvr,
|
||||||
f.detected_vendor_id, v.name as detected_vendor_name,
|
f.detected_vendor_id, v.name as detected_vendor_name,
|
||||||
e.total_amount as detected_amount
|
ext.vendor_name,
|
||||||
|
ext.vendor_cvr,
|
||||||
|
ext.vendor_matched_id,
|
||||||
|
COALESCE(v_ext.name, ext.vendor_name, v.name) as best_vendor_name,
|
||||||
|
ext.total_amount,
|
||||||
|
ext.confidence as vendor_match_confidence
|
||||||
FROM incoming_files f
|
FROM incoming_files f
|
||||||
LEFT JOIN vendors v ON f.detected_vendor_id = v.id
|
LEFT JOIN vendors v ON f.detected_vendor_id = v.id
|
||||||
LEFT JOIN extractions e ON f.file_id = e.file_id
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT vendor_name, vendor_cvr, vendor_matched_id, total_amount, confidence
|
||||||
|
FROM extractions
|
||||||
|
WHERE file_id = f.file_id
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
) ext ON true
|
||||||
|
LEFT JOIN vendors v_ext ON v_ext.id = ext.vendor_matched_id
|
||||||
WHERE f.status IN ({placeholders})
|
WHERE f.status IN ({placeholders})
|
||||||
ORDER BY f.uploaded_at DESC
|
ORDER BY f.uploaded_at DESC
|
||||||
LIMIT %s
|
LIMIT %s
|
||||||
@ -353,10 +365,22 @@ async def get_files_by_status(status: Optional[str] = None, limit: int = 100):
|
|||||||
SELECT f.file_id, f.filename, f.file_path, f.file_size, f.mime_type,
|
SELECT f.file_id, f.filename, f.file_path, f.file_size, f.mime_type,
|
||||||
f.status, f.uploaded_at, f.processed_at, f.detected_cvr,
|
f.status, f.uploaded_at, f.processed_at, f.detected_cvr,
|
||||||
f.detected_vendor_id, v.name as detected_vendor_name,
|
f.detected_vendor_id, v.name as detected_vendor_name,
|
||||||
e.total_amount as detected_amount
|
ext.vendor_name,
|
||||||
|
ext.vendor_cvr,
|
||||||
|
ext.vendor_matched_id,
|
||||||
|
COALESCE(v_ext.name, ext.vendor_name, v.name) as best_vendor_name,
|
||||||
|
ext.total_amount,
|
||||||
|
ext.confidence as vendor_match_confidence
|
||||||
FROM incoming_files f
|
FROM incoming_files f
|
||||||
LEFT JOIN vendors v ON f.detected_vendor_id = v.id
|
LEFT JOIN vendors v ON f.detected_vendor_id = v.id
|
||||||
LEFT JOIN extractions e ON f.file_id = e.file_id
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT vendor_name, vendor_cvr, vendor_matched_id, total_amount, confidence
|
||||||
|
FROM extractions
|
||||||
|
WHERE file_id = f.file_id
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
) ext ON true
|
||||||
|
LEFT JOIN vendors v_ext ON v_ext.id = ext.vendor_matched_id
|
||||||
ORDER BY f.uploaded_at DESC
|
ORDER BY f.uploaded_at DESC
|
||||||
LIMIT %s
|
LIMIT %s
|
||||||
"""
|
"""
|
||||||
@ -3255,16 +3279,9 @@ async def retry_extraction(file_id: int):
|
|||||||
|
|
||||||
logger.info(f"🔄 Retrying extraction for file {file_id}: {file_data['filename']}")
|
logger.info(f"🔄 Retrying extraction for file {file_id}: {file_data['filename']}")
|
||||||
|
|
||||||
# Trigger re-analysis by calling the existing upload processing logic
|
# Run full extraction cascade immediately
|
||||||
# For now, just mark as pending - the user can then run batch-analyze
|
result = await reprocess_uploaded_file(file_id)
|
||||||
|
return result
|
||||||
return {
|
|
||||||
"file_id": file_id,
|
|
||||||
"filename": file_data['filename'],
|
|
||||||
"message": "File marked for re-analysis. Run batch-analyze to process.",
|
|
||||||
"previous_status": file_data['status'],
|
|
||||||
"new_status": "pending"
|
|
||||||
}
|
|
||||||
|
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@ -1812,9 +1812,10 @@ function renderUnhandledFiles(files) {
|
|||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const statusBadge = getFileStatusBadge(file.status);
|
const statusBadge = getFileStatusBadge(file.status);
|
||||||
const vendorName = file.detected_vendor_name || '-';
|
const vendorName = file.best_vendor_name || file.vendor_name || file.detected_vendor_name || '-';
|
||||||
const confidence = file.vendor_match_confidence ? `${file.vendor_match_confidence}%` : '-';
|
const confRaw = file.vendor_match_confidence;
|
||||||
const amount = file.detected_amount ? formatCurrency(file.detected_amount) : '-';
|
const confidence = confRaw !== null && confRaw !== undefined ? `${Math.round(confRaw * 100)}%` : '-';
|
||||||
|
const amount = file.total_amount ? formatCurrency(file.total_amount) : '-';
|
||||||
const uploadDate = file.uploaded_at ? new Date(file.uploaded_at).toLocaleDateString('da-DK') : '-';
|
const uploadDate = file.uploaded_at ? new Date(file.uploaded_at).toLocaleDateString('da-DK') : '-';
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
@ -1842,14 +1843,9 @@ function renderUnhandledFiles(files) {
|
|||||||
<td>${statusBadge}</td>
|
<td>${statusBadge}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm">
|
<div class="btn-group btn-group-sm">
|
||||||
${file.status === 'extraction_failed' ?
|
<button class="btn btn-outline-warning" onclick="rerunSingleFile(${file.file_id})" title="Kør analyse igen">
|
||||||
`<button class="btn btn-outline-warning" onclick="retryExtraction(${file.file_id})" title="Prøv igen">
|
<i class="bi bi-arrow-repeat"></i>
|
||||||
<i class="bi bi-arrow-clockwise"></i>
|
</button>
|
||||||
</button>` :
|
|
||||||
`<button class="btn btn-outline-primary" onclick="analyzeFile(${file.file_id})" title="Analyser">
|
|
||||||
<i class="bi bi-search"></i>
|
|
||||||
</button>`
|
|
||||||
}
|
|
||||||
<button class="btn btn-outline-secondary" onclick="viewFilePDF(${file.file_id})" title="Vis PDF">
|
<button class="btn btn-outline-secondary" onclick="viewFilePDF(${file.file_id})" title="Vis PDF">
|
||||||
<i class="bi bi-file-pdf"></i>
|
<i class="bi bi-file-pdf"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -1965,6 +1961,36 @@ async function retryExtraction(fileId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rerun full extraction for a file in the unhandled tab
|
||||||
|
async function rerunSingleFile(fileId) {
|
||||||
|
try {
|
||||||
|
showLoadingOverlay('Kører analyse...');
|
||||||
|
|
||||||
|
const response = await fetch(`/api/v1/supplier-invoices/reprocess/${fileId}`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const err = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(err.detail || 'Analyse fejlede');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
hideLoadingOverlay();
|
||||||
|
|
||||||
|
const confPct = result.confidence ? Math.round(result.confidence * 100) + '%' : '?%';
|
||||||
|
const vendorInfo = result.vendor_id ? `Leverandør matchet (ID ${result.vendor_id})` : 'Ingen leverandør matchet';
|
||||||
|
alert(`✅ Analyse færdig\n${vendorInfo}\nConfidence: ${confPct}`);
|
||||||
|
|
||||||
|
loadUnhandledFiles();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
hideLoadingOverlay();
|
||||||
|
console.error('Rerun error:', error);
|
||||||
|
alert('❌ Fejl ved analyse: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NEW: Analyze single file
|
// NEW: Analyze single file
|
||||||
async function analyzeFile(fileId) {
|
async function analyzeFile(fileId) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user