| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- # -*- coding: utf-8 -*-
- # Part of Odoo. See LICENSE file for full copyright and licensing details.
- from datetime import date, datetime
- from dateutil.relativedelta import relativedelta
- from odoo import api, fields, models, _
- from odoo.exceptions import UserError
- class HrEfficiencyCalculationWizard(models.TransientModel):
- _name = 'hr.efficiency.calculation.wizard'
- _description = 'HR Efficiency Calculation Wizard'
- start_month = fields.Char('Start Month', required=True, help="Format: YYYY-MM")
- end_month = fields.Char('End Month', required=True, help="Format: YYYY-MM")
- employee_ids = fields.Many2many('hr.employee', string='Employees', domain=[('employee_type', '=', 'employee')], help="Leave empty to calculate for all active employees")
- company_id = fields.Many2one('res.company', 'Company', required=True, default=lambda self: self.env.company)
-
- # Results
- result_message = fields.Text('Result', readonly=True)
- created_count = fields.Integer('Created Records', readonly=True)
- updated_count = fields.Integer('Updated Records', readonly=True)
- total_count = fields.Integer('Total Processed', readonly=True)
- @api.model
- def default_get(self, fields_list):
- res = super().default_get(fields_list)
- # Set default period: last 3 months and next 6 months
- current_date = date.today()
- res['start_month'] = (current_date - relativedelta(months=3)).strftime('%Y-%m')
- res['end_month'] = (current_date + relativedelta(months=6)).strftime('%Y-%m')
- return res
- def action_calculate_efficiency(self):
- """
- Calculate efficiency for the specified period and employees
- """
- # Validate input
- self._validate_input()
-
- # Get employees to process
- if self.employee_ids:
- employees = self.employee_ids
- else:
- employees = self.env['hr.employee'].search([
- ('active', '=', True),
- ('company_id', '=', self.company_id.id),
- ('employee_type', '=', 'employee')
- ])
-
- if not employees:
- raise UserError(_("No employees found to process."))
-
- # Calculate efficiency
- efficiency_model = self.env['hr.efficiency']
-
- # Generate list of months
- months = self._generate_month_list(self.start_month, self.end_month)
-
- created_records = []
-
- for employee in employees:
- for month in months:
- # Calculate efficiency data
- efficiency_data = efficiency_model._calculate_employee_efficiency(employee, month)
- efficiency_data['company_id'] = self.company_id.id
-
- # Check if there are changes compared to the latest record
- latest_record = efficiency_model.search([
- ('employee_id', '=', employee.id),
- ('month_year', '=', month),
- ('company_id', '=', self.company_id.id),
- ], order='calculation_date desc', limit=1)
-
- has_changes = False
- if latest_record:
- # Compare current data with latest record
- fields_to_compare = [
- 'available_hours', 'planned_hours', 'planned_billable_hours',
- 'planned_non_billable_hours', 'actual_billable_hours',
- 'actual_non_billable_hours'
- ]
-
- for field in fields_to_compare:
- if abs(efficiency_data[field] - latest_record[field]) > 0.01: # Tolerance for floating point
- has_changes = True
- break
- else:
- # No previous record exists, so this is a change
- has_changes = True
-
- # Only create new record if there are changes
- if has_changes:
- # Archive existing records for this employee and month
- existing_records = efficiency_model.search([
- ('employee_id', '=', employee.id),
- ('month_year', '=', month),
- ('company_id', '=', self.company_id.id),
- ])
- if existing_records:
- existing_records.write({'active': False})
-
- # Create new record
- new_record = efficiency_model.create(efficiency_data)
- created_records.append(new_record)
-
- # Update wizard with results
- self.write({
- 'created_count': len(created_records),
- 'updated_count': 0, # No longer updating existing records
- 'total_count': len(created_records),
- 'result_message': self._generate_result_message(created_records, employees, months),
- })
-
- return {
- 'type': 'ir.actions.act_window',
- 'res_model': 'hr.efficiency.calculation.wizard',
- 'res_id': self.id,
- 'view_mode': 'form',
- 'target': 'new',
- }
- def _validate_input(self):
- """
- Validate wizard input
- """
- # Validate month format
- try:
- datetime.strptime(self.start_month, '%Y-%m')
- datetime.strptime(self.end_month, '%Y-%m')
- except ValueError:
- raise UserError(_("Invalid month format. Please use YYYY-MM format (e.g., 2024-01)"))
-
- # Validate date range
- start_date = datetime.strptime(self.start_month, '%Y-%m')
- end_date = datetime.strptime(self.end_month, '%Y-%m')
-
- if start_date > end_date:
- raise UserError(_("Start month cannot be after end month."))
-
- # Limit range to reasonable period (e.g., 2 years)
- if (end_date - start_date).days > 730:
- raise UserError(_("Date range cannot exceed 2 years."))
- def _generate_month_list(self, start_month, end_month):
- """
- Generate list of months between start_month and end_month (inclusive)
- """
- months = []
- current = datetime.strptime(start_month, '%Y-%m')
- end = datetime.strptime(end_month, '%Y-%m')
-
- while current <= end:
- months.append(current.strftime('%Y-%m'))
- current = current + relativedelta(months=1)
-
- return months
- def _generate_result_message(self, created_records, employees, months):
- """
- Generate result message for the wizard
- """
- message = _("Efficiency calculation completed successfully!\n\n")
- message += _("Period: %s to %s\n") % (self.start_month, self.end_month)
- message += _("Employees processed: %d\n") % len(employees)
- message += _("Months processed: %d\n\n") % len(months)
- message += _("Results:\n")
- message += _("- New records created: %d\n") % len(created_records)
- message += _("- Total records processed: %d\n") % len(created_records)
- message += _("\nNote: New records are only created when there are changes. Old records are archived.")
-
- return message
- def action_view_results(self):
- """
- Open the efficiency records view
- """
- return {
- 'type': 'ir.actions.act_window',
- 'name': _('Efficiency Records'),
- 'res_model': 'hr.efficiency',
- 'view_mode': 'list,form',
- 'domain': [
- ('company_id', '=', self.company_id.id),
- ('month_year', '>=', self.start_month),
- ('month_year', '<=', self.end_month),
- ],
- 'context': {
- 'default_company_id': self.company_id.id,
- },
- }
|