# -*- coding: utf-8 -*- from odoo import api, fields, models, _ from odoo.exceptions import UserError from dateutil.relativedelta import relativedelta class RunPayrollWizard(models.TransientModel): _name = 'run.payroll.wizard' _description = 'Run Payroll' _rec_name = 'display_name' display_name = fields.Char(compute='_compute_display_name') @api.depends('pay_schedule', 'date_start', 'date_end') def _compute_display_name(self): schedule_labels = { 'weekly': 'Weekly', 'biweekly': 'Bi-Weekly', 'semi_monthly': 'Semi-Monthly', 'monthly': 'Monthly', } for wizard in self: schedule = schedule_labels.get(wizard.pay_schedule, 'Bi-Weekly') wizard.display_name = f"Run Payroll: {schedule}" # === Wizard State === state = fields.Selection([ ('entry', 'Enter Payroll'), ('preview', 'Preview Payroll'), ], string='State', default='entry') # === Company & Schedule === company_id = fields.Many2one( 'res.company', string='Company', required=True, default=lambda self: self.env.company, ) # === Pay Period Selection (QuickBooks-like dropdown) === pay_period_id = fields.Many2one( 'payroll.pay.period', string='Pay Period', domain="[('company_id', '=', company_id)]", ) pay_schedule = fields.Selection([ ('weekly', 'Weekly'), ('biweekly', 'Bi-Weekly'), ('semi_monthly', 'Semi-Monthly'), ('monthly', 'Monthly'), ], string='Pay Schedule', required=True, default='biweekly') # === Chart of Accounts (for journal entries) === bank_account_id = fields.Many2one( 'account.account', string='Funding Account', domain="[('account_type', 'in', ['asset_cash', 'liability_credit_card'])]", help='Bank or credit card account to fund payroll', ) # === Preview Summary Fields === total_payroll_cost = fields.Monetary( string='Total Payroll Cost', currency_field='currency_id', compute='_compute_preview_totals', ) change_from_last = fields.Float( string='Change from Last (%)', compute='_compute_preview_totals', ) date_start = fields.Date( string='Period Start', required=True, default=lambda self: self._default_date_start(), ) date_end = fields.Date( string='Period End', required=True, default=lambda self: self._default_date_end(), ) pay_date = fields.Date( string='Pay Date', required=True, default=lambda self: fields.Date.context_today(self) + relativedelta(days=7), ) # Period display name for dropdown period_display = fields.Char( string='Period', compute='_compute_period_display', ) # === Employee Payroll Entries (QuickBooks-like grid) === entry_ids = fields.One2many( 'payroll.entry', 'wizard_id', string='Payroll Entries', ) # Summary totals total_regular_hours = fields.Float( string='Total Regular Hours', compute='_compute_totals', ) total_hours = fields.Float( string='Total Hours', compute='_compute_totals', ) total_gross_pay = fields.Monetary( string='Total Gross Pay', currency_field='currency_id', compute='_compute_totals', ) total_net_pay = fields.Monetary( string='Total Net Pay', currency_field='currency_id', compute='_compute_totals', ) total_employee_taxes = fields.Monetary( string='Total Employee Taxes', currency_field='currency_id', compute='_compute_totals', ) total_employer_taxes = fields.Monetary( string='Total Employer Taxes', currency_field='currency_id', compute='_compute_totals', ) employee_count = fields.Integer( string='Employees', compute='_compute_totals', ) selected_count = fields.Integer( string='Selected', compute='_compute_totals', ) currency_id = fields.Many2one( 'res.currency', string='Currency', default=lambda self: self.env.company.currency_id, ) # === Generated Payslips === payslip_run_id = fields.Many2one( 'hr.payslip.run', string='Payslip Batch', readonly=True, ) payslip_count = fields.Integer( string='Payslips Generated', compute='_compute_payslip_count', ) # Available periods for dropdown available_period_ids = fields.Many2many( 'payroll.pay.period', string='Available Periods', compute='_compute_available_periods', ) def _default_date_start(self): """Default to start of current pay period""" today = fields.Date.context_today(self) # Default to start of current bi-weekly period (Monday) return today - relativedelta(days=today.weekday()) def _default_date_end(self): """Default to end of current pay period""" today = fields.Date.context_today(self) start = today - relativedelta(days=today.weekday()) return start + relativedelta(days=13) @api.depends('date_start', 'date_end') def _compute_period_display(self): for wizard in self: if wizard.date_start and wizard.date_end: wizard.period_display = f"{wizard.date_start.strftime('%m.%d.%Y')} to {wizard.date_end.strftime('%m.%d.%Y')}" else: wizard.period_display = '' @api.depends('company_id', 'pay_schedule') def _compute_available_periods(self): today = fields.Date.context_today(self) six_months_ago = today - relativedelta(months=6) for wizard in self: # Get periods from last 6 months plus future periods periods = self.env['payroll.pay.period'].search([ ('company_id', '=', wizard.company_id.id), ('schedule_type', '=', wizard.pay_schedule), '|', ('date_start', '>=', six_months_ago), # Past 6 months ('date_end', '>=', today), # Current and future ], order='date_start asc') # Sort ascending so current is near top wizard.available_period_ids = periods @api.depends('entry_ids', 'entry_ids.regular_hours', 'entry_ids.total_hours', 'entry_ids.gross_pay', 'entry_ids.net_pay', 'entry_ids.total_employee_tax', 'entry_ids.total_employer_tax') def _compute_totals(self): for wizard in self: wizard.total_regular_hours = sum(wizard.entry_ids.mapped('regular_hours')) wizard.total_hours = sum(wizard.entry_ids.mapped('total_hours')) wizard.total_gross_pay = sum(wizard.entry_ids.mapped('gross_pay')) wizard.total_net_pay = sum(wizard.entry_ids.mapped('net_pay')) wizard.total_employee_taxes = sum(wizard.entry_ids.mapped('total_employee_tax')) wizard.total_employer_taxes = sum(wizard.entry_ids.mapped('total_employer_tax')) wizard.employee_count = len(wizard.entry_ids) wizard.selected_count = len(wizard.entry_ids.filtered(lambda e: e.regular_hours > 0)) @api.depends('entry_ids', 'entry_ids.gross_pay', 'entry_ids.total_employer_tax') def _compute_preview_totals(self): for wizard in self: gross = sum(wizard.entry_ids.mapped('gross_pay')) employer_tax = sum(wizard.entry_ids.mapped('total_employer_tax')) wizard.total_payroll_cost = gross + employer_tax # Calculate change from last payroll last_payroll = self.env['hr.payslip.run'].search([ ('company_id', '=', wizard.company_id.id), ('state', 'in', ['close', 'paid']), ], order='date_end desc', limit=1) if last_payroll and last_payroll.slip_ids: last_total = sum(last_payroll.slip_ids.mapped('net_wage')) if last_total > 0: wizard.change_from_last = ((wizard.total_net_pay - last_total) / last_total) * 100 else: wizard.change_from_last = 0 else: wizard.change_from_last = 0 @api.depends('payslip_run_id') def _compute_payslip_count(self): for wizard in self: if wizard.payslip_run_id: wizard.payslip_count = len(wizard.payslip_run_id.slip_ids) else: wizard.payslip_count = 0 @api.onchange('pay_period_id') def _onchange_pay_period_id(self): """Update dates when period is selected.""" if self.pay_period_id: self.date_start = self.pay_period_id.date_start self.date_end = self.pay_period_id.date_end if self.pay_period_id.pay_date: self.pay_date = self.pay_period_id.pay_date @api.onchange('pay_schedule', 'date_start') def _onchange_pay_schedule(self): """Adjust date_end based on pay schedule""" if self.date_start and self.pay_schedule: if self.pay_schedule == 'weekly': self.date_end = self.date_start + relativedelta(days=6) elif self.pay_schedule == 'biweekly': self.date_end = self.date_start + relativedelta(days=13) elif self.pay_schedule == 'semi_monthly': if self.date_start.day <= 15: self.date_end = self.date_start.replace(day=15) else: self.date_end = self.date_start + relativedelta(months=1, day=1) - relativedelta(days=1) elif self.pay_schedule == 'monthly': self.date_end = self.date_start + relativedelta(months=1, day=1) - relativedelta(days=1) @api.onchange('company_id', 'pay_schedule') def _onchange_load_employees(self): """Load employees when company or schedule changes.""" self._load_employees() def _load_employees(self): """Load all active employees into entries.""" if not self.company_id: return # Clear existing entries self.entry_ids = [(5, 0, 0)] # Find active employees in the company employees = self.env['hr.employee'].search([ ('company_id', '=', self.company_id.id), ('active', '=', True), ]) entries = [] for employee in employees: # Get payment method from Fusion Payroll settings payment_method = getattr(employee, 'payment_method', 'cheque') or 'cheque' # Get vacation pay percent from Fusion Payroll settings (vacation_rate field) vacation_percent = getattr(employee, 'vacation_rate', 4.0) or 4.0 entry_vals = { 'employee_id': employee.id, 'payment_method': payment_method, 'vacation_pay_percent': vacation_percent, 'regular_hours': 0, 'stat_holiday_hours': 0, } entries.append((0, 0, entry_vals)) self.entry_ids = entries def action_add_employee(self): """Open dialog to add an employee.""" return { 'type': 'ir.actions.act_window', 'name': _('Add Employee'), 'res_model': 'hr.employee', 'view_mode': 'kanban,list,form', 'target': 'new', 'context': { 'default_company_id': self.company_id.id, 'payroll_wizard_id': self.id, }, } def action_load_attendance(self): """Load attendance hours for all employees in the period.""" for entry in self.entry_ids: entry.action_load_attendance_hours() return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Attendance Loaded'), 'message': _('Loaded attendance hours for %d employees.') % len(self.entry_ids), 'type': 'success', }, } def action_preview_payroll(self): """Show the preview page with totals.""" self.ensure_one() # Validate that at least one employee has hours entries_with_pay = self.entry_ids.filtered( lambda e: e.regular_hours > 0 or e.stat_holiday_hours > 0 or e.stat_pay_avg_daily_wage > 0 ) if not entries_with_pay: raise UserError(_("No employees have hours entered. Please enter hours for at least one employee.")) # Set state to preview (this was incorrectly indented before) self.state = 'preview' return { 'type': 'ir.actions.act_window', 'name': _('Run Payroll'), 'res_model': 'run.payroll.wizard', 'res_id': self.id, 'view_mode': 'form', 'target': 'current', 'context': {'form_view_ref': 'fusion_payroll.run_payroll_wizard_view_form'}, } def action_back_to_entry(self): """Go back to the entry page.""" self.ensure_one() self.state = 'entry' return { 'type': 'ir.actions.act_window', 'name': _('Run Payroll'), 'res_model': 'run.payroll.wizard', 'res_id': self.id, 'view_mode': 'form', 'target': 'current', 'context': {'form_view_ref': 'fusion_payroll.run_payroll_wizard_view_form'}, } def action_save_for_later(self): """Save the payroll entries for later without processing.""" self.ensure_one() # Update pay period status to in_progress if using periods if self.pay_period_id: self.pay_period_id.write({'state': 'in_progress'}) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Payroll Saved'), 'message': _('Payroll entries have been saved. You can continue later.'), 'type': 'success', 'next': {'type': 'ir.actions.act_window_close'}, }, } def action_submit_payroll(self): """Submit the payroll - create payslips and journal entries.""" self.ensure_one() return self.action_generate_payslips() def action_generate_payslips(self): """Generate payslips for all entries with hours.""" self.ensure_one() # Get entries with hours entries_with_pay = self.entry_ids.filtered( lambda e: e.regular_hours > 0 or e.stat_holiday_hours > 0 or e.stat_pay_avg_daily_wage > 0 ) if not entries_with_pay: raise UserError(_("No employees have hours entered. Please enter hours for at least one employee.")) # Create payslip batch batch_name = f"Payroll {self.date_start.strftime('%Y-%m-%d')} to {self.date_end.strftime('%Y-%m-%d')}" payslip_run = self.env['hr.payslip.run'].create({ 'name': batch_name, 'date_start': self.date_start, 'date_end': self.date_end, 'company_id': self.company_id.id, }) # Generate payslips for each entry for entry in entries_with_pay: # Generate payslip name payslip_name = f"Payslip - {entry.employee_id.name} - {self.date_start.strftime('%Y/%m')}" # Create payslip payslip_vals = { 'name': payslip_name, 'employee_id': entry.employee_id.id, 'date_from': self.date_start, 'date_to': self.date_end, 'payslip_run_id': payslip_run.id, 'company_id': self.company_id.id, } # Add contract_id if employee has one if hasattr(entry.employee_id, 'contract_id') and entry.employee_id.contract_id: payslip_vals['contract_id'] = entry.employee_id.contract_id.id # Create the payslip (this was incorrectly indented before) payslip = self.env['hr.payslip'].create(payslip_vals) # Add inputs from Fusion Payroll entry self._add_fusion_payroll_inputs(payslip, entry) # Compute sheet payslip.compute_sheet() # Override computed values with Fusion Payroll calculations self._apply_fusion_payroll_values(payslip, entry) self.payslip_run_id = payslip_run # Update pay period status if using periods if self.pay_period_id: self.pay_period_id.write({ 'state': 'in_progress', 'payslip_run_id': payslip_run.id, }) # Create cheques for employees with payment method = 'cheque' cheques_created = self._create_cheques_for_batch(payslip_run) # If cheques were created, show print option if cheques_created: return self._action_payroll_complete_with_cheques(payslip_run, cheques_created) return self.action_view_payslips() def _add_fusion_payroll_inputs(self, payslip, entry): """Add salary inputs from Fusion Payroll entry to payslip.""" # Find or create input types InputType = self.env['hr.payslip.input.type'] Input = self.env['hr.payslip.input'] # Regular Hours input if entry.regular_hours > 0: hours_type = InputType.search([('code', '=', 'HOURS')], limit=1) if not hours_type: hours_type = InputType.search([('code', '=', 'WORKED_HOURS')], limit=1) if not hours_type: hours_type = InputType.create({ 'name': 'Worked Hours', 'code': 'HOURS', }) Input.create({ 'payslip_id': payslip.id, 'input_type_id': hours_type.id, 'amount': entry.regular_hours, }) # Regular Pay (calculated amount) if entry.regular_pay > 0: reg_pay_type = InputType.search([('code', '=', 'REGPAY')], limit=1) if not reg_pay_type: reg_pay_type = InputType.create({ 'name': 'Regular Pay', 'code': 'REGPAY', }) Input.create({ 'payslip_id': payslip.id, 'input_type_id': reg_pay_type.id, 'amount': entry.regular_pay, }) # Vacation Pay if entry.vacation_pay > 0: vac_type = InputType.search([('code', '=', 'VAC')], limit=1) if not vac_type: vac_type = InputType.create({ 'name': 'Vacation Pay', 'code': 'VAC', }) Input.create({ 'payslip_id': payslip.id, 'input_type_id': vac_type.id, 'amount': entry.vacation_pay, }) # Stat Holiday Pay if entry.stat_holiday_pay > 0: stat_type = InputType.search([('code', '=', 'STAT')], limit=1) if not stat_type: stat_type = InputType.create({ 'name': 'Stat Holiday Pay', 'code': 'STAT', }) Input.create({ 'payslip_id': payslip.id, 'input_type_id': stat_type.id, 'amount': entry.stat_holiday_pay, }) # Stat Pay (Average Daily Wage) if entry.stat_pay_avg_daily_wage > 0: stat_avg_type = InputType.search([('code', '=', 'STATAVG')], limit=1) if not stat_avg_type: stat_avg_type = InputType.create({ 'name': 'Stat Pay - Avg Daily Wage', 'code': 'STATAVG', }) Input.create({ 'payslip_id': payslip.id, 'input_type_id': stat_avg_type.id, 'amount': entry.stat_pay_avg_daily_wage, }) def _apply_fusion_payroll_values(self, payslip, entry): """Apply Fusion Payroll calculated values to payslip lines.""" # Get or create salary rule categories Category = self.env['hr.salary.rule.category'] # Find categories gross_cat = Category.search([('code', '=', 'GROSS')], limit=1) ded_cat = Category.search([('code', '=', 'DED')], limit=1) net_cat = Category.search([('code', '=', 'NET')], limit=1) # Update or create payslip lines with Fusion Payroll values # This directly sets the computed amounts # Update basic wage on payslip payslip.write({ 'basic_wage': entry.gross_pay, 'net_wage': entry.net_pay, }) # Update payslip lines for line in payslip.line_ids: if line.code == 'BASIC' or line.code == 'GROSS': line.write({'amount': entry.gross_pay, 'total': entry.gross_pay}) elif line.code == 'NET': line.write({'amount': entry.net_pay, 'total': entry.net_pay}) # Create additional lines if they don't exist self._ensure_payslip_lines(payslip, entry) def _ensure_payslip_lines(self, payslip, entry): """Ensure all Fusion Payroll calculated values have corresponding payslip lines.""" Line = self.env['hr.payslip.line'] Rule = self.env['hr.salary.rule'] Category = self.env['hr.salary.rule.category'] # Get categories gross_cat = Category.search([('code', '=', 'GROSS')], limit=1) ded_cat = Category.search([('code', '=', 'DED')], limit=1) alw_cat = Category.search([('code', '=', 'ALW')], limit=1) # Helper to find or create rule def get_or_create_rule(code, name, category, sequence=100): rule = Rule.search([('code', '=', code)], limit=1) if not rule and category: struct = payslip.struct_id or self.env['hr.payroll.structure'].search([], limit=1) rule = Rule.create({ 'name': name, 'code': code, 'category_id': category.id, 'sequence': sequence, 'struct_id': struct.id if struct else False, 'amount_select': 'fix', 'amount_fix': 0, }) return rule # Check and add lines existing_codes = payslip.line_ids.mapped('code') # Regular Pay line if 'REGPAY' not in existing_codes and entry.regular_pay > 0: rule = get_or_create_rule('REGPAY', 'Regular Pay', gross_cat, 10) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Regular Pay', 'code': 'REGPAY', 'category_id': gross_cat.id if gross_cat else False, 'sequence': 10, 'quantity': entry.regular_hours, 'rate': entry.hourly_rate, 'amount': entry.regular_pay, 'total': entry.regular_pay, }) # Vacation Pay line if 'VAC' not in existing_codes and entry.vacation_pay > 0: rule = get_or_create_rule('VAC', 'Vacation Pay', alw_cat or gross_cat, 20) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Vacation Pay', 'code': 'VAC', 'category_id': (alw_cat or gross_cat).id if (alw_cat or gross_cat) else False, 'sequence': 20, 'quantity': 1, 'rate': entry.vacation_pay_percent, 'amount': entry.vacation_pay, 'total': entry.vacation_pay, }) # Stat Holiday Pay line if 'STAT' not in existing_codes and entry.stat_holiday_pay > 0: rule = get_or_create_rule('STAT', 'Stat Holiday Pay', alw_cat or gross_cat, 25) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Stat Holiday Pay', 'code': 'STAT', 'category_id': (alw_cat or gross_cat).id if (alw_cat or gross_cat) else False, 'sequence': 25, 'quantity': entry.stat_holiday_hours, 'rate': entry.hourly_rate, 'amount': entry.stat_holiday_pay, 'total': entry.stat_holiday_pay, }) # Employee Tax lines (negative deductions) if 'FIT' not in existing_codes and 'INCOMETAX' not in existing_codes and entry.income_tax > 0: rule = get_or_create_rule('FIT', 'Federal Income Tax', ded_cat, 100) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Income Tax', 'code': 'FIT', 'category_id': ded_cat.id if ded_cat else False, 'sequence': 100, 'quantity': 1, 'rate': 100, 'amount': -entry.income_tax, 'total': -entry.income_tax, }) if 'EI' not in existing_codes and 'EI_EMP' not in existing_codes and entry.employment_insurance > 0: rule = get_or_create_rule('EI_EMP', 'Employment Insurance', ded_cat, 110) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Employment Insurance', 'code': 'EI_EMP', 'category_id': ded_cat.id if ded_cat else False, 'sequence': 110, 'quantity': 1, 'rate': 100, 'amount': -entry.employment_insurance, 'total': -entry.employment_insurance, }) if 'CPP' not in existing_codes and 'CPP_EMP' not in existing_codes and entry.cpp > 0: rule = get_or_create_rule('CPP_EMP', 'Canada Pension Plan', ded_cat, 120) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Canada Pension Plan', 'code': 'CPP_EMP', 'category_id': ded_cat.id if ded_cat else False, 'sequence': 120, 'quantity': 1, 'rate': 100, 'amount': -entry.cpp, 'total': -entry.cpp, }) if 'CPP2' not in existing_codes and 'CPP2_EMP' not in existing_codes and entry.cpp2 > 0: rule = get_or_create_rule('CPP2_EMP', 'Second CPP', ded_cat, 125) if rule: Line.create({ 'slip_id': payslip.id, 'salary_rule_id': rule.id, 'name': 'Second Canada Pension Plan', 'code': 'CPP2_EMP', 'category_id': ded_cat.id if ded_cat else False, 'sequence': 125, 'quantity': 1, 'rate': 100, 'amount': -entry.cpp2, 'total': -entry.cpp2, }) def _create_cheques_for_batch(self, payslip_run): """Create cheque records for employees paid by cheque.""" cheques = self.env['payroll.cheque'] for payslip in payslip_run.slip_ids: # Check if employee's payment method is cheque if hasattr(payslip.employee_id, 'payment_method') and \ payslip.employee_id.payment_method == 'cheque': cheque = self.env['payroll.cheque'].create_from_payslip(payslip) if cheque: cheques |= cheque return cheques def _action_payroll_complete_with_cheques(self, payslip_run, cheques): """Show completion wizard with option to print cheques.""" return { 'type': 'ir.actions.act_window', 'name': _('Payroll Complete'), 'res_model': 'payroll.cheque.print.wizard', 'view_mode': 'form', 'target': 'new', 'context': { 'default_payslip_run_id': payslip_run.id, 'default_cheque_ids': [(6, 0, cheques.ids)], 'default_cheque_count': len(cheques), }, } def action_print_cheques(self): """Print all cheques for the current batch.""" self.ensure_one() if not self.payslip_run_id: raise UserError(_("No payslips have been generated yet.")) cheques = self.env['payroll.cheque'].search([ ('payslip_run_id', '=', self.payslip_run_id.id), ]) if not cheques: raise UserError(_("No cheques found for this payroll batch.")) # Assign numbers and mark as printed for cheque in cheques: cheque.action_assign_number() return self.env.ref('fusion_payroll.action_report_payroll_cheque').report_action(cheques) def action_view_payslips(self): """View generated payslips.""" self.ensure_one() if not self.payslip_run_id: raise UserError(_("No payslips have been generated yet.")) return { 'type': 'ir.actions.act_window', 'name': _('Payslip Batch'), 'res_model': 'hr.payslip.run', 'res_id': self.payslip_run_id.id, 'view_mode': 'form', 'target': 'current', } def action_confirm_payslips(self): """Confirm all generated payslips.""" self.ensure_one() if not self.payslip_run_id: raise UserError(_("No payslips have been generated yet.")) # Confirm all payslips in the batch for payslip in self.payslip_run_id.slip_ids: if payslip.state == 'draft': payslip.action_payslip_done() # Update pay period status if self.pay_period_id: self.pay_period_id.write({'state': 'paid'}) return self.action_view_payslips() @api.model def action_open_run_payroll(self): """Open the Run Payroll wizard with pre-loaded data.""" # Check/create pay period settings settings = self.env['payroll.pay.period.settings'].get_or_create_settings() # Auto-generate periods for past 6 months and future 6 months self.env['payroll.pay.period'].auto_generate_periods_if_needed( company_id=self.env.company.id, schedule_type=settings.schedule_type, ) # Find the current pay period (today falls within it) today = fields.Date.context_today(self) current_period = self.env['payroll.pay.period'].search([ ('company_id', '=', self.env.company.id), ('schedule_type', '=', settings.schedule_type), ('date_start', '<=', today), ('date_end', '>=', today), ], limit=1) # If no current period, find the next upcoming one if not current_period: current_period = self.env['payroll.pay.period'].search([ ('company_id', '=', self.env.company.id), ('schedule_type', '=', settings.schedule_type), ('date_start', '>', today), ], order='date_start asc', limit=1) # Create wizard with default period wizard_vals = { 'company_id': self.env.company.id, 'pay_schedule': settings.schedule_type, } if current_period: wizard_vals['pay_period_id'] = current_period.id wizard_vals['date_start'] = current_period.date_start wizard_vals['date_end'] = current_period.date_end wizard_vals['pay_date'] = current_period.pay_date wizard = self.create(wizard_vals) wizard._load_employees() schedule_labels = { 'weekly': 'Weekly', 'biweekly': 'Bi-Weekly', 'semi_monthly': 'Semi-Monthly', 'monthly': 'Monthly', } schedule = schedule_labels.get(settings.schedule_type, 'Bi-Weekly') return { 'type': 'ir.actions.act_window', 'name': _('Run Payroll: %s') % schedule, 'res_model': 'run.payroll.wizard', 'res_id': wizard.id, 'view_mode': 'form', 'target': 'current', 'context': {'form_view_ref': 'fusion_payroll.run_payroll_wizard_view_form'}, }