Files
tra_backend/hr_gratuity_settlement/models/hr_gratuity.py
2020-07-21 10:09:46 +00:00

233 lines
14 KiB
Python

# -*- coding: utf-8 -*-
from datetime import date
from odoo import fields, models, api, _
from odoo.exceptions import Warning, UserError
class EmployeeGratuity(models.Model):
_name = 'hr.gratuity'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = "Employee Gratuity"
state = fields.Selection([
('draft', 'Draft'),
('submit', 'Submitted'),
('approve', 'Approved'),
('cancel', 'Cancelled')],
default='draft', track_visibility='onchange')
name = fields.Char(string='Reference', required=True, copy=False,
readonly=True,
default=lambda self: _('New'))
employee_id = fields.Many2one('hr.employee', string='Employee',
required=True, help="Employee")
employee_contract_type = fields.Selection([
('limited', 'Limited'),
('unlimited', 'Unlimited')], string='Contract Type', readonly=True,
store=True, help="Choose the contract type."
"if contract type is limited then during gratuity settlement if you have not specify the end date for contract, gratuity configration of limited type will be taken or"
"if contract type is Unlimited then during gratuity settlement if you have specify the end date for contract, gratuity configration of limited type will be taken.")
employee_joining_date = fields.Date(string='Joining Date', readonly=True,
store=True, help="Employee joining date")
wage_type = fields.Selection([('monthly', 'Monthly Fixed Wage'), ('hourly', 'Hourly Wage')],
help="Select the wage type monthly or hourly")
total_working_years = fields.Float(string='Total Years Worked', readonly=True, store=True,
help="Total working years")
employee_probation_years = fields.Float(string='Leaves Taken(Years)', readonly=True, store=True,
help="Employee probation years")
employee_gratuity_years = fields.Float(string='Gratuity Calculation Years',
readonly=True, store=True, help="Employee gratuity years")
employee_basic_salary = fields.Float(string='Basic Salary',
readonly=True,
help="Employee's basic salary.")
employee_gratuity_duration = fields.Many2one('gratuity.configuration',
readonly=True,
string='Configuration Line')
employee_gratuity_configuration = fields.Many2one('hr.gratuity.accounting.configuration',
readonly=True,
string='Gratuity Configuration')
employee_gratuity_amount = fields.Float(string='Gratuity Payment', readonly=True, store=True,
help="Gratuity amount for the employee. \it is calculated If the wage type is hourly then gratuity payment is calculated as employee basic salary * Employee Daily Wage Days * gratuity configration rule percentage * gratuity calculation years.orIf the wage type is monthly then gratuity payment is calculated as employee basic salary * (Working Days/Employee Daily Wage Days) * gratuity configration rule percentage * gratuity calculation years.")
hr_gratuity_credit_account = fields.Many2one('account.account', help="Gratuity credit account")
hr_gratuity_debit_account = fields.Many2one('account.account', help="Gratuity debit account")
hr_gratuity_journal = fields.Many2one('account.journal', help="Gratuity journal")
company_id = fields.Many2one('res.company', 'Company', required=True,
default=lambda self: self.env.company, help="Company")
currency_id = fields.Many2one(related="company_id.currency_id",
string="Currency", readonly=True, help="Currency")
@api.model
def create(self, vals):
""" assigning the sequence for the record """
vals['name'] = self.env['ir.sequence'].next_by_code('hr.gratuity')
return super(EmployeeGratuity, self).create(vals)
@api.depends('employee_id')
@api.onchange('employee_id')
def _onchange_employee_id(self):
""" calculating the gratuity pay based on the contract and gratuity
configurations """
if self.employee_id.id:
current_date = date.today()
probation_ids = self.env['hr.training'].search([('employee_id', '=', self.employee_id.id)])
contract_ids = self.env['hr.contract'].search([('employee_id', '=', self.employee_id.id)])
contract_sorted = contract_ids.sorted(lambda line: line.date_start)
if not contract_sorted:
raise Warning(_('No contracts found for the selected employee...!\n'
'Employee must have at least one contract to compute gratuity settelement.'))
self.employee_joining_date = joining_date = contract_sorted[0].date_start
employee_probation_days = 0
# find total probation days
for probation in probation_ids:
start_date = probation.start_date
end_date = probation.end_date
employee_probation_days += (end_date - start_date).days
# get running contract
hr_contract_id = self.env['hr.contract'].search(
[('employee_id', '=', self.employee_id.id), ('state', '=', 'open')])
if len(hr_contract_id) > 1 or not hr_contract_id:
raise Warning(_('Selected employee have multiple or no running contracts!'))
self.wage_type = hr_contract_id.wage_type
if self.wage_type == 'hourly':
self.employee_basic_salary = hr_contract_id.hourly_wage
else:
self.employee_basic_salary = hr_contract_id.wage
if hr_contract_id.date_end:
self.employee_contract_type = 'limited'
employee_working_days = (hr_contract_id.date_end - joining_date).days
self.total_working_years = employee_working_days / 365
self.employee_probation_years = employee_probation_days / 365
employee_gratuity_years = (employee_working_days - employee_probation_days) / 365
self.employee_gratuity_years = employee_gratuity_years
else:
self.employee_contract_type = 'unlimited'
employee_working_days = (current_date - joining_date).days
self.total_working_years = employee_working_days / 365
self.employee_probation_years = employee_probation_days / 365
employee_gratuity_years = (employee_working_days - employee_probation_days) / 365
self.employee_gratuity_years = round(employee_gratuity_years, 2)
gratuity_duration_id = False
hr_accounting_configuration_id = self.env[
'hr.gratuity.accounting.configuration'].search(
[('active', '=', True), ('config_contract_type', '=', self.employee_contract_type),
'|', ('gratuity_end_date', '>=', current_date), ('gratuity_end_date', '=', False),
'|', ('gratuity_start_date', '<=', current_date), ('gratuity_start_date', '=', False)])
if len(hr_accounting_configuration_id) > 1:
raise UserError(_(
"There is a date conflict in Gratuity accounting configuration. "
"Please remove the conflict and try again!"))
elif not hr_accounting_configuration_id:
raise UserError(
_('No gratuity accounting configuration found '
'or please set proper start date and end date for gratuity configuration!'))
# find configuration ids related to the gratuity accounting configuration
self.employee_gratuity_configuration = hr_accounting_configuration_id.id
conf_ids = hr_accounting_configuration_id.gratuity_configuration_table.mapped('id')
hr_duration_config_ids = self.env['gratuity.configuration'].browse(conf_ids)
for duration in hr_duration_config_ids:
if duration.from_year and duration.to_year and duration.from_year <= self.total_working_years <= duration.to_year:
gratuity_duration_id = duration
break
elif duration.from_year and not duration.to_year and duration.from_year <= self.total_working_years:
gratuity_duration_id = duration
break
elif duration.to_year and not duration.from_year and self.total_working_years <= duration.to_year:
gratuity_duration_id = duration
break
if gratuity_duration_id:
self.employee_gratuity_duration = gratuity_duration_id.id
else:
raise Warning(_('No suitable gratuity durations found !'))
# show warning when the employee's working years is less than
# one year or no running employee found.
if self.total_working_years < 1 and self.employee_id.id:
raise Warning(_('Selected Employee is not eligible for Gratuity Settlement'))
self.hr_gratuity_journal = hr_accounting_configuration_id.gratuity_journal.id
self.hr_gratuity_credit_account = hr_accounting_configuration_id.gratuity_credit_account.id
self.hr_gratuity_debit_account = hr_accounting_configuration_id.gratuity_debit_account.id
if self.employee_gratuity_duration and self.wage_type == 'hourly':
if self.employee_gratuity_duration.employee_working_days != 0:
if self.employee_id.resource_calendar_id and self.employee_id.resource_calendar_id.hours_per_day:
daily_wage = self.employee_basic_salary * self.employee_id.resource_calendar_id.hours_per_day
else:
daily_wage = self.employee_basic_salary * 8
working_days_salary = daily_wage * self.employee_gratuity_duration.employee_working_days
gratuity_pay_per_year = working_days_salary * self.employee_gratuity_duration.percentage
employee_gratuity_amount = gratuity_pay_per_year * self.employee_gratuity_years
self.employee_gratuity_amount = round(employee_gratuity_amount, 2)
else:
raise Warning(_("Employee working days is not configured in "
"the gratuity configuration..!"))
elif self.employee_gratuity_duration and self.wage_type == 'monthly':
if self.employee_gratuity_duration.employee_daily_wage_days != 0:
daily_wage = self.employee_basic_salary / self.employee_gratuity_duration.employee_daily_wage_days
working_days_salary = daily_wage * self.employee_gratuity_duration.employee_working_days
gratuity_pay_per_year = working_days_salary * self.employee_gratuity_duration.percentage
employee_gratuity_amount = gratuity_pay_per_year * self.employee_gratuity_years
self.employee_gratuity_amount = round(employee_gratuity_amount, 2)
else:
raise Warning(_("Employee wage days is not configured in "
"the gratuity configuration..!"))
# Changing state to submit
def submit_request(self):
self.write({'state': 'submit'})
# Canceling the gratuity request
def cancel_request(self):
self.write({'state': 'cancel'})
# Set the canceled request to draft
def set_to_draft(self):
self.write({'state': 'draft'})
# function for creating the account move with gratuity amount and
# account credentials
def approved_request(self):
for hr_gratuity_id in self:
debit_vals = {
'name': hr_gratuity_id.employee_id.name,
'account_id': hr_gratuity_id.hr_gratuity_debit_account.id,
'partner_id': hr_gratuity_id.employee_id.address_home_id.id or False,
'journal_id': hr_gratuity_id.hr_gratuity_journal.id,
'date': date.today(),
'debit': hr_gratuity_id.employee_gratuity_amount > 0.0 and hr_gratuity_id.employee_gratuity_amount or 0.0,
'credit': hr_gratuity_id.employee_gratuity_amount < 0.0 and -hr_gratuity_id.employee_gratuity_amount or 0.0,
}
credit_vals = {
'name': hr_gratuity_id.employee_id.name,
'account_id': hr_gratuity_id.hr_gratuity_credit_account.id,
'partner_id': hr_gratuity_id.employee_id.address_home_id.id or False,
'journal_id': hr_gratuity_id.hr_gratuity_journal.id,
'date': date.today(),
'debit': hr_gratuity_id.employee_gratuity_amount < 0.0 and -hr_gratuity_id.employee_gratuity_amount or 0.0,
'credit': hr_gratuity_id.employee_gratuity_amount > 0.0 and hr_gratuity_id.employee_gratuity_amount or 0.0,
}
vals = {
'name': hr_gratuity_id.name + " - " + 'Gratuity for' + ' ' + hr_gratuity_id.employee_id.name,
'narration': hr_gratuity_id.employee_id.name,
'ref': hr_gratuity_id.name,
'partner_id': hr_gratuity_id.employee_id.address_home_id.id or False,
'journal_id': hr_gratuity_id.hr_gratuity_journal.id,
'date': date.today(),
'line_ids': [(0, 0, debit_vals), (0, 0, credit_vals)],
}
move = hr_gratuity_id.env['account.move'].create(vals)
move.post()
self.write({'state': 'approve'})
class EmployeeContractWage(models.Model):
_inherit = 'hr.contract'
# structure_type_id = fields.Many2one('hr.payroll.structure.type', string="Salary Structure Type")
company_country_id = fields.Many2one('res.country', string="Company country", related='company_id.country_id',
readonly=True)
wage_type = fields.Selection([('monthly', 'Monthly Fixed Wage'), ('hourly', 'Hourly Wage')])
hourly_wage = fields.Monetary('Hourly Wage', digits=(16, 2), default=0, required=True, tracking=True,
help="Employee's hourly gross wage.")