Files
Odoo-Modules/fusion_clock/CLAUDE.md
gsinghpal 005daade55 changes
2026-05-23 07:53:41 -04:00

15 KiB

Fusion Clock - Claude Code Instructions

Read together with the repo-root ../CLAUDE.md for global Odoo 19 rules, asset-cache handling, Supabase KB notes, and shared Fusion conventions. This file is only for the fusion_clock module.

1. What This Module Is

  • Name: Fusion Clock.
  • Version: 19.0.3.3.0.
  • Category: Human Resources/Attendances.
  • License: OPL-1, Nexa Systems Inc.
  • Purpose: complete time and attendance app built on Odoo hr.attendance.
  • Top-level menu: Fusion Clock.
  • Main surfaces:
    • Portal clock page at /my/clock.
    • Portal timesheets at /my/clock/timesheets.
    • Portal reports at /my/clock/reports.
    • Shared PIN kiosk at /fusion_clock/kiosk.
    • NFC tap kiosk at /fusion_clock/kiosk/nfc.
    • Backend systray clock widget.
    • Backend manager/team-lead dashboard client action.

Core behaviours: geofenced clock-in/out, IP whitelist fallback, shift scheduling, break deduction, penalties, overtime, auto clock-out, absence detection, leave requests, correction workflow, payroll CSV export, PDF reports, weekly summaries, shared kiosk, NFC kiosk with photo capture, and activity audit logs.

2. Dependencies

Declared in __manifest__.py:

hr_attendance, hr, portal, mail, resource

External Python used directly:

  • pytz for timezone-safe local day boundaries.
  • requests for Google Geocoding, OpenStreetMap/Nominatim fallback, and IP metadata.
  • dateutil.relativedelta inside pay-period calculations.

External browser APIs:

  • Browser geolocation.
  • ipapi.co fallback geolocation in frontend/backend clock widgets.
  • Google Maps/Places when fusion_clock.google_maps_api_key is configured.
  • Web NFC and camera APIs for the NFC kiosk.

3. Naming And Field Prefixes

This module uses the module-specific prefix x_fclk_* on inherited Odoo models, not x_fc_*.

Examples:

  • hr.employee.x_fclk_enable_clock
  • hr.employee.x_fclk_nfc_card_uid
  • hr.attendance.x_fclk_clock_source
  • res.company.x_fclk_nfc_kiosk_location_id

New inherited fields in this module should keep the x_fclk_* prefix unless there is a strong migration reason not to.

4. Model Map

Custom models:

Model File Purpose
fusion.clock.location models/clock_location.py Geofenced/IP-whitelisted clock locations.
fusion.clock.shift models/clock_shift.py Shift start/end/break schedule assigned to employees.
fusion.clock.penalty models/clock_penalty.py Late clock-in / early clock-out penalty records.
fusion.clock.activity.log models/clock_activity_log.py Append-style audit log for clock activity, geofence misses, absences, NFC enrolment, corrections.
fusion.clock.leave.request models/clock_leave_request.py Portal leave requests, auto-approved but office-notified.
fusion.clock.correction models/clock_correction.py Timesheet correction requests with approve/reject workflow.
fusion.clock.report models/clock_report.py Employee or batch pay-period report with PDF/CSV export and email send.
fusion.clock.nfc.enrollment.wizard wizard/clock_nfc_enrollment_wizard.py Backend NFC card enrolment/reassignment wizard.

Inherited models:

  • hr.employee: enable clock, default location, shift, kiosk PIN, NFC UID, pending reason flag, streaks, absence/overtime counters, and One2many links.
  • hr.attendance: clock source, location, distances, photos, break minutes, net hours, penalties, auto clock-out flag, overtime fields.
  • res.config.settings: all fusion_clock.* settings.
  • res.company: NFC kiosk location binding.

Timezone helpers live in models/tz_utils.py. Use get_local_today() and get_local_day_boundaries() for attendance domains instead of comparing UTC dates directly.

