38686-vm/core/urls.py
Konrad du Plessis 03f177e7d0 feat(adjustments): bulk-delete unpaid rows + floating action bar
New POST /payroll/adjustments/bulk-delete/ endpoint takes a list of
adjustment_ids and DELETEs the ones that are still unpaid
(payroll_record__isnull=True at the DB level) — paid rows are silently
skipped, defensive against stale-UI race conditions. Admin-only;
supervisors get 403. Returns JSON {deleted, requested}.

Floating bar slides up from the bottom of the viewport when >=1 row
selected: shows count + Delete + Clear. Confirm dialog guards the
POST. On success, page reloads to reflect the new state.

CSRF via X-CSRFToken header from the csrftoken cookie (Django
middleware sets this). Two new tests lock in the 'only unpaid' +
'admin-only' contracts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:51:54 +02:00

125 lines
6.4 KiB
Python

# === URL ROUTING ===
# Maps URLs to view functions. Each path() connects a web address to
# the Python function that handles it.
from django.urls import path
from . import views
urlpatterns = [
# Dashboard — the home page after login
path('', views.index, name='home'),
# Attendance logging — where supervisors log daily work
path('attendance/log/', views.attendance_log, name='attendance_log'),
# Work history — table of all work logs with filters
path('history/', views.work_history, name='work_history'),
# CSV export — downloads filtered work logs as a spreadsheet
path('history/export/', views.export_work_log_csv, name='export_work_log_csv'),
# === WORK LOG PAYROLL CROSS-LINK (admin-only) ===
# Click a historic work log -> see who got paid and who didn't.
# AJAX endpoint returns JSON (the modal builds its own DOM safely);
# detail view renders the same data as a shareable full page.
path('history/<int:log_id>/', views.work_log_payroll_detail, name='work_log_payroll_detail'),
path('history/<int:log_id>/payroll/ajax/', views.work_log_payroll_ajax, name='work_log_payroll_ajax'),
# CSV export — downloads all worker data (admin only)
path('workers/export/', views.export_workers_csv, name='export_workers_csv'),
# AJAX toggle — activates/deactivates workers, projects, teams from dashboard
path('toggle/<str:model_name>/<int:item_id>/', views.toggle_active, name='toggle_active'),
# === PAYROLL ===
# Main payroll dashboard — shows pending payments, history, loans, and charts
path('payroll/', views.payroll_dashboard, name='payroll_dashboard'),
# Process payment — pays a worker and links their unpaid logs + adjustments
path('payroll/pay/<int:worker_id>/', views.process_payment, name='process_payment'),
# Batch pay — preview which workers would be paid, then process all at once
path('payroll/batch-pay/preview/', views.batch_pay_preview, name='batch_pay_preview'),
path('payroll/batch-pay/', views.batch_pay, name='batch_pay'),
# Price overtime — creates Overtime adjustments from unpriced OT entries
path('payroll/price-overtime/', views.price_overtime, name='price_overtime'),
# Add a new payroll adjustment (bonus, deduction, loan, etc.)
path('payroll/adjustment/add/', views.add_adjustment, name='add_adjustment'),
# Edit an existing unpaid adjustment
path('payroll/adjustment/<int:adj_id>/edit/', views.edit_adjustment, name='edit_adjustment'),
# Delete an unpaid adjustment
path('payroll/adjustment/<int:adj_id>/delete/', views.delete_adjustment, name='delete_adjustment'),
# Bulk-delete multiple unpaid adjustments at once (Adjustments tab)
path('payroll/adjustments/bulk-delete/', views.bulk_delete_adjustments, name='bulk_delete_adjustments'),
# Preview a worker's payslip (AJAX — returns JSON)
path('payroll/preview/<int:worker_id>/', views.preview_payslip, name='preview_payslip'),
# Worker lookup — AJAX report card for a single worker (returns JSON)
path('payroll/worker-lookup/<int:worker_id>/', views.worker_lookup_ajax, name='worker_lookup_ajax'),
# Add a repayment from the payslip preview modal (AJAX — returns JSON)
path('payroll/repayment/<int:worker_id>/', views.add_repayment_ajax, name='add_repayment_ajax'),
# View a completed payslip (print-friendly page)
path('payroll/payslip/<int:pk>/', views.payslip_detail, name='payslip_detail'),
# === REPORTS ===
# Generate payroll reports filtered by date range, project, or team
path('report/', views.generate_report, name='generate_report'),
path('report/pdf/', views.generate_report_pdf, name='generate_report_pdf'),
# === WORKERS ===
# Admin-friendly worker management UI (alternative to /admin/core/worker/)
path('workers/', views.worker_list, name='worker_list'),
path('workers/new/', views.worker_edit, name='worker_new'),
path('workers/<int:worker_id>/', views.worker_detail, name='worker_detail'),
path('workers/<int:worker_id>/edit/', views.worker_edit, name='worker_edit'),
# Batch report (table of all workers with aggregated history)
path('workers/report/', views.worker_batch_report, name='worker_batch_report'),
path('workers/report/csv/', views.worker_batch_report_csv, name='worker_batch_report_csv'),
path('workers/report/pdf/', views.worker_batch_report_pdf, name='worker_batch_report_pdf'),
# === TEAMS ===
# Admin-friendly team management UI (alternative to /admin/core/team/)
path('teams/', views.team_list, name='team_list'),
path('teams/new/', views.team_edit, name='team_new'),
path('teams/report/', views.team_batch_report, name='team_batch_report'),
path('teams/report/csv/', views.team_batch_report_csv, name='team_batch_report_csv'),
path('teams/<int:team_id>/', views.team_detail, name='team_detail'),
path('teams/<int:team_id>/edit/', views.team_edit, name='team_edit'),
# === PROJECTS ===
# Admin-friendly project management UI (alternative to /admin/core/project/)
path('projects/', views.project_list, name='project_list'),
path('projects/new/', views.project_edit, name='project_new'),
path('projects/report/', views.project_batch_report, name='project_batch_report'),
path('projects/report/csv/', views.project_batch_report_csv, name='project_batch_report_csv'),
path('projects/<int:project_id>/', views.project_detail, name='project_detail'),
path('projects/<int:project_id>/edit/', views.project_edit, name='project_edit'),
# === EXPENSE RECEIPTS ===
# Create a new expense receipt — emails HTML + PDF to Spark Receipt
path('receipts/create/', views.create_receipt, name='create_receipt'),
# === TEMPORARY: Import production data from browser ===
# Visit /import-data/ once to populate the database. Remove after use.
path('import-data/', views.import_data, name='import_data'),
# === TEMPORARY: Run migrations from browser ===
# Visit /run-migrate/ to apply pending database migrations on production.
path('run-migrate/', views.run_migrate, name='run_migrate'),
# === BACKUP / RESTORE (admin-only, browser-accessible) ===
# Flatlogic has no SSH/shell — admins use these to snapshot and
# restore all app data via the browser. See CLAUDE.md "Backup &
# Restore" section for the full procedure.
path('backup-data/', views.backup_data, name='backup_data'),
path('restore-data/', views.restore_data, name='restore_data'),
]