diff --git a/core/templates/base.html b/core/templates/base.html
index 98ec260..23b9e9c 100644
--- a/core/templates/base.html
+++ b/core/templates/base.html
@@ -549,7 +549,10 @@ document.addEventListener('DOMContentLoaded', function() {
var adjBody = document.createElement('tbody');
data.adjustments.forEach(function(adj) {
var tr = document.createElement('tr');
- tr.appendChild(el('td', null, adj.type));
+ // Prefer the short display label from the server; fall back to the
+ // raw DB value if a stale JSON response doesn't include type_label
+ // (graceful degradation — never render a blank cell).
+ tr.appendChild(el('td', null, adj.type_label || adj.type));
var wTd = document.createElement('td');
wTd.appendChild(link('/workers/' + adj.worker_id + '/', adj.worker_name));
tr.appendChild(wTd);
diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html
index 523e1d8..011fbe8 100644
--- a/core/templates/core/payroll_dashboard.html
+++ b/core/templates/core/payroll_dashboard.html
@@ -2395,7 +2395,10 @@ document.addEventListener('DOMContentLoaded', function() {
labelWrap.className = 'd-flex justify-content-between flex-grow-1';
var label = document.createElement('span');
- label.textContent = adj.type + (adj.project ? ' (' + adj.project + ')' : '');
+ // Prefer the short display label ('Loan' / 'Advance' / 'Advance Repaid')
+ // from the server; fall back to the raw DB value if a stale JSON
+ // response doesn't include type_label (graceful degradation).
+ label.textContent = (adj.type_label || adj.type) + (adj.project ? ' (' + adj.project + ')' : '');
if (adj.description) {
var descSmall = document.createElement('small');
descSmall.className = 'text-muted ms-1';
diff --git a/core/views.py b/core/views.py
index 2f1b9d7..86bacbb 100644
--- a/core/views.py
+++ b/core/views.py
@@ -1020,8 +1020,11 @@ def work_log_payroll_ajax(request, log_id):
} for row in ctx['worker_rows']]
# Adjustments linked directly to this work_log (Overtime, etc.).
+ # We emit BOTH 'type' (raw DB value — stable identifier for any JS logic)
+ # AND 'type_label' (short display label from get_type_display()) — visible UI.
adjustments = [{
'type': adj.type,
+ 'type_label': adj.get_type_display(),
'amount': float(adj.amount),
'worker_id': adj.worker.id,
'worker_name': adj.worker.name,
@@ -4236,7 +4239,11 @@ def preview_payslip(request, worker_id):
adj_total += float(adj.amount) if adj.type in ADDITIVE_TYPES else -float(adj.amount)
adjustments_list.append({
'id': adj.id,
+ # 'type' keeps the raw DB value so any JS that uses it as an
+ # identifier keeps working; 'type_label' is the short display
+ # label ('Loan' / 'Advance' / 'Advance Repaid' etc.) for visible UI.
'type': adj.type,
+ 'type_label': adj.get_type_display(),
'amount': float(adj.amount),
'sign': sign,
'description': adj.description,