5. Clocking Flow

Primary API endpoint: /fusion_clock/clock_action in controllers/clock_api.py.

Clock-in flow:

  1. Resolve current user to hr.employee.
  2. Block if x_fclk_enable_clock is false.
  3. If x_fclk_pending_reason is true, return requires_reason.
  4. Verify location against allowed active fusion.clock.location records.
  5. Call Odoo's _attendance_action_change().
  6. Write location, distance, source, and optional photo to hr.attendance.
  7. Log clock_in.
  8. Create late_in penalty when outside grace.
  9. Increment/reset on-time streak; log milestone at 5, 10, 20, 50, 100.
  10. Notify office user for very-late clock-ins.

Clock-out flow:

  1. Verify location again.
  2. Call _attendance_action_change().
  3. Write out-distance.
  4. Apply break deduction when configured.
  5. Create early_out penalty when outside grace.
  6. Log clock_out.
  7. Log overtime if computed overtime is positive.

Location verification uses GPS when coordinates are available and geocoded locations exist. IP whitelist matching is attempted when a client IP is available. Error types include no_locations, gps_unavailable, no_geocoded, and outside.

6. Kiosk And NFC

Classic kiosk:

  • Page: /fusion_clock/kiosk
  • JSON routes:
    • /fusion_clock/kiosk/search
    • /fusion_clock/kiosk/verify_pin
    • /fusion_clock/kiosk/clock
  • Requires fusion_clock.group_fusion_clock_manager.
  • Controlled by fusion_clock.enable_kiosk and fusion_clock.kiosk_pin_required.
  • Uses hr.employee.x_fclk_kiosk_pin.

NFC kiosk:

  • Page: /fusion_clock/kiosk/nfc
  • JSON routes:
    • /fusion_clock/kiosk/nfc/enroll
    • /fusion_clock/kiosk/nfc/tap
    • /fusion_clock/kiosk/nfc/employee_search
  • Requires fusion_clock.group_fusion_clock_manager.
  • Controlled by:
    • fusion_clock.enable_nfc_kiosk
    • fusion_clock.nfc_photo_required
    • fusion_clock.nfc_enroll_password
    • fusion_clock.nfc_kiosk_debug
    • res.company.x_fclk_nfc_kiosk_location_id
  • Card UID canonical format is uppercase colon-separated hex, e.g. 04:A2:B5:62:C1:80.
  • Normalization lives in FusionClockNfcKiosk._normalize_uid() and is reused by the backend wizard.
  • Tap debounce is module-level memory in controllers/clock_nfc_kiosk.py: same UID within 5 seconds returns debounce.
  • Photo data URLs are stripped before writing binary fields.
  • NFC clock-ins write x_fclk_check_in_photo; NFC clock-outs write x_fclk_check_out_photo.

Important: unknown-card taps currently return card_unknown; the unknown_card_tap log type exists but is not written by the endpoint.

7. Reports And Payroll Export

fusion.clock.report supports:

  • Employee reports when employee_id is set.
  • Batch reports when employee_id is empty.
  • PDF generation through QWeb reports:
    • fusion_clock.action_report_clock_employee
    • fusion_clock.action_report_clock_batch
  • CSV export via action_export_csv().
  • Custom CSV headings via JSON in fusion_clock.csv_column_mapping.
  • Email send with generated PDF attached.

Pay period types:

weekly, biweekly, semi_monthly, monthly

The anchor date setting is fusion_clock.pay_period_start as a string in YYYY-MM-DD format.

Historical report generation is exposed through the Generate Historical Reports menu action and creates draft reports for completed attendance periods. The scheduled report cron only generates when yesterday is the period end.

8. Scheduled Automation

Configured in data/ir_cron_data.xml:

