Reuse the existing Pay Period setting (Frequency + Anchor) as the single source of truth via a shared pure helper (models/pay_period.py); fusion.clock.report delegates to it. Add Current/Previous/Next Pay Period filters to the attendance search view (search-method computed booleans on hr.attendance), a Bi-Weekly Period picker wizard (pick start -> auto +2 weeks, editable; Apply opens the filtered list) reachable from an Attendance menu item and a dashboard tile. Window follows the configured frequency; TZ-correct via local-day boundaries. Bump 3.14.4 -> 3.15.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.8 KiB
Python
67 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Copyright 2026 Nexa Systems Inc.
|
||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||
|
||
from datetime import timedelta
|
||
from odoo import models, fields, api, _
|
||
from odoo.exceptions import ValidationError
|
||
from ..models.pay_period import compute_pay_period, period_length_days, current_prev_next
|
||
from ..models.tz_utils import get_local_today, get_local_day_boundaries
|
||
|
||
|
||
class FusionClockPeriodPicker(models.TransientModel):
|
||
"""Pick a pay-period window and open the attendance list filtered to it.
|
||
|
||
Defaults to the current pay period. Changing the start auto-fills the end
|
||
to one pay period later (two weeks by default); the end stays editable so a
|
||
fully custom range can be entered too.
|
||
"""
|
||
_name = 'fusion.clock.period.picker'
|
||
_description = 'Bi-Weekly Period Picker'
|
||
|
||
date_start = fields.Date(string='Period Start', required=True,
|
||
default=lambda self: self._fclk_default_window()[0])
|
||
date_end = fields.Date(string='Period End', required=True,
|
||
default=lambda self: self._fclk_default_window()[1])
|
||
|
||
def _fclk_config(self):
|
||
ICP = self.env['ir.config_parameter'].sudo()
|
||
return (ICP.get_param('fusion_clock.pay_period_type', 'biweekly'),
|
||
ICP.get_param('fusion_clock.pay_period_start', ''))
|
||
|
||
def _fclk_default_window(self):
|
||
frequency, anchor = self._fclk_config()
|
||
return current_prev_next(frequency, anchor, get_local_today(self.env))['current']
|
||
|
||
@api.onchange('date_start')
|
||
def _onchange_date_start(self):
|
||
if not self.date_start:
|
||
return
|
||
frequency, anchor = self._fclk_config()
|
||
length = period_length_days(frequency)
|
||
if length:
|
||
self.date_end = self.date_start + timedelta(days=length - 1)
|
||
else:
|
||
self.date_end = compute_pay_period(frequency, anchor, self.date_start)[1]
|
||
|
||
@api.constrains('date_start', 'date_end')
|
||
def _check_dates(self):
|
||
for rec in self:
|
||
if rec.date_start and rec.date_end and rec.date_end < rec.date_start:
|
||
raise ValidationError(_("Period end cannot be before period start."))
|
||
|
||
def action_apply(self):
|
||
self.ensure_one()
|
||
start_utc, _dummy = get_local_day_boundaries(self.env, self.date_start)
|
||
_dummy2, end_excl_utc = get_local_day_boundaries(self.env, self.date_end)
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': _("Attendances · %s – %s") % (self.date_start, self.date_end),
|
||
'res_model': 'hr.attendance',
|
||
'view_mode': 'list,form',
|
||
'domain': ['&',
|
||
('check_in', '>=', fields.Datetime.to_string(start_utc)),
|
||
('check_in', '<', fields.Datetime.to_string(end_excl_utc))],
|
||
'target': 'current',
|
||
}
|