15 Commits

Author SHA1 Message Date
Konrad du Plessis
32972276b5 feat(absences): add optional project FK on Absence
Migration 0015 adds Project FK (SET_NULL, nullable) to Absence.
When is_paid=True, the auto-Bonus PayrollAdjustment inherits the
project for cost-attribution. Form + admin + list + edit + log
templates expose the field. List view filter now uses
absence.project_id directly (was indirect via worker__work_logs).
5 new tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 22:11:22 +02:00
Konrad du Plessis
bf6f0a5c74 feat(absences): Absence model + migration + admin registration
Per-worker dated records with 8 reason choices (Sick/Family/Annual/
Personal-Unpaid/IOD/Suspension/Absconded/Other), is_paid flag, optional
OneToOne to PayrollAdjustment for the auto-Bonus path, audit fields.
Unique-per-day at DB layer. 4 model tests.
2026-05-14 19:22:06 +02:00
Konrad du Plessis
864ae722c4 feat(site-report): structured site progress logging — Phase A.1
Companion to attendance: capture WHAT was done on site each day,
alongside WHO worked. Optional 1:1 with WorkLog. Mobile-first form
auto-redirected from /attendance/log/ on success (with a Skip link).

Why this design (vs. extending WorkLog or per-project templates):

- Hybrid schema. Stable + queryable fields are real columns
  (`weather`, `temperature_min`, `temperature_max`, `notes`,
  `created_by`, `created_at`, `updated_at`). The METRICS that change
  per project / over time live in a single JSONField with shape
  `{counts: {key: int}, checks: {key: bool}}` — driven by
  `core/site_report_schema.py`. Adding a new metric is a one-line
  edit to that file, NO migration required. Old reports without the
  new key just render as 0 / unchecked.

- Two-step flow. Attendance form is unchanged; on successful POST
  the supervisor lands on `/site-report/<work_log_id>/edit/` for the
  most-recently-created log. They can fill in progress details
  (~30 sec on a phone) or click "Skip" to home. WorkLogs without a
  SiteReport are completely valid historic rows.

- Permission scope mirrors WorkLog access. Anyone who can see the
  parent log (admin / log's supervisor / project's supervisors) can
  see + edit its SiteReport. Wraps the existing pattern from
  `work_history()` in a small helper `_can_access_site_report()`.

What ships:

  Models:
    - SiteReport (1:1 → WorkLog, weather choices, IntegerField temps,
      JSONField metrics defaulting to {})
    - Migration 0013_add_site_report (pure CreateModel, no schema
      changes to existing tables)

  Schema:
    - core/site_report_schema.py (NEW) — single source of truth for
      the metric list. Currently 7 counts + 4 checks per Konrad's
      v1 spec. Helpers: get_count_keys, get_check_keys, label_for,
      empty_metrics.

  Form:
    - SiteReportForm (in core/forms.py) — ModelForm with the four
      stable fields PLUS dynamic IntegerField/BooleanField per
      metric in __init__. save() serializes both halves into the
      JSON blob. clean() validates min ≤ max temperature.

  Views:
    - site_report_edit — create-or-update; stamps created_by on
      first save; preserves it on subsequent admin edits
    - site_report_detail — read-only display; 404 when no report
    - attendance_log redirect updated to two-step flow
    - _can_access_site_report — shared permission helper

  URLs:
    - /site-report/<work_log_id>/edit/  (name: site_report_edit)
    - /site-report/<work_log_id>/       (name: site_report_detail)

  Templates:
    - site_report_edit.html — mobile-first stack of inputs, weather
      as a chunky icon-button row (☀️ ☁️ 🌧️ ⛈️ 🥵 🥶 💨), counts in a
      2-col grid, checks as toggle switches, Notes textarea, Skip
      + Save buttons. Iterates pre-built (metric, bound_field)
      pairs from the view to avoid needing a new template filter.
    - site_report_detail.html — counts as accent-coloured value
      cards, checks as a check-list, weather + temp + notes + edit
      link.
    - work_history.html — added a small clipboard icon next to
      each row's date: filled (linked to detail) when a report
      exists, muted outline (linked to edit) when not. Click is
      event.stopPropagation()-ed so the row's payroll-modal
      handler doesn't also fire.

  Performance:
    - work_history queryset adds .select_related('site_report') so
      the new template indicator doesn't introduce an N+1.

  Admin:
    - SiteReport registered with raw_id_fields on work_log +
      created_by, list filters on weather + project + date.

  Tests (16 new, full suite 85/85):
    - SiteReportModelTests — defaults, 1:1 reverse accessor,
      arbitrary-key JSON round-trip
    - SiteReportFormTests — dynamic field generation, save
      serialisation, temp validation, instance pre-fill
    - SiteReportEditViewTests — admin GET/POST, project
      supervisor allowed, outsider supervisor 403, created_by
      preserved on subsequent admin edits
    - SiteReportDetailViewTests — 404 when absent, displays data
      when present
    - AttendanceLogRedirectsToSiteReportTests — confirms the
      two-step flow

  CLAUDE.md updates:
    - SiteReport added to "Key Models" with shape + reverse-accessor note
    - New "SiteReport metric schema" section near "UI-vs-DB
      naming drift" — explains the JSON-column-with-Python-source
      pattern, when it's safe, what NOT to do (rename a key with
      data), and where the keys appear across the codebase
    - URL Routes table gets the two new endpoints