Cron Model method Frequency
Fusion Clock: Auto Clock-Out hr.attendance._cron_fusion_auto_clock_out() Every 15 minutes
Fusion Clock: Generate Period Reports fusion.clock.report._cron_generate_period_reports() Daily
Fusion Clock: Daily Absence Check hr.attendance._cron_fusion_check_absences() Daily
Fusion Clock: Employee Reminders hr.attendance._cron_fusion_employee_reminders() Every 15 minutes
Fusion Clock: Weekly Summary hr.attendance._cron_fusion_weekly_summary() Daily, internally sends Mondays

Auto clock-out closes open attendances after scheduled end plus grace, capped by max shift hours. It sets x_fclk_pending_reason so the employee must explain before clocking in again.

Absence detection checks enabled employees, skips weekends and global resource calendar leaves, and logs absent when no attendance or leave request exists.

9. Security

Groups:

  • group_fusion_clock_user
  • group_fusion_clock_team_lead
  • group_fusion_clock_manager

Admin is auto-assigned to manager in security/security.xml.

Access pattern:

  • Users and portal users can read their own clock data.
  • Team leads can read direct reports for penalties, activity logs, corrections, and dashboard data.
  • Managers have full model access and all configuration/kiosk/report menus.
  • Portal rules are defined for hr.attendance, fusion.clock.location, fusion.clock.report, fusion.clock.penalty, fusion.clock.activity.log, fusion.clock.leave.request, fusion.clock.correction, and fusion.clock.shift.

Backend dashboard access is checked in /fusion_clock/dashboard_data: manager sees all enabled employees; team lead sees employees where parent_id is the current user's employee.

10. Frontend Assets

Frontend bundle:

  • static/src/css/portal_clock.css
  • static/src/scss/nfc_kiosk.scss
  • static/src/js/fusion_clock_portal.js
  • static/src/js/fusion_clock_kiosk.js
  • static/src/js/fusion_clock_nfc_kiosk.js

Backend bundle:

  • static/src/scss/fusion_clock.scss
  • static/src/js/fusion_clock_systray.js
  • static/src/xml/systray_clock.xml
  • static/src/js/fusion_clock_dashboard.js
  • static/src/xml/fusion_clock_dashboard.xml
  • static/src/js/fusion_clock_location_map.js
  • static/src/js/fusion_clock_location_places.js
  • static/src/xml/fusion_clock_location.xml

Patterns:

  • Public portal/kiosk JS should use Interaction from @web/public/interaction and register in registry.category("public.interactions").
  • Backend OWL client actions and field widgets use standalone rpc() from @web/core/network/rpc.
  • fusion_clock_systray.js is a systray OWL component registered as fusion_clock.ClockSystray.
  • fusion_clock_dashboard.js is a client action registered as fusion_clock.Dashboard.
  • Location widgets are registered field widgets: fclk_location_map and fclk_places_autocomplete.

Known technical debt:

  • static/src/js/fusion_clock_nfc_kiosk.js is currently an isolated IIFE. If touching it, prefer migrating to an Odoo 19 Interaction instead of expanding the IIFE pattern.
  • static/src/css/portal_clock.css and static/src/scss/fusion_clock.scss contain runtime dark-mode selectors/media rules. For backend SCSS changes, follow the repo-root Odoo 19 compile-time dark bundle guidance.
  • fusion_clock.scss uses some Bootstrap CSS vars for status accents. Avoid relying on Bootstrap vars for card/background/border surfaces in new dashboard work.

11. Settings Keys

Important ir.config_parameter keys:

