feat(inventory): list operations in Entradas/Salidas/Traspasos/Ajustes tabs

- Add GET /operations endpoint with filtering by type, pagination, date range
- Join with inventory, employees, branches for rich display
- Add tbody IDs and footer/pagination IDs to operation tables in HTML
- Add loadOperations() JS function with renderOperationRow() per type
- Integrate loadOperations into switchTab for auto-load on tab change
- Update recordPurchase/Adjustment/Transfer to refresh respective lists
- Expose loadOperations globally for HTML inline script access
This commit is contained in:
2026-05-18 06:00:58 +00:00
parent bfa7bc2997
commit 60dd8162f7
3 changed files with 244 additions and 18 deletions

View File

@@ -298,9 +298,19 @@
return;
}
apiFetch(API + '/adjustment', { method: 'POST', body: JSON.stringify(data) }).then(function (result) {
document.getElementById('adjustResult').innerHTML = result && result.operation_id
? '<span style="color:var(--color-success);">Ajuste registrado (op #' + result.operation_id + ')</span>'
: '<span style="color:var(--color-error);">' + (result ? result.error || 'Error' : 'Error de red') + '</span>';
if (result && result.operation_id) {
document.getElementById('adjustResult').innerHTML = '<span style="color:var(--color-success);">Ajuste registrado (op #' + result.operation_id + ')</span>';
closeAdjustmentModal();
['adjustItemId','adjustQty','adjustReason'].forEach(function(id) {
var el = document.getElementById(id);
if (el) el.value = '';
});
if (window.loadInventoryStats) window.loadInventoryStats();
if (window.loadOperations) window.loadOperations('ajustes', 1);
loadItems(currentPage);
} else {
document.getElementById('adjustResult').innerHTML = '<span style="color:var(--color-error);">' + (result ? result.error || 'Error' : 'Error de red') + '</span>';
}
});
}
@@ -329,12 +339,119 @@
return;
}
apiFetch(API + '/transfer', { method: 'POST', body: JSON.stringify(data) }).then(function (result) {
document.getElementById('transferResult').innerHTML = result && result.out_operation_id
? '<span style="color:var(--color-success);">Transferencia registrada</span>'
: '<span style="color:var(--color-error);">' + (result ? result.error || 'Error' : 'Error de red') + '</span>';
if (result && result.out_operation_id) {
document.getElementById('transferResult').innerHTML = '<span style="color:var(--color-success);">Transferencia registrada</span>';
closeTransferModal();
['transferItemId','transferFrom','transferTo','transferQty','transferNotes'].forEach(function(id) {
var el = document.getElementById(id);
if (el) el.value = '';
});
if (window.loadInventoryStats) window.loadInventoryStats();
if (window.loadOperations) window.loadOperations('traspasos', 1);
loadItems(currentPage);
} else {
document.getElementById('transferResult').innerHTML = '<span style="color:var(--color-error);">' + (result ? result.error || 'Error' : 'Error de red') + '</span>';
}
});
}
// =====================================================================
// OPERATIONS LIST (Entradas, Salidas, Traspasos, Ajustes)
// =====================================================================
var opTypeMap = {
'entradas': 'PURCHASE',
'salidas': 'SALE',
'traspasos': 'TRANSFER',
'ajustes': 'ADJUST'
};
function loadOperations(type, page) {
var opType = opTypeMap[type];
if (!opType) return;
page = page || 1;
var params = new URLSearchParams({ type: opType, page: page, per_page: 50 });
apiFetch(API + '/operations?' + params.toString()).then(function (data) {
if (!data) return;
var tbodyId = type + 'TableBody';
var footerId = type + 'Footer';
var pagId = type + 'Pagination';
var tbody = document.getElementById(tbodyId);
var ops = data.data || [];
if (!ops.length) {
tbody.innerHTML = '<tr><td colspan="8" style="text-align:center;padding:30px;color:var(--color-text-muted);">Sin registros</td></tr>';
document.getElementById(pagId).innerHTML = '';
document.getElementById(footerId).textContent = '';
return;
}
tbody.innerHTML = ops.map(function (op) { return renderOperationRow(op, type); }).join('');
var pg = data.pagination || {};
if (pg.total_pages > 1) {
document.getElementById(pagId).innerHTML =
'<div class="pagination">' +
'<button class="page-btn" ' + (pg.page <= 1 ? 'disabled' : 'onclick="window._loadOperations(\'' + type + '\',' + (pg.page - 1) + ')"') + '>&lsaquo;</button>' +
'<span style="padding:0 var(--space-2);font-size:var(--text-body-sm);color:var(--color-text-muted);">' + pg.page + ' / ' + pg.total_pages + ' (' + pg.total + ' registros)</span>' +
'<button class="page-btn" ' + (pg.page >= pg.total_pages ? 'disabled' : 'onclick="window._loadOperations(\'' + type + '\',' + (pg.page + 1) + ')"') + '>&rsaquo;</button>' +
'</div>';
} else {
document.getElementById(pagId).innerHTML = '';
}
document.getElementById(footerId).textContent = (pg.total || 0) + ' registros';
});
}
window.loadOperations = loadOperations;
window._loadOperations = loadOperations;
function renderOperationRow(op, type) {
var dateStr = op.created_at ? new Date(op.created_at).toLocaleString('es-MX', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : '-';
var productInfo = esc(op.part_number || op.barcode || '') + ' — ' + esc(op.product_name || '');
if (type === 'entradas') {
return '<tr>' +
'<td class="td--mono">#' + op.id + '</td>' +
'<td>' + dateStr + '</td>' +
'<td>' + productInfo + '</td>' +
'<td style="text-align:right">' + (op.quantity || 0) + '</td>' +
'<td style="text-align:right" class="td--amount">$' + fmt(op.cost_at_time || 0) + '</td>' +
'<td style="text-align:right" class="td--amount">$' + fmt(op.total || 0) + '</td>' +
'<td>' + esc(op.notes || '-') + '</td>' +
'<td>' + esc(op.employee_name || '-') + '</td>' +
'</tr>';
}
if (type === 'salidas') {
return '<tr>' +
'<td class="td--mono">#' + op.id + '</td>' +
'<td>' + dateStr + '</td>' +
'<td>' + productInfo + '</td>' +
'<td style="text-align:right">' + (op.quantity || 0) + '</td>' +
'<td style="text-align:right" class="td--amount">$' + fmt(op.total || 0) + '</td>' +
'<td>' + esc(op.notes || '-') + '</td>' +
'<td>' + esc(op.employee_name || '-') + '</td>' +
'</tr>';
}
if (type === 'traspasos') {
return '<tr>' +
'<td class="td--mono">#' + op.id + '</td>' +
'<td>' + dateStr + '</td>' +
'<td>' + productInfo + '</td>' +
'<td style="text-align:right">' + (op.quantity || 0) + '</td>' +
'<td>' + esc(op.branch_name || '-') + '</td>' +
'<td>' + esc(op.notes || '-') + '</td>' +
'<td>' + esc(op.employee_name || '-') + '</td>' +
'</tr>';
}
if (type === 'ajustes') {
return '<tr>' +
'<td class="td--mono">#' + op.id + '</td>' +
'<td>' + dateStr + '</td>' +
'<td>' + productInfo + '</td>' +
'<td style="text-align:right">' + (op.quantity || 0) + '</td>' +
'<td>' + esc(op.notes || '-') + '</td>' +
'<td>' + esc(op.employee_name || '-') + '</td>' +
'</tr>';
}
return '<tr><td colspan="8">-</td></tr>';
}
// =====================================================================
// PHYSICAL COUNT / CONTEO (countModal)
// =====================================================================