What's NOT in this commit (deferred per the brainstorm plan):
  - JournalEntry model + manual web-entry UI (Phase A.2 — depends
    on Konrad's Q7 answer about Vi/recipient field)
  - Letterly inbound webhook (Phase B — integrations branch only,
    depends on Q5 sample payload)
  - Photos on site reports (Q9, defaulted to "future")
  - Per-project metric templates (Q4, defaulted to "same set for all v1")

Reference plan: ~/.claude/plans/prancy-painting-brook.md (local).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 02:29:33 +02:00
Konrad du Plessis
c1d9014fe1 ux(labels): shorter adjustment type labels (display-only rename)
Path A rename - DB values untouched, only TYPE_CHOICES display
labels change:
  'New Loan'          -> shown as 'Loan'
  'Advance Payment'   -> shown as 'Advance'
  'Advance Repayment' -> shown as 'Advance Repaid'

Templates that render the type as visible text switched from
{{ adj.type }} to {{ adj.get_type_display }}. Data attributes and
CSS class slugs keep the raw DB value (identifiers, not labels).

Zero data migration. Zero changes to ADDITIVE_TYPES / DEDUCTIVE_TYPES
constants, hardcoded string comparisons, CSS class names, test
fixtures, or any other code that references the canonical DB value.
Every historic PayrollAdjustment row keeps type='New Loan' /
'Advance Payment' / 'Advance Repayment' as stored.

Django's makemigrations generated a no-op AlterField migration to
record the choices-metadata change.

Tests: 69/69.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:49:26 +02:00
Konrad du Plessis
3c28387dd3 WIP: 2026-04-22 session checkpoint
Complete working state of the session. Will be split into two deploy
phases (safety scaffolding then feature release) before merging to ai-dev.

Includes:
- Security fixes (email creds / SECRET_KEY / DEBUG / CSRF)
- Backup + restore management commands and browser endpoints
- WeasyPrint migration (replaces xhtml2pdf)
- New Worker fields + WorkerCertificate + WorkerWarning models
- Worker / Team / Project friendly management UIs
- Dashboard cert-expiry card + Manage All buttons
- Bootstrap tooltips (global init + theme-aware CSS)
- Django admin template override (taller M2M pickers)
- Money filter for ZAR currency formatting
- Resources dropdown nav
- Massive CLAUDE.md expansion + deploy plan docs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 00:19:15 +02:00
Konrad du Plessis
81009be0c6 Add PPE sizing and drivers license fields to Worker model
New fields: shoe_size, overall_top_size, pants_size, tshirt_size,
has_drivers_license (boolean), drivers_license (file upload).
Admin organised into 3 fieldsets. CSV export updated with new columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:10:46 +02:00
Konrad du Plessis
409e7bfd57 Add split payslip feature with team pay schedules
Enable selective payment of work logs and adjustments instead of
all-or-nothing. The preview modal now shows checkboxes on every item
(all checked by default) with dynamic net pay recalculation.

Teams can be configured with a pay frequency (weekly/fortnightly/monthly)
and anchor start date. When set, a "Split at Pay Date" button appears
that auto-unchecks items outside the current pay period.

Key changes:
- Team model: add pay_frequency and pay_start_date fields
- preview_payslip: return IDs, dates, and pay period info in JSON
- process_payment: accept optional selected_log_ids/selected_adj_ids
- Preview modal JS: checkboxes, recalcNetPay(), Split button, Pay Selected
- Backward compatible: existing Pay button still processes everything

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 21:07:28 +02:00
Konrad du Plessis
0257b454af Add Advance Payment system + enhanced preview modal with inline repayments
Redesign Advance Payments to work like loans with tracked balances:
- Add loan_type field to Loan model ('loan' or 'advance')
- Move Advance Payment from DEDUCTIVE to ADDITIVE types (worker receives money)
- Add new Advance Repayment type for deducting from future salary
- Create/edit/delete handlers mirror New Loan behavior for advances
- Loans & Advances tab with type badges and filter buttons

Enhance Payslip Preview modal into "Worker Payment Hub":
- Show outstanding loans & advances with balances in preview
- Inline repayment form per loan (amount pre-filled, note, Deduct button)
- AJAX add_repayment_ajax endpoint creates adjustment without page reload
- Modal auto-refreshes after repayment showing updated net pay
- New refreshPreview() JS function enables re-fetching after AJAX

Other changes:
- Rename History to Work History in navbar
- Advance-specific payslip layout for pure advance payments
- Fix JS noProjectTypes to hide Project field for advance types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:46:58 +02:00
Konrad du Plessis
19c662ec7d Fix 3 critical bugs in dashboard + attendance logging
- Fix outstanding payments: check per-worker (not per-log) to handle partially-paid WorkLogs
- Fix adjustment math: deductions now subtract from outstanding instead of adding
- Fix conflict resolution: use explicit worker ID list (QueryDict.getlist) instead of broken form.data.workers iteration
- Add missing migration 0003 for Project start_date/end_date fields
- Add CLAUDE.md project documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:28:11 +02:00
Konrad du Plessis
ef77c97719 Revert project dates — migrations not running on Flatlogic
Removed start_date and end_date from Project model. Flatlogic doesn't
run migrations during rebuild, so the DB columns never got created,
crashing the site. Active/inactive resource split is kept.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 01:10:40 +02:00
Konrad du Plessis
47de74bde4 Show active resources by default, collapse inactive + add project dates
Dashboard Manage Resources now shows only active workers/projects/teams
by default. Inactive items are hidden behind a collapsible "Show X
Inactive" button — faded at 50% opacity. Tab badges show active counts.

Also adds start_date and end_date fields to Project model (optional).
Dates display under the project name in the resource list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 00:49:57 +02:00
Konrad du Plessis
3199e52e72 Add data migration to set real SA ID numbers for all workers
Reads ID numbers from the workers_list.xlsx data and matches workers
by first name + surname (case-insensitive). Handles name variations
like "Soldier Aphiwe Dobe" matching "Aphiwe" + "Dobe".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 00:24:15 +02:00
Konrad du Plessis
b1f415b72b Remove all .pyc files from git tracking
These compiled bytecode files were causing Flatlogic's Gemini AI to
get stuck in infinite loops reading them. They are now in .gitignore
and will not be tracked going forward.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:00:00 +02:00
Flatlogic Bot
d10151cf40 Ver 01 2026-02-22 12:26:15 +00:00
Flatlogic Bot
d3fb8046d5 Initial version 2026-02-22 12:14:54 +00:00