fusion_clock.default_clock_in_time
fusion_clock.default_clock_out_time
fusion_clock.default_break_minutes
fusion_clock.auto_deduct_break
fusion_clock.break_threshold_hours
fusion_clock.enable_auto_clockout
fusion_clock.grace_period_minutes
fusion_clock.max_shift_hours
fusion_clock.enable_penalties
fusion_clock.penalty_grace_minutes
fusion_clock.penalty_deduction_minutes
fusion_clock.enable_overtime
fusion_clock.daily_overtime_threshold
fusion_clock.weekly_overtime_threshold
fusion_clock.office_user_id
fusion_clock.very_late_threshold_minutes
fusion_clock.max_monthly_absences
fusion_clock.enable_employee_notifications
fusion_clock.reminder_before_shift_minutes
fusion_clock.reminder_before_end_minutes
fusion_clock.send_weekly_summary
fusion_clock.enable_ip_fallback
fusion_clock.enable_photo_verification
fusion_clock.google_maps_api_key
fusion_clock.enable_kiosk
fusion_clock.kiosk_pin_required
fusion_clock.enable_correction_requests
fusion_clock.enable_sounds
fusion_clock.pay_period_type
fusion_clock.pay_period_start
fusion_clock.auto_generate_reports
fusion_clock.send_employee_reports
fusion_clock.report_recipient_user_ids
fusion_clock.report_recipient_emails
fusion_clock.csv_column_mapping
fusion_clock.enable_nfc_kiosk
fusion_clock.nfc_photo_required
fusion_clock.nfc_enroll_password
fusion_clock.nfc_kiosk_debug

fclk_report_recipient_user_ids is a Many2many on settings but is persisted manually as comma-separated user IDs in fusion_clock.report_recipient_user_ids.

12. Routes

HTTP pages:

/my/clock
/my/clock/timesheets
/my/clock/reports
/my/clock/reports/<report_id>/download
/fusion_clock/kiosk
/fusion_clock/kiosk/nfc

JSON-RPC endpoints:

/fusion_clock/verify_location
/fusion_clock/clock_action
/fusion_clock/submit_reason
/fusion_clock/request_leave
/fusion_clock/request_correction
/fusion_clock/get_status
/fusion_clock/get_locations
/fusion_clock/get_settings
/fusion_clock/dashboard_data
/fusion_clock/kiosk/search
/fusion_clock/kiosk/verify_pin
/fusion_clock/kiosk/clock
/fusion_clock/kiosk/nfc/enroll
/fusion_clock/kiosk/nfc/tap
/fusion_clock/kiosk/nfc/employee_search

All new JSON endpoints must use type="jsonrpc", not deprecated type="json".

13. Gotchas

  • Always use local-day helpers for date domains. UTC midnight boundaries will break attendance totals around timezone offsets.
  • hr.employee._get_fclk_scheduled_times(date) returns naive UTC datetimes suitable for Odoo comparisons.
  • Break deduction is stored as minutes in hr.attendance.x_fclk_break_minutes; penalties add to that same field.
  • x_fclk_net_hours is computed from Odoo worked_hours minus break minutes.
  • Daily overtime currently compares net hours to employee scheduled hours or daily threshold; weekly threshold is configured but not used in hr.attendance._compute_overtime_hours().
  • fusion_clock.enable_ip_fallback exists in settings, but server-side _verify_location() attempts IP whitelist matching whenever a client IP is present.
  • NFC kiosk needs a company-level x_fclk_nfc_kiosk_location_id; without it taps return no_location_configured.
  • Kiosk routes are authenticated (auth='user') and manager-gated; wall tablets need a manager-authorised kiosk user.
  • Portal report download manually streams the PDF binary rather than using fusion_pdf_preview.
  • If CSS/assets change, bump __manifest__.py version so Odoo rebuilds bundles.

14. Tests

Tests are post-install tagged:

@tagged('-at_install', 'post_install', 'fusion_clock')

Coverage currently focuses on NFC:

  • tests/test_nfc_models.py: employee UID uniqueness, attendance NFC source/photo fields, company kiosk location field.
  • tests/test_clock_nfc_kiosk.py: kiosk page gating, UID normalization, enroll endpoint, tap happy path, tap errors, photo-required handling, employee search.

Run locally:

docker exec odoo-dev-app odoo -d fusion-dev -u fusion_clock --test-tags fusion_clock --stop-after-init

For a normal module upgrade:

docker exec odoo-dev-app odoo -d fusion-dev -u fusion_clock --stop-after-init