feat(fees): kolumna zaległości z lat poprzednich z edycją inline
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Dodano pole previous_years_debt w modelu Company. Kolumna widoczna w widoku rocznym składek — kliknięcie kwoty otwiera pole edycji. Legenda zaktualizowana. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e97f586311
commit
3736065dda
@ -1144,6 +1144,31 @@ def admin_fees_send_reminder():
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/fees/update-debt', methods=['POST'])
|
||||
@login_required
|
||||
@role_required(SystemRole.OFFICE_MANAGER)
|
||||
def admin_fees_update_debt():
|
||||
"""Update previous years debt for a company."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
company_id = request.form.get('company_id', type=int)
|
||||
debt = request.form.get('debt', type=float, default=0)
|
||||
|
||||
company = db.query(Company).filter_by(id=company_id).first()
|
||||
if not company:
|
||||
return jsonify({'success': False, 'error': 'Firma nie znaleziona'}), 404
|
||||
|
||||
company.previous_years_debt = debt
|
||||
db.commit()
|
||||
return jsonify({'success': True, 'message': f'Zaległość zapisana: {debt:.0f} zł'})
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error updating debt: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CALENDAR ADMIN ROUTES
|
||||
# ============================================================
|
||||
|
||||
@ -839,6 +839,7 @@ class Company(Base):
|
||||
norda_biznes_url = Column(String(500))
|
||||
norda_biznes_member_id = Column(String(50))
|
||||
member_since = Column(Date) # Data przystąpienia do Izby NORDA
|
||||
previous_years_debt = Column(Numeric(10, 2), default=0) # Zaległości z lat poprzednich (ręcznie wpisane)
|
||||
|
||||
# Metadata
|
||||
last_updated = Column(DateTime, default=datetime.now)
|
||||
|
||||
7
database/migrations/088_previous_years_debt.sql
Normal file
7
database/migrations/088_previous_years_debt.sql
Normal file
@ -0,0 +1,7 @@
|
||||
-- Migration 088: Add previous_years_debt column to companies
|
||||
-- Manual field for tracking arrears from previous years
|
||||
|
||||
ALTER TABLE companies ADD COLUMN IF NOT EXISTS previous_years_debt NUMERIC(10,2) DEFAULT 0;
|
||||
|
||||
-- Grant permissions
|
||||
GRANT ALL ON TABLE companies TO nordabiz_app;
|
||||
@ -294,6 +294,7 @@
|
||||
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell pending" style="width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-size: 11px;">1</span> Oczekujące</span>
|
||||
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell overdue" style="width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-size: 11px;">1</span> Zaległe</span>
|
||||
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell empty" style="width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-size: 11px;">-</span> Brak danych</span>
|
||||
<span style="display: flex; align-items: center; gap: 4px; border-left: 1px solid var(--border); padding-left: var(--spacing-lg);"><span style="color:var(--error);font-weight:600;font-size:12px;">500 zł</span> Zaległości z lat poprzednich (kliknij kwotę aby edytować)</span>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
@ -358,6 +359,7 @@
|
||||
{% else %}
|
||||
<th>Sty</th><th>Lut</th><th>Mar</th><th>Kwi</th><th>Maj</th><th>Cze</th>
|
||||
<th>Lip</th><th>Sie</th><th>Wrz</th><th>Paź</th><th>Lis</th><th>Gru</th>
|
||||
<th title="Zaległości z lat poprzednich — wpisywane ręcznie">Zaległości</th>
|
||||
<th>Przypomnienie</th>
|
||||
<th></th>
|
||||
{% endif %}
|
||||
@ -368,7 +370,7 @@
|
||||
{% for cf in companies_fees %}
|
||||
{% if not month and not cf.has_data and not ns.separator_shown %}
|
||||
{% set ns.separator_shown = true %}
|
||||
<tr><td colspan="15" style="background: var(--border); padding: var(--spacing-xs); text-align: center; font-size: var(--font-size-sm); color: var(--text-secondary); font-weight: 600;">Firmy bez danych o składkach</td></tr>
|
||||
<tr><td colspan="16" style="background: var(--border); padding: var(--spacing-xs); text-align: center; font-size: var(--font-size-sm); color: var(--text-secondary); font-weight: 600;">Firmy bez danych o składkach</td></tr>
|
||||
{% endif %}
|
||||
<tr{% if not month and not cf.has_data %} style="opacity: 0.5;"{% endif %}>
|
||||
{% if month %}
|
||||
@ -425,6 +427,14 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td style="white-space:nowrap;">
|
||||
{% set debt = cf.company.previous_years_debt|default(0)|float %}
|
||||
{% if debt > 0 %}
|
||||
<span class="debt-value" style="cursor:pointer;color:var(--error);font-weight:600;font-size:12px;" onclick="editDebt(this, {{ cf.company.id }}, {{ debt }})" title="Kliknij aby edytować">{{ debt|int }} zł</span>
|
||||
{% else %}
|
||||
<span class="debt-value" style="cursor:pointer;color:var(--text-secondary);font-size:11px;" onclick="editDebt(this, {{ cf.company.id }}, 0)" title="Kliknij aby wpisać zaległość">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="font-size:11px;white-space:nowrap;">
|
||||
{% if cf.reminder %}
|
||||
{% if cf.reminder.is_read %}
|
||||
@ -713,6 +723,59 @@ async function generateFees() {
|
||||
}
|
||||
}
|
||||
|
||||
// Inline debt editing
|
||||
function editDebt(el, companyId, currentDebt) {
|
||||
const td = el.parentElement;
|
||||
const input = document.createElement('input');
|
||||
input.type = 'number';
|
||||
input.value = currentDebt || '';
|
||||
input.placeholder = '0';
|
||||
input.style.cssText = 'width:80px;padding:2px 4px;font-size:12px;border:1px solid var(--primary);border-radius:4px;text-align:right;';
|
||||
input.min = '0';
|
||||
input.step = '1';
|
||||
|
||||
td.innerHTML = '';
|
||||
td.appendChild(input);
|
||||
const suffix = document.createElement('span');
|
||||
suffix.textContent = ' zł';
|
||||
suffix.style.fontSize = '11px';
|
||||
td.appendChild(suffix);
|
||||
input.focus();
|
||||
input.select();
|
||||
|
||||
async function saveDebt() {
|
||||
const val = parseFloat(input.value) || 0;
|
||||
const fd = new FormData();
|
||||
fd.append('company_id', companyId);
|
||||
fd.append('debt', val);
|
||||
try {
|
||||
const resp = await fetch('{{ url_for("admin.admin_fees_update_debt") }}', {
|
||||
method: 'POST', body: fd,
|
||||
headers: {'X-CSRFToken': '{{ csrf_token() }}'}
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
if (val > 0) {
|
||||
td.innerHTML = '<span class="debt-value" style="cursor:pointer;color:var(--error);font-weight:600;font-size:12px;" onclick="editDebt(this,' + companyId + ',' + val + ')" title="Kliknij aby edytować">' + val + ' zł</span>';
|
||||
} else {
|
||||
td.innerHTML = '<span class="debt-value" style="cursor:pointer;color:var(--text-secondary);font-size:11px;" onclick="editDebt(this,' + companyId + ',0)" title="Kliknij aby wpisać zaległość">—</span>';
|
||||
}
|
||||
showToast(data.message, 'success');
|
||||
} else {
|
||||
showToast(data.error, 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showToast('Błąd: ' + err, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener('blur', saveDebt);
|
||||
input.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') { e.preventDefault(); input.blur(); }
|
||||
if (e.key === 'Escape') { location.reload(); }
|
||||
});
|
||||
}
|
||||
|
||||
// Close modal on outside click
|
||||
document.getElementById('paymentModal').addEventListener('click', function(e) {
|
||||
if (e.target === this) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user