odex30_standard/employee_requests/models/employee_overtime_request.py

535 lines
27 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import division
from datetime import datetime
from odoo import models, fields, api, _
from odoo.exceptions import UserError
import calendar
# import logging
class employee_overtime_request(models.Model):
_name = 'employee.overtime.request'
_description = 'Employee Overtime Request'
_rec_name = 'request_date'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'request_date DESC'
request_date = fields.Date(default=lambda self: fields.Date.today())
reason = fields.Text()
date_to = fields.Date()
date_from = fields.Date()
transfer_type = fields.Selection([('accounting', 'Accounting'), ('payroll', 'Payroll')], default='payroll')
overtime_plase = fields.Selection([('inside', 'Inside'), ('outside', 'Outside')])
state = fields.Selection(
[('draft', _('Draft')),
('submit', _('Waiting Direct Manager')),
('direct_manager', _('Waiting Department Manager')),
('financial_manager', _('Wait HR Department')),
('hr_aaproval', _('Wait Approval')),
('executive_office', _('Wait Transfer')),
('validated', _('Transferred')),
('refused', _('Refused'))], default="draft", tracking=True)
# Relation fields
account_id = fields.Many2one(comodel_name='account.account', string='Account')
journal_id = fields.Many2one(comodel_name='account.journal', string='Payment Method')
benefits_discounts = fields.Many2one(comodel_name='hr.salary.rule', string='Benefits/Discounts',
domain=[('rules_type', '=', 'overtime')])
line_ids_over_time = fields.One2many(comodel_name='line.ids.over.time', inverse_name='employee_over_time_id',
tracking=True)
department_id = fields.Many2one('hr.department')
employee_id = fields.Many2one('hr.employee', 'Responsible', default=lambda item: item.get_user_id(),
domain=[('state', '=', 'open')])
manager_id = fields.Many2one('hr.employee', string='Direct Manager', related='employee_id.parent_id', store=True,
readonly=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
employee_no = fields.Char(related='employee_id.emp_no', readonly=True,string='Employee Number', store=True)
exception = fields.Boolean(string="Exception Hours", default=False,
help='Exceeding The Limit Of Overtime Hours Per Month')
company_id = fields.Many2one('res.company',string="Company", default=lambda self: self.env.company)
is_branch = fields.Many2one(related='department_id.branch_name', store=True, readonly=True)
def get_user_id(self):
employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
if employee_id:
return employee_id.id
else:
return False
@api.onchange('employee_id')
def get_department_id(self):
if self.employee_id:
self.department_id = self.employee_id.department_id.id
@api.onchange('request_date','date_from')
def chick_date_request(self):
for rec in self:
days_after = rec.employee_id.contract_id.working_hours.request_after_day
if days_after > 0 and rec.date_from:
rec.date_to=False
date_from = datetime.strptime(str(rec.date_from), "%Y-%m-%d").date()
request_date = datetime.strptime(str(rec.request_date), "%Y-%m-%d").date()
last_day_date = date_from.replace(day=calendar.monthrange(date_from.year, date_from.month)[1])
diff_days = (request_date - last_day_date).days
if diff_days > days_after:
raise UserError(_(
'Sorry, Cannot Be Request after %s Days From The End Of The Overtime Month') % days_after)
#get account and journal from setting
@api.onchange('transfer_type','employee_id')
def get_account_ids(self):
if self.transfer_type == 'accounting':
self.journal_id = self.employee_id.contract_id.working_hours.journal_overtime_id
self.account_id = self.employee_id.contract_id.working_hours.account_overtime_id
@api.onchange('transfer_type', 'account_id', 'journal_id', 'line_ids_over_time')
def onchange_transfer_type(self):
if self.transfer_type == 'payroll':
self.account_id = False
self.journal_id = False
for line in self.line_ids_over_time:
line.account_id = False
line.journal_id = False
if self.transfer_type == 'accounting':
for line in self.line_ids_over_time:
if self.state == 'hr_aaproval':
if not line.account_id:
line.account_id = self.account_id
if not line.journal_id:
line.journal_id = self.journal_id
#else:
# line.account_id = False
# line.journal_id = False
@api.onchange('account_id')
def onchange_account_id(self):
for line in self.line_ids_over_time:
line.account_id = self.account_id
@api.onchange('journal_id')
def onchange_journal_id(self):
for line in self.line_ids_over_time:
line.journal_id = self.journal_id
def re_draft(self):
# when redraft cancel the created account move
if self.transfer_type == 'payroll':
for record in self.line_ids_over_time:
record.advantage_id.draft()
record.advantage_id.unlink()
self.state = 'draft'
if self.transfer_type == 'accounting':
if self.line_ids_over_time[0].move_id:
move_id_not_draft = False
for line in self.line_ids_over_time:
if line.move_id.state == 'posted':
move_id_not_draft_name = line.move_id.name
move_id_not_draft = True
if move_id_not_draft:
raise UserError(_(
'You can not cancel account move "%s" in state not draft') % move_id_not_draft_name)
else:
for record in self.line_ids_over_time:
# record.move_id.write({'state': 'canceled'})
record.move_id.unlink()
record.write({
'move_id': False
})
#record.account_id = False
#record.journal_id = False
self.write({'state': 'draft'})
#self.account_id = False
#self.journal_id = False
else:
self.write({
'state': 'draft',
#'account_id': False,
#'journal_id': False
})
for record in self.line_ids_over_time:
record.write({
'move_id': False,
#'account_id': False,
#'journal_id': False
})
def submit(self):
if not self.line_ids_over_time:
raise UserError(_('Sorry, Can Not Request without The Employees'))
self.line_ids_over_time.get_max_remain_hours()
self.chick_not_mission()
self.state = "submit"
def direct_manager(self):
for rec in self:
rec.line_ids_over_time.chick_price_hour()
rec.chick_not_mission()
manager = rec.sudo().employee_id.parent_id
hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
if manager:
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
rec.write({'state': 'direct_manager'})
else:
raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only OR HR Manager!")%(rec.employee_id.parent_id.name))
else:
rec.write({'state': 'direct_manager'})
def financial_manager(self):
for rec in self:
rec.chick_not_mission()
manager = rec.sudo().employee_id.coach_id
hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
if manager:
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
rec.write({'state': 'financial_manager'})
else:
raise UserError(_("Sorry, The Approval For The Department Manager '%s' Only OR HR Manager!")%(rec.employee_id.coach_id.name))
else:
rec.write({'state': 'financial_manager'})
def hr_aaproval(self):
self.chick_not_mission()
if self.exception == True:
self.state = "hr_aaproval"
else:
self.state = "executive_office"
def executive_office(self):
self.chick_not_mission()
self.state = "executive_office"
def validated(self):
if self.transfer_type == 'accounting':
for item in self:
for record in item.line_ids_over_time:
emp_type = record.employee_id.employee_type_id
account_debit_id = record.employee_id.contract_id.working_hours.get_debit_overtim_account_id(emp_type)
journal_id = record.employee_id.contract_id.working_hours.journal_overtime_id
analytic_account_id = record.employee_id.contract_id.working_hours.analytic_account_id
if not analytic_account_id:
analytic_account_id = record.employee_id.department_id.analytic_account_id
if not journal_id:
raise UserError(_('You Must Enter The Journal Name Overtim Setting.'))
if not account_debit_id:
raise UserError(_('Employee %s, has no Overtime Account Setting Base On Employee Type.'
) % record.employee_id.name)
debit_line_vals = {
'name': record.employee_id.name,
'debit': record.price_hour,
'account_id': account_debit_id.id,
'partner_id': record.employee_id.user_id.partner_id.id,
'analytic_account_id': analytic_account_id.id,
}
credit_line_vals = {
'name': record.employee_id.name,
'credit': record.price_hour,
'account_id': journal_id.default_account_id.id,
'partner_id': record.employee_id.user_id.partner_id.id
}
if not record.move_id:
move = record.env['account.move'].create({
'state': 'draft',
'journal_id': journal_id.id,
'date': item.request_date,
'ref': record.employee_id.name,
'line_ids': [(0, 0, debit_line_vals), (0, 0, credit_line_vals)],
'res_model': 'employee.overtime.request',
'res_id': self.id
})
record.account_id = account_debit_id.id
record.journal_id = journal_id.id
record.move_id = move.id
self.state = "validated"
if self.transfer_type == 'payroll':
# last_day_of_current_month = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])
# first_day_of_current_month = date.today().replace(day=1)
for item in self:
for record in item.line_ids_over_time:
if record.employee_id.contract_id:
advantage_arc = record.env['contract.advantage'].create({
'benefits_discounts': item.benefits_discounts.id,
'type': 'customize',
'date_from': item.date_from,
'date_to': item.date_to,
'amount': record.price_hour,
'over_time_id': True,
'employee_id': record.employee_id.id,
'contract_advantage_id': record.employee_id.contract_id.id,
'out_rule': True,
'state': 'confirm',
'comments': item.reason})
record.advantage_id = advantage_arc.id
else:
raise UserError(_('Employee "%s" has no contract Please create contract to add '
'line to advantages') % record.employee_id.name)
self.state = "validated"
def refused(self):
self.state = "refused"
#Refuse For The Direct Manager Only
def direct_manager_refused(self):
for rec in self:
manager = rec.sudo().employee_id.parent_id
hr_manager = rec.sudo().employee_id.user_id.company_id.hr_manager_id
if manager:
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
rec.refused()
else:
raise UserError(_("Sorry, The Refuse For The Direct Manager '%s' Only OR HR Manager!") % (manager.name))
else:
rec.refused()
#Refuse For The Department Manager Only
def dep_manager_refused(self):
for rec in self:
manager = rec.sudo().employee_id.coach_id
hr_manager = rec.sudo().employee_id.user_id.company_id.hr_manager_id
if manager:
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
rec.refused()
else:
raise UserError(_("Sorry, The Refuse For The Department Manager '%s' Only !")%(rec.employee_id.coach_id.name))
else:
rec.refused()
def unlink(self):
for i in self:
if i.state != 'draft':
raise UserError(_('You can not delete record in state not in draft'))
i.line_ids_over_time.unlink()
return super(employee_overtime_request, self).unlink()
@api.onchange('line_ids_over_time.employee_id', 'date_from', 'date_to')
def chick_not_mission(self):
for rec in self:
Module = self.env['ir.module.module'].sudo()
modules_mission = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_official_mission')])
if modules_mission:
if rec.date_to and rec.date_from:
if rec.date_to < rec.date_from:
raise UserError(_('Date Form Must Be Less than Date To'))
for line in rec.line_ids_over_time:
clause_1 = ['&', ('official_mission_id.date_from', '<=', rec.date_from),
('official_mission_id.date_to', '>=', rec.date_from)]
clause_2 = ['&', ('official_mission_id.date_from', '<=', rec.date_to),
('official_mission_id.date_to', '>=', rec.date_to)]
clause_3 = ['&', ('official_mission_id.date_from', '>=', rec.date_from),
('official_mission_id.date_to', '<=', rec.date_to)]
mission = self.env['hr.official.mission.employee'].search(
[('employee_id', '=', line.employee_id.id), ('official_mission_id.state', '!=', 'refused'),
('official_mission_id.mission_type.related_with_financial', '=', True),
#('official_mission_id.mission_type.work_state', '=', 'legation'),
'|', '|'] + clause_1 + clause_2 + clause_3)
if mission:
raise UserError(_('Sorry The Employee %s Actually Has Legation '
'Amount For this Period') % line.employee_id.name)
# TOOO DOOO
@api.onchange('overtime_plase', 'date_from', 'date_to', 'exception')
def chick_hours_calenders(self):
self.line_ids_over_time.chick_hours_calender()
class HrEmployeeOverTime(models.Model):
_name = 'line.ids.over.time'
_description = 'Overtime Line'
_rec_name = 'employee_id'
over_time_workdays_hours = fields.Float(string="workdays hours")
over_time_vacation_hours = fields.Float(string="Vacation days hours")
daily_hourly_rate = fields.Float(string='Daily Hourly Rate', compute='get_over_time_amount', store=True)
holiday_hourly_rate = fields.Float(string='Holiday Hourly Rate', compute='get_over_time_amount', store=True)
price_hour = fields.Float(string='Overtime Amount', compute='get_over_time_amount', store=True)
# Relational fields
account_id = fields.Many2one('account.account')
journal_id = fields.Many2one('account.journal', string='Payment Method', domain=[('type', 'in', ('bank', 'cash'))])
move_id = fields.Many2one('account.move')
employee_id = fields.Many2one('hr.employee', string='Employee', required=True)
employee_over_time_id = fields.Many2one('employee.overtime.request', string='Employee')
calculate_from_total = fields.Boolean(string="Calculate From Total")
transfer_type = fields.Selection(related='employee_over_time_id.transfer_type')
advantage_id = fields.Many2one(comodel_name='contract.advantage', string='Allowance Employee')
max_hours = fields.Float(compute='get_max_remain_hours', string="Max Hours", store=True)
remaining_hours = fields.Float(compute='get_max_remain_hours', string="Remaining Hours", store=True)
#exception = fields.Boolean(string="Exception", default=False)
exception = fields.Boolean(related='employee_over_time_id.exception', string="Exception Hours", store=True)
overtime_plase = fields.Selection(related='employee_over_time_id.overtime_plase', store=True,
string="Overtime Plase")
state = fields.Selection(related='employee_over_time_id.state', store=True, string="State")
@api.constrains('price_hour')
def chick_price_hour(self):
for rec in self:
if rec.employee_id:
if rec.price_hour < 0:
raise UserError(_('You can not Request Overtime The Employee %s The Overtime Amount Less Than Zero') % rec.employee_id.name)
if rec.price_hour == 0:
raise UserError(_('You can not Request Overtime The Employee %s The Overtime Amount Zero') % rec.employee_id.name)
@api.onchange('overtime_plase', 'over_time_workdays_hours', 'over_time_vacation_hours', 'employee_id', 'exception')
def chick_hours_calender(self):
for rec in self:
overtime_day_hour = 0
overtime_holi_hour = 0
# day_hour=0.0
# holiday_hour=0.0
if rec.employee_id:
if not rec.employee_id.first_hiring_date:
raise UserError(
_('You can not Request Overtime The Employee %s have Not First Hiring Date') % rec.employee_id.name)
if rec.overtime_plase == 'inside' and rec.employee_id:
attendance_transaction = self.env['hr.attendance.transaction'].search(
[('employee_id', '=', rec.employee_id.id),
('date', '>=', rec.employee_over_time_id.date_from),
('date', '<=', rec.employee_over_time_id.date_to)])
for tran in attendance_transaction:
if tran.additional_hours > 0 and not tran.public_holiday:
overtime_day_hour += tran.additional_hours
if tran.public_holiday:
overtime_holi_hour += tran.office_hours
if rec.over_time_workdays_hours > overtime_day_hour and not rec.exception:
raise UserError(
_('The Overtime Workdays Employee %s Exceeded The Attendance workdays '
'Hours') % rec.employee_id.name)
if rec.over_time_vacation_hours > overtime_holi_hour and not rec.exception:
raise UserError(
_('The Overtime Publice Holiday Employee %s Exceeded The Attendance Public '
'Holiday Hours') % rec.employee_id.name)
# dynamic domain on employee_id
# Select employee once in Over Time Line
@api.onchange('employee_id')
def get_emplyee_id_domain(self):
# Check if employee selected once
if self.employee_over_time_id.department_id:
# for dep in self.official_mission_id.department_id:
employee_id = self.env['hr.employee'].search(
[('department_id', '=', self.employee_over_time_id.department_id.id), ('state', '=', 'open')]).ids
if employee_id:
for line in self.employee_over_time_id.line_ids_over_time:
if line.employee_id:
if line.employee_id.id in employee_id:
employee_id.remove(line.employee_id.id)
return {'domain': {'employee_id': [('id', 'in', employee_id)]}}
else:
employee_id = self.env['hr.employee'].search([('state', '=', 'open')]).ids
if employee_id:
for line in self.employee_over_time_id.line_ids_over_time:
if line.employee_id:
if line.employee_id.id in employee_id:
employee_id.remove(line.employee_id.id)
return {'domain': {'employee_id': [('id', 'in', employee_id)]}}
'''@api.model
def default_get(self, fields):
res = super(HrEmployeeOverTime, self).default_get(fields)
if self._context.get('account_id') and self._context.get('journal_id'):
res['account_id'] = self._context.get('account_id')
res['journal_id'] = self._context.get('journal_id')
return res'''
@api.depends('employee_id.contract_id', 'over_time_workdays_hours', 'over_time_vacation_hours', 'employee_id',
'calculate_from_total')
def get_over_time_amount(self):
for line in self:
contract_id = line.employee_id.contract_id
if contract_id.working_hours:
wage_daily = contract_id.total_allowance + (
contract_id.salary * contract_id.working_hours.overtime_factor_daily)
# 1/2 Basic salary + total Salaey
wage_holiday = contract_id.total_allowance + (
contract_id.salary * contract_id.working_hours.overtime_factor_holiday)
# 1 Basic salary + total Salaey
total_hours = contract_id.working_hours.work_days * contract_id.working_hours.work_hour
# 240 per month
if total_hours != 0:
price_hour_daily = wage_daily / total_hours
price_hour_holiday = wage_holiday / total_hours
# if line.over_time_workdays_hours > 0 or line.over_time_vacation_hours > 0:
if line.employee_id:
line.daily_hourly_rate = price_hour_daily
o_t_a_d = price_hour_daily * line.over_time_workdays_hours
line.holiday_hourly_rate = price_hour_holiday
o_t_a_v = price_hour_holiday * line.over_time_vacation_hours
line.price_hour = o_t_a_d + o_t_a_v
emp_total_hours = line.over_time_workdays_hours + line.over_time_vacation_hours
# if emp_total_hours > contract_id.working_hours.max_overtime_hour:
# raise exceptions.UserError(_('The Number Of Overtime Hours For The Employee %s Is Greater
# Max Hours per Month')% line.employee_id.name)
else:
raise UserError(_('The Number Of Overtime Hours And Days is Missing'))
# else:
# line.daily_hourly_rate = 0
# line.holiday_hourly_rate = 0
#
# line.price_hour = 0
@api.depends('employee_id.contract_id', 'employee_id', 'employee_over_time_id.date_from',
'employee_over_time_id.date_to','over_time_workdays_hours','over_time_vacation_hours')
def get_max_remain_hours(self):
for line in self:
contract_id = line.employee_id.contract_id
if contract_id.working_hours:
line.max_hours = contract_id.working_hours.max_overtime_hour
if line.employee_over_time_id.date_to and line.employee_over_time_id.date_from:
month_current_from = datetime.strptime(str(line.employee_over_time_id.date_from),
'%Y-%m-%d').strftime('%m')
year_current_from = datetime.strptime(str(line.employee_over_time_id.date_from),
'%Y-%m-%d').strftime('%y')
month_current_to = datetime.strptime(str(line.employee_over_time_id.date_to),
'%Y-%m-%d').strftime('%m')
year_current_to = datetime.strptime(str(line.employee_over_time_id.date_to),
'%Y-%m-%d').strftime('%y')
if month_current_from != month_current_to or year_current_from != year_current_to:
raise UserError(
_('Sorry, The overtime period Must be During the same Month for the Year'))
overtime_ids = self.search(
[('employee_id', '=', line.employee_id.id), ('employee_over_time_id.state', '!=', 'refused')])
total_hours = 0.0
remaining_hours = line.max_hours
for rec in overtime_ids:
month_previous = datetime.strptime(str(rec.employee_over_time_id.date_from),
'%Y-%m-%d').strftime('%m')
year_previous = datetime.strptime(str(rec.employee_over_time_id.date_from),
'%Y-%m-%d').strftime('%y')
if month_current_from == month_previous and year_current_from == year_previous:
total_hours += rec.over_time_workdays_hours + rec.over_time_vacation_hours
remaining_hours = line.max_hours - total_hours
line.remaining_hours = remaining_hours
if remaining_hours < 0 and line.exception == False:
raise UserError(
_('The Number Of Overtime Hours For The Employee %s Is Greater Max Hours per '
'Month') % line.employee_id.name)
def unlink(self):
for i in self:
if i.state != 'draft':
raise UserError(_('You can not delete record in state not in draft'))
return super(HrEmployeeOverTime, self).unlink()