@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHrms Project <https://www.openhrms.co.uk> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). |
||||
|
# Author: Aswani PC (<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# This program is free software: you can modify |
||||
|
# it under the terms of the GNU Affero General Public License (AGPL) as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
from . import models |
||||
|
from . import wizard |
@ -0,0 +1,49 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of Open Hrms Project <https://www.openhrms.co.uk> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). |
||||
|
# Author: Aswani PC (<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# This program is free software: you can modify |
||||
|
# it under the terms of the GNU Affero General Public License (AGPL) as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
{ |
||||
|
'name': "Open HRMS Vacation Management", |
||||
|
'version': '11.0.1.0.0', |
||||
|
'summary': """Manage Employee Vacation""", |
||||
|
'description': """HR Vacation management""", |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'website': 'https://www.openhrms.com', |
||||
|
'category': 'Generic Modules/Human Resources', |
||||
|
'depends': ['hr_leave_request_aliasing','project', 'hr_payroll', 'account'], |
||||
|
'data': [ |
||||
|
'security/hr_vacation_security.xml', |
||||
|
'security/ir.model.access.csv', |
||||
|
'data/hr_payslip_data.xml', |
||||
|
'views/hr_reminder.xml', |
||||
|
'data/hr_vacation_data.xml', |
||||
|
'wizard/reassign_task.xml', |
||||
|
'views/hr_employee_ticket.xml', |
||||
|
'views/hr_vacation.xml', |
||||
|
'views/hr_payslip.xml', |
||||
|
], |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="hr_leave_salary_register" model="hr.contribution.register"> |
||||
|
<field name="name">Leave Salary</field> |
||||
|
<field name="partner_id" eval="False"/> |
||||
|
</record> |
||||
|
<!-- Salary Rules --> |
||||
|
|
||||
|
<record id="hr_salary_rule_leave_salary_basic" model="hr.salary.rule"> |
||||
|
<field name="name">Leave Salary</field> |
||||
|
<field name="code">LS</field> |
||||
|
<field name="sequence" eval="90"/> |
||||
|
<field name="category_id" ref="hr_payroll.ALW"/> |
||||
|
<field name="register_id" ref="hr_leave_salary_register"/> |
||||
|
<field name="condition_select">none</field> |
||||
|
<field name="amount_select">code</field> |
||||
|
<field name="amount_python_compute">result = categories.BASIC</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="hr_salary_rule_leave_salary_gross" model="hr.salary.rule"> |
||||
|
<field name="name">Leave Salary</field> |
||||
|
<field name="code">LS</field> |
||||
|
<field name="sequence" eval="90"/> |
||||
|
<field name="category_id" ref="hr_payroll.ALW"/> |
||||
|
<field name="register_id" ref="hr_leave_salary_register"/> |
||||
|
<field name="condition_select">none</field> |
||||
|
<field name="amount_select">code</field> |
||||
|
<field name="amount_python_compute">result = categories.BASIC + categories.ALW</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,31 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="ir_cron_ticket_status_update" model="ir.cron"> |
||||
|
<field name="name">Flight ticket status update</field> |
||||
|
<field name="model_id" ref="model_hr_flight_ticket"/> |
||||
|
<field name="state">code</field> |
||||
|
<field name="code">model.run_update_ticket_status()</field> |
||||
|
<field name="interval_number">1</field> |
||||
|
<field name="interval_type">days</field> |
||||
|
<field name="numbercall">-1</field> |
||||
|
<field eval="False" name="doall" /> |
||||
|
</record> |
||||
|
|
||||
|
<record id="hr_email_leave_reminder" model="ir.cron"> |
||||
|
<field name="name">HR Leave Reminder</field> |
||||
|
<field name="model_id" ref="hr_holidays.model_hr_holidays"/> |
||||
|
<field name="state">code</field> |
||||
|
<field name="code">model.send_leave_reminder()</field> |
||||
|
<field name="interval_number">1</field> |
||||
|
<field name="interval_type">days</field> |
||||
|
<field name="numbercall">-1</field> |
||||
|
<field eval="False" name="doall" /> |
||||
|
</record> |
||||
|
|
||||
|
<record id="air_lines_partner" model="res.partner"> |
||||
|
<field name="name">Airlines</field> |
||||
|
<field name="supplier">True</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,6 @@ |
|||||
|
## Module <ohrms_core> |
||||
|
|
||||
|
#### 25.04.2018 |
||||
|
#### Version 11.0.1.0.0 |
||||
|
##### ADD |
||||
|
- Initial commit for OpenHrms Project |
@ -0,0 +1,6 @@ |
|||||
|
## Module <ohrms_core> |
||||
|
|
||||
|
#### 30.03.2018 |
||||
|
#### Version 10.0.1.0.0 |
||||
|
##### ADD |
||||
|
- Initial commit for OpenHrms Project |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import hr_vacation |
||||
|
from . import hr_payslip |
||||
|
from . import hr_employee_ticket |
@ -0,0 +1,120 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from datetime import datetime |
||||
|
from odoo import models, fields, api, _ |
||||
|
from odoo.exceptions import UserError, ValidationError |
||||
|
|
||||
|
|
||||
|
class HrFlightTicket(models.Model): |
||||
|
_name = 'hr.flight.ticket' |
||||
|
|
||||
|
name = fields.Char() |
||||
|
employee_id = fields.Many2one('hr.employee', string='Employee', required=True) |
||||
|
ticket_type = fields.Selection([('one', 'One Way'), ('round', 'Round Trip')], string='Ticket Type', default='round') |
||||
|
depart_from = fields.Char(string='Departure', required=True) |
||||
|
destination = fields.Char(string='Destination', required=True) |
||||
|
date_start = fields.Date(string='Start Date', required=True) |
||||
|
date_return = fields.Date(string='Return Date') |
||||
|
ticket_class = fields.Selection([('economy', 'Economy'), |
||||
|
('premium_economy', 'Premium Economy'), |
||||
|
('business', 'Business'), |
||||
|
('first_class', 'First Class')], string='Class') |
||||
|
ticket_fare = fields.Float(string='Ticket Fare') |
||||
|
flight_details = fields.Text(string='Flight Details') |
||||
|
return_flight_details = fields.Text(string='Return Flight Details') |
||||
|
state = fields.Selection([('booked', 'Booked'), ('confirmed', 'Confirmed'), ('started', 'Started'), |
||||
|
('completed', 'Completed'), ('canceled', 'Canceled')], string='Status', default='booked') |
||||
|
invoice_id = fields.Many2one('account.invoice', string='Invoice') |
||||
|
leave_id = fields.Many2one('hr.holidays', string='Leave') |
||||
|
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id) |
||||
|
|
||||
|
@api.multi |
||||
|
def name_get(self): |
||||
|
res = [] |
||||
|
for ticket in self: |
||||
|
res.append((ticket.id, _("Flight ticket for %s on %s to %s") % ( |
||||
|
ticket.employee_id.name, ticket.date_start, ticket.destination))) |
||||
|
return res |
||||
|
|
||||
|
@api.constrains('date_start', 'date_return') |
||||
|
def check_valid_date(self): |
||||
|
if self.filtered(lambda c: c.date_return and c.date_start > c.date_return): |
||||
|
raise ValidationError(_('Flight travelling start date must be less than flight return date.')) |
||||
|
|
||||
|
def book_ticket(self): |
||||
|
return {'type': 'ir.actions.act_window_close'} |
||||
|
|
||||
|
def confirm_ticket(self): |
||||
|
if self.ticket_fare <= 0: |
||||
|
raise UserError(_('Please add ticket fare.')) |
||||
|
inv_obj = self.env['account.invoice'].sudo() |
||||
|
expense_account = self.env['ir.config_parameter'].sudo().get_param('travel_expense_account') |
||||
|
if not expense_account: |
||||
|
raise UserError(_('Please select expense account for the flight tickets.')) |
||||
|
domain = [ |
||||
|
('type', '=', 'purchase'), |
||||
|
('company_id', '=', self.company_id.id), |
||||
|
] |
||||
|
journal_id = self.env['account.journal'].search(domain, limit=1) |
||||
|
partner = self.env.ref('hr_vacation_mngmt.air_lines_partner') |
||||
|
if not partner.property_payment_term_id: |
||||
|
date_due = fields.Date.context_today(self) |
||||
|
else: |
||||
|
pterm = partner.property_payment_term_id |
||||
|
pterm_list = \ |
||||
|
pterm.with_context(currency_id=self.env.user.company_id.id).compute( |
||||
|
value=1, date_ref=fields.Date.context_today(self))[0] |
||||
|
date_due = max(line[0] for line in pterm_list) |
||||
|
inv_data = { |
||||
|
'name': '', |
||||
|
'origin': 'Flight Ticket', |
||||
|
'type': 'in_invoice', |
||||
|
'journal_id': journal_id.id, |
||||
|
'payment_term_id': partner.property_payment_term_id.id, |
||||
|
'date_due': date_due, |
||||
|
'reference': False, |
||||
|
'partner_id': partner.id, |
||||
|
'account_id': partner.property_account_payable_id.id, |
||||
|
'invoice_line_ids': [(0, 0, { |
||||
|
'name': 'Flight Ticket', |
||||
|
'price_unit': self.ticket_fare, |
||||
|
'quantity': 1.0, |
||||
|
'account_id': expense_account, |
||||
|
})], |
||||
|
} |
||||
|
inv_id = inv_obj.create(inv_data) |
||||
|
inv_id.action_invoice_open() |
||||
|
self.write({'state': 'confirmed', 'invoice_id': inv_id.id}) |
||||
|
|
||||
|
def cancel_ticket(self): |
||||
|
if self.state == 'booked': |
||||
|
self.write({'state': 'canceled'}) |
||||
|
elif self.state == 'confirmed': |
||||
|
if self.invoice_id and self.invoice_id.state == 'paid': |
||||
|
self.write({'state': 'canceled'}) |
||||
|
if self.invoice_id and self.invoice_id.state == 'open': |
||||
|
self.invoice_id.action_invoice_cancel() |
||||
|
self.write({'state': 'canceled'}) |
||||
|
|
||||
|
@api.model |
||||
|
def run_update_ticket_status(self): |
||||
|
run_out_tickets = self.search([('state', 'in', ['confirmed', 'started']), |
||||
|
('date_return', '<=', datetime.now())]) |
||||
|
confirmed_tickets = self.search([('state', '=', 'confirmed'), ('date_start', '<=', datetime.now()), |
||||
|
('date_return', '>', datetime.now())]) |
||||
|
for ticket in run_out_tickets: |
||||
|
ticket.write({'state': 'completed'}) |
||||
|
for ticket in confirmed_tickets: |
||||
|
ticket.write({'state': 'started'}) |
||||
|
|
||||
|
@api.multi |
||||
|
def action_view_invoice(self): |
||||
|
return { |
||||
|
'name': _('Flight Ticket Invoice'), |
||||
|
'view_mode': 'form', |
||||
|
'view_id': self.env.ref('account.invoice_supplier_form').id, |
||||
|
'res_model': 'account.invoice', |
||||
|
'context': "{'type':'in_invoice'}", |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'res_id': self.invoice_id.id, |
||||
|
} |
@ -0,0 +1,183 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from odoo import models, fields, api, _ |
||||
|
|
||||
|
|
||||
|
class HrPayslip(models.Model): |
||||
|
_inherit = 'hr.payslip' |
||||
|
|
||||
|
leave_salary = fields.Boolean(string='Leave Salary') |
||||
|
|
||||
|
@api.model |
||||
|
def _get_payslip_lines(self, contract_ids, payslip_id): |
||||
|
def _sum_salary_rule_category(localdict, category, amount): |
||||
|
if category.parent_id: |
||||
|
localdict = _sum_salary_rule_category(localdict, category.parent_id, amount) |
||||
|
localdict['categories'].dict[category.code] = category.code in localdict['categories'].dict and localdict['categories'].dict[category.code] + amount or amount |
||||
|
return localdict |
||||
|
|
||||
|
class BrowsableObject(object): |
||||
|
def __init__(self, employee_id, dict, env): |
||||
|
self.employee_id = employee_id |
||||
|
self.dict = dict |
||||
|
self.env = env |
||||
|
|
||||
|
def __getattr__(self, attr): |
||||
|
return attr in self.dict and self.dict.__getitem__(attr) or 0.0 |
||||
|
|
||||
|
class InputLine(BrowsableObject): |
||||
|
"""a class that will be used into the python code, mainly for usability purposes""" |
||||
|
def sum(self, code, from_date, to_date=None): |
||||
|
if to_date is None: |
||||
|
to_date = fields.Date.today() |
||||
|
self.env.cr.execute(""" |
||||
|
SELECT sum(amount) as sum |
||||
|
FROM hr_payslip as hp, hr_payslip_input as pi |
||||
|
WHERE hp.employee_id = %s AND hp.state = 'done' |
||||
|
AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""", |
||||
|
(self.employee_id, from_date, to_date, code)) |
||||
|
return self.env.cr.fetchone()[0] or 0.0 |
||||
|
|
||||
|
class WorkedDays(BrowsableObject): |
||||
|
"""a class that will be used into the python code, mainly for usability purposes""" |
||||
|
def _sum(self, code, from_date, to_date=None): |
||||
|
if to_date is None: |
||||
|
to_date = fields.Date.today() |
||||
|
self.env.cr.execute(""" |
||||
|
SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours |
||||
|
FROM hr_payslip as hp, hr_payslip_worked_days as pi |
||||
|
WHERE hp.employee_id = %s AND hp.state = 'done' |
||||
|
AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""", |
||||
|
(self.employee_id, from_date, to_date, code)) |
||||
|
return self.env.cr.fetchone() |
||||
|
|
||||
|
def sum(self, code, from_date, to_date=None): |
||||
|
res = self._sum(code, from_date, to_date) |
||||
|
return res and res[0] or 0.0 |
||||
|
|
||||
|
def sum_hours(self, code, from_date, to_date=None): |
||||
|
res = self._sum(code, from_date, to_date) |
||||
|
return res and res[1] or 0.0 |
||||
|
|
||||
|
class Payslips(BrowsableObject): |
||||
|
"""a class that will be used into the python code, mainly for usability purposes""" |
||||
|
|
||||
|
def sum(self, code, from_date, to_date=None): |
||||
|
if to_date is None: |
||||
|
to_date = fields.Date.today() |
||||
|
self.env.cr.execute("""SELECT sum(case when hp.credit_note = False then (pl.total) else (-pl.total) end) |
||||
|
FROM hr_payslip as hp, hr_payslip_line as pl |
||||
|
WHERE hp.employee_id = %s AND hp.state = 'done' |
||||
|
AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""", |
||||
|
(self.employee_id, from_date, to_date, code)) |
||||
|
res = self.env.cr.fetchone() |
||||
|
return res and res[0] or 0.0 |
||||
|
|
||||
|
#we keep a dict with the result because a value can be overwritten by another rule with the same code |
||||
|
result_dict = {} |
||||
|
rules_dict = {} |
||||
|
worked_days_dict = {} |
||||
|
inputs_dict = {} |
||||
|
blacklist = [] |
||||
|
payslip = self.env['hr.payslip'].browse(payslip_id) |
||||
|
for worked_days_line in payslip.worked_days_line_ids: |
||||
|
worked_days_dict[worked_days_line.code] = worked_days_line |
||||
|
for input_line in payslip.input_line_ids: |
||||
|
inputs_dict[input_line.code] = input_line |
||||
|
|
||||
|
categories = BrowsableObject(payslip.employee_id.id, {}, self.env) |
||||
|
inputs = InputLine(payslip.employee_id.id, inputs_dict, self.env) |
||||
|
worked_days = WorkedDays(payslip.employee_id.id, worked_days_dict, self.env) |
||||
|
payslips = Payslips(payslip.employee_id.id, payslip, self.env) |
||||
|
rules = BrowsableObject(payslip.employee_id.id, rules_dict, self.env) |
||||
|
|
||||
|
baselocaldict = {'categories': categories, 'rules': rules, 'payslip': payslips, 'worked_days': worked_days, |
||||
|
'inputs': inputs} |
||||
|
# get the ids of the structures on the contracts and their parent id as well |
||||
|
contracts = self.env['hr.contract'].browse(contract_ids) |
||||
|
structure_ids = contracts.get_all_structures() |
||||
|
# get the rules of the structure and thier children |
||||
|
rule_ids = self.env['hr.payroll.structure'].browse(structure_ids).get_all_rules() |
||||
|
# leave salary computation |
||||
|
if payslip.leave_salary: |
||||
|
leave_sal_basic = self.env.ref('hr_vacation_mngmt.hr_salary_rule_leave_salary_basic') |
||||
|
leave_sal_gross = self.env.ref('hr_vacation_mngmt.hr_salary_rule_leave_salary_gross') |
||||
|
default_leave_salary = self.env['ir.config_parameter'].sudo().get_param('default_leave_salary') |
||||
|
if default_leave_salary == '0': |
||||
|
leave_salary = leave_sal_basic |
||||
|
elif default_leave_salary == '1': |
||||
|
leave_salary = leave_sal_gross |
||||
|
else: |
||||
|
leave_salary = leave_sal_basic |
||||
|
rule_ids.append((leave_salary.id, leave_salary.sequence)) |
||||
|
# run the rules by sequence |
||||
|
sorted_rule_ids = [id for id, sequence in sorted(rule_ids, key=lambda x:x[1])] |
||||
|
sorted_rules = self.env['hr.salary.rule'].browse(sorted_rule_ids) |
||||
|
|
||||
|
for contract in contracts: |
||||
|
employee = contract.employee_id |
||||
|
localdict = dict(baselocaldict, employee=employee, contract=contract) |
||||
|
for rule in sorted_rules: |
||||
|
key = rule.code + '-' + str(contract.id) |
||||
|
localdict['result'] = None |
||||
|
localdict['result_qty'] = 1.0 |
||||
|
localdict['result_rate'] = 100 |
||||
|
#check if the rule can be applied |
||||
|
if rule._satisfy_condition(localdict) and rule.id not in blacklist: |
||||
|
#compute the amount of the rule |
||||
|
amount, qty, rate = rule._compute_rule(localdict) |
||||
|
#check if there is already a rule computed with that code |
||||
|
previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 |
||||
|
#set/overwrite the amount computed for this rule in the localdict |
||||
|
tot_rule = amount * qty * rate / 100.0 |
||||
|
localdict[rule.code] = tot_rule |
||||
|
rules_dict[rule.code] = rule |
||||
|
#sum the amount for its salary category |
||||
|
localdict = _sum_salary_rule_category(localdict, rule.category_id, tot_rule - previous_amount) |
||||
|
#create/overwrite the rule in the temporary results |
||||
|
result_dict[key] = { |
||||
|
'salary_rule_id': rule.id, |
||||
|
'contract_id': contract.id, |
||||
|
'name': rule.name, |
||||
|
'code': rule.code, |
||||
|
'category_id': rule.category_id.id, |
||||
|
'sequence': rule.sequence, |
||||
|
'appears_on_payslip': rule.appears_on_payslip, |
||||
|
'condition_select': rule.condition_select, |
||||
|
'condition_python': rule.condition_python, |
||||
|
'condition_range': rule.condition_range, |
||||
|
'condition_range_min': rule.condition_range_min, |
||||
|
'condition_range_max': rule.condition_range_max, |
||||
|
'amount_select': rule.amount_select, |
||||
|
'amount_fix': rule.amount_fix, |
||||
|
'amount_python_compute': rule.amount_python_compute, |
||||
|
'amount_percentage': rule.amount_percentage, |
||||
|
'amount_percentage_base': rule.amount_percentage_base, |
||||
|
'register_id': rule.register_id.id, |
||||
|
'amount': amount, |
||||
|
'employee_id': contract.employee_id.id, |
||||
|
'quantity': qty, |
||||
|
'rate': rate, |
||||
|
} |
||||
|
else: |
||||
|
#blacklist this rule and its children |
||||
|
blacklist += [id for id, seq in rule._recursive_search_of_rules()] |
||||
|
|
||||
|
return list(result_dict.values()) |
||||
|
|
||||
|
|
||||
|
class HrPayrollConfigSettings(models.TransientModel): |
||||
|
_inherit = 'res.config.settings' |
||||
|
|
||||
|
default_leave_salary = fields.Selection([('0', 'Basic'), ('1', 'Gross')], string='Leave Salary') |
||||
|
|
||||
|
def get_values(self): |
||||
|
res = super(HrPayrollConfigSettings, self).get_values() |
||||
|
res.update( |
||||
|
default_leave_salary=self.env['ir.config_parameter'].sudo().get_param('default_leave_salary') |
||||
|
) |
||||
|
return res |
||||
|
|
||||
|
def set_values(self): |
||||
|
super(HrPayrollConfigSettings, self).set_values() |
||||
|
self.env['ir.config_parameter'].sudo().set_param('default_leave_salary', self.default_leave_salary) |
@ -0,0 +1,171 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from datetime import datetime, timedelta, date |
||||
|
from odoo import models, fields, api, _ |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class HrLeaveRequest(models.Model): |
||||
|
_inherit = 'hr.holidays' |
||||
|
|
||||
|
remaining_leaves = fields.Float(string='Remaining Legal Leaves', related='employee_id.remaining_leaves') |
||||
|
overlapping_leaves = fields.Many2many('hr.holidays', compute='get_overlapping_leaves', string='Overlapping Leaves') |
||||
|
pending_tasks = fields.One2many('pending.task', 'leave_id', string='Pending Tasks') |
||||
|
holiday_managers = fields.Many2many('res.users', compute='get_hr_holiday_managers') |
||||
|
flight_ticket = fields.One2many('hr.flight.ticket', 'leave_id', string='Flight Ticket') |
||||
|
|
||||
|
@api.one |
||||
|
def get_overlapping_leaves(self): |
||||
|
if self.type == 'remove' and self.date_from and self.date_to: |
||||
|
overlap_leaves = [] |
||||
|
from_date = datetime.strptime(self.date_from, '%Y-%m-%d %H:%M:%S').date() |
||||
|
to_date = datetime.strptime(self.date_to, '%Y-%m-%d %H:%M:%S').date() |
||||
|
r = (to_date + timedelta(days=1) - from_date).days |
||||
|
leave_dates = [str(from_date + timedelta(days=i)) for i in range(r)] |
||||
|
leaves = self.env['hr.holidays'].search([('state', '=', 'validate'), ('type', '=', 'remove'), |
||||
|
('department_id', '=', self.department_id.id)]) |
||||
|
other_leaves = leaves - self |
||||
|
for leave in other_leaves: |
||||
|
frm_dte = datetime.strptime(leave.date_from, '%Y-%m-%d %H:%M:%S').date() |
||||
|
to_dte = datetime.strptime(leave.date_to, '%Y-%m-%d %H:%M:%S').date() |
||||
|
r = (to_dte + timedelta(days=1) - frm_dte).days |
||||
|
leave_dtes = [str(frm_dte + timedelta(days=i)) for i in range(r)] |
||||
|
if set(leave_dtes).intersection(set(leave_dates)): |
||||
|
overlap_leaves.append(leave.id) |
||||
|
self.update({'overlapping_leaves': [(6, 0, overlap_leaves)]}) |
||||
|
|
||||
|
@api.multi |
||||
|
def action_approve(self): |
||||
|
# if double_validation: this method is the first approval approval |
||||
|
# if not double_validation: this method calls action_validate() below |
||||
|
if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'): |
||||
|
raise UserError(_('Only an HR Officer or Manager can approve leave requests.')) |
||||
|
|
||||
|
manager = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) |
||||
|
for holiday in self: |
||||
|
if holiday.state != 'confirm': |
||||
|
raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.')) |
||||
|
|
||||
|
if holiday.pending_tasks: |
||||
|
if holiday.user_id: |
||||
|
ctx = dict(self.env.context or {}) |
||||
|
ctx.update({ |
||||
|
'default_leave_req_id': self.id, |
||||
|
}) |
||||
|
return { |
||||
|
'name': _('Re-Assign Task'), |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'view_type': 'form', |
||||
|
'view_mode': 'form', |
||||
|
'res_model': 'task.reassign', |
||||
|
'target': 'new', |
||||
|
'context': ctx, |
||||
|
} |
||||
|
else: |
||||
|
raise UserError(_('Please configure user for the employee %s') % (holiday.employee_id.name,)) |
||||
|
else: |
||||
|
if holiday.double_validation: |
||||
|
return holiday.write({'state': 'validate1', 'manager_id': manager.id if manager else False}) |
||||
|
else: |
||||
|
holiday.action_validate() |
||||
|
|
||||
|
def book_ticket(self): |
||||
|
if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'): |
||||
|
raise UserError(_('Only an HR Officer or Manager can book flight tickets.')) |
||||
|
ctx = dict(self.env.context or {}) |
||||
|
ctx.update({ |
||||
|
'default_employee_id': self.employee_id.id, |
||||
|
'default_leave_id': self.id, |
||||
|
}) |
||||
|
return { |
||||
|
'name': _('Book Flight Ticket'), |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'view_type': 'form', |
||||
|
'view_mode': 'form', |
||||
|
'view_id': self.env.ref('hr_vacation_mngmt.view_hr_book_flight_ticket_form').id, |
||||
|
'res_model': 'hr.flight.ticket', |
||||
|
'target': 'new', |
||||
|
'context': ctx, |
||||
|
} |
||||
|
|
||||
|
@api.one |
||||
|
def get_hr_holiday_managers(self): |
||||
|
self.holiday_managers = self.env.ref('hr_holidays.group_hr_holidays_manager').users |
||||
|
|
||||
|
def view_flight_ticket(self): |
||||
|
return { |
||||
|
'name': _('Flight Ticket'), |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'view_type': 'form', |
||||
|
'view_mode': 'form', |
||||
|
'res_model': 'hr.flight.ticket', |
||||
|
'target': 'current', |
||||
|
'res_id': self.flight_ticket[0].id, |
||||
|
} |
||||
|
|
||||
|
@api.model |
||||
|
def send_leave_reminder(self): |
||||
|
leave_request = self.env['hr.holidays'].search([('type', '=', 'remove'), ('state', '=', 'validate')]) |
||||
|
leave_reminder = self.env['ir.config_parameter'].sudo().get_param('leave_reminder') |
||||
|
reminder_day_before = int(self.env['ir.config_parameter'].sudo().get_param('reminder_day_before')) |
||||
|
mail_template = self.env.ref('hr_vacation_mngmt.email_template_hr_leave_reminder_mail') |
||||
|
holiday_managers = self.env.ref('hr_holidays.group_hr_holidays_manager').users |
||||
|
today = date.today() |
||||
|
if leave_reminder: |
||||
|
for request in leave_request: |
||||
|
if request.date_from: |
||||
|
from_date = datetime.strptime(request.date_from, '%Y-%m-%d %H:%M:%S').date() |
||||
|
if reminder_day_before == 0: |
||||
|
prev_reminder_day = request.date_from |
||||
|
else: |
||||
|
prev_reminder_day = from_date - timedelta(days=reminder_day_before) |
||||
|
if prev_reminder_day == today: |
||||
|
for manager in holiday_managers: |
||||
|
template = mail_template.sudo().with_context( |
||||
|
email_to=manager.email, |
||||
|
) |
||||
|
template.send_mail(request.id, force_send=True) |
||||
|
|
||||
|
|
||||
|
class PendingTask(models.Model): |
||||
|
_name = 'pending.task' |
||||
|
|
||||
|
name = fields.Char(string='Task', required=True) |
||||
|
leave_id = fields.Many2one('hr.holidays', string='Leave Request') |
||||
|
dept_id = fields.Many2one('hr.department', string='Department', related='leave_id.department_id') |
||||
|
project_id = fields.Many2one('project.project', string='Project', required=True) |
||||
|
description = fields.Text(string='Description') |
||||
|
assigned_to = fields.Many2one('hr.employee', string='Assigned to', |
||||
|
domain="[('department_id', '=', dept_id)]") |
||||
|
unavailable_employee = fields.Many2many('hr.employee', string='Unavailable Employees', |
||||
|
compute='get_unavailable_employee') |
||||
|
|
||||
|
@api.one |
||||
|
def get_unavailable_employee(self): |
||||
|
unavail_emp = [] |
||||
|
for leave in self.leave_id.overlapping_leaves: |
||||
|
unavail_emp.append(leave.employee_id.id) |
||||
|
self.update({'unavailable_employee': unavail_emp}) |
||||
|
|
||||
|
|
||||
|
class HrVacationConfigSettings(models.TransientModel): |
||||
|
_inherit = 'res.config.settings' |
||||
|
|
||||
|
leave_reminder = fields.Boolean(string='Leave Reminder Email', help="Send leave remainder emails to hr managers") |
||||
|
reminder_day_before = fields.Integer(string='Reminder Day Before') |
||||
|
default_expense_account = fields.Many2one('account.account', string='Travel Expense Account') |
||||
|
|
||||
|
def get_values(self): |
||||
|
res = super(HrVacationConfigSettings, self).get_values() |
||||
|
res.update( |
||||
|
leave_reminder=self.env['ir.config_parameter'].sudo().get_param('leave_reminder'), |
||||
|
reminder_day_before=int(self.env['ir.config_parameter'].sudo().get_param('reminder_day_before')), |
||||
|
default_expense_account=int(self.env['ir.config_parameter'].sudo().get_param('travel_expense_account')) |
||||
|
) |
||||
|
return res |
||||
|
|
||||
|
def set_values(self): |
||||
|
super(HrVacationConfigSettings, self).set_values() |
||||
|
self.env['ir.config_parameter'].sudo().set_param('leave_reminder', self.leave_reminder) |
||||
|
self.env['ir.config_parameter'].sudo().set_param('reminder_day_before', self.reminder_day_before) |
||||
|
self.env['ir.config_parameter'].sudo().set_param('travel_expense_account', self.default_expense_account.id) |
@ -0,0 +1,11 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<record id="property_rule_hr_flight_ticket" model="ir.rule"> |
||||
|
<field name="name">Hr Flight Ticket Multi Company</field> |
||||
|
<field name="model_id" ref="model_hr_flight_ticket"/> |
||||
|
<field eval="True" name="global"/> |
||||
|
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 221 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,216 @@ |
|||||
|
<section class="oe_container "> |
||||
|
<div class="oe_row"> |
||||
|
<h2 class="oe_slogan">Open HRMS</h2> |
||||
|
<h3 class="oe_slogan">Most advanced open source HR management software</h3> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced oe_mt32"> |
||||
|
<div class="oe_span"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<a href="https://www.openhrms.com/#request-demo"> |
||||
|
<img src="HRMS-BUTTON.png"> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h2 class="oe_slogan">Open HRMS Vacation Management</h2> |
||||
|
<h3 class="oe_slogan">Manage Employee Vacation</h3> |
||||
|
<h4 class="oe_slogan"><a href="https://www.cybrosys.com">Cybrosys Technologies</a></h4> |
||||
|
</div> |
||||
|
<div class="oe_row oe_spaced" style="padding-left:65px;"> |
||||
|
<h4>Features:</h4> |
||||
|
<div> |
||||
|
<span style="color:green;"> ☑ </span>Remaining legal leaves in leave request to approval.<br/> |
||||
|
<span style="color:green;"> ☑ </span>Overlapping leaves.<br/> |
||||
|
<span style="color:green;"> ☑ </span>Leave notification.<br/> |
||||
|
<span style="color:green;"> ☑ </span>Leave salary.<br/> |
||||
|
<span style="color:green;"> ☑ </span>Pending task update and re-assign task.<br/> |
||||
|
<span style="color:green;"> ☑ </span>Flight ticket booking.<br/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="" style="text-align: center;"> |
||||
|
<div class="oe_picture"> |
||||
|
<h3 class="oe_slogan">Overview</h3> |
||||
|
<p class="oe_mt32"> |
||||
|
This module extend Odoo default HR Holiday Management with extra features adaptable for managing employees vacation.. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div style="text-align: center"> |
||||
|
<p><h4 class="oe_slogan">Remaining & Overlapping Leaves</h4><p> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_01.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div style="text-align: center"> |
||||
|
<p><h4 class="oe_slogan">Pending Tasks Update & Task Re-assign</h4><p> |
||||
|
|
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<p class="oe_mt32"> |
||||
|
Employees can able to update their pending tasks on leave request |
||||
|
</p> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_02.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
|
||||
|
<div style="text-align: center"> |
||||
|
<p class="oe_mt32"> |
||||
|
On leave request approval it opens a wizard to re-assign tasks to available employees. |
||||
|
</p> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_03.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div style="text-align: center"> |
||||
|
<p><h4 class="oe_slogan">Flight Ticket Booking</h4><p> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<p class="oe_mt32"> |
||||
|
HR managers can able to book the flight ticket for the employee. |
||||
|
</p> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_04.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_05.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_06.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_07.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<p class="oe_mt32"> |
||||
|
On confirming the flight ticket generate corresponding supplier invoice. |
||||
|
</p> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_08.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div style="text-align: center"> |
||||
|
<p><h4 class="oe_slogan">Leave Salary</h4><p> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_09.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div style="text-align: center"> |
||||
|
<p><h4 class="oe_slogan">Configurations</h4><p> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_10.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div style="text-align: center"> |
||||
|
<a href="https://www.cybrosys.com"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img style="border:10px solid white;" src="ohrms_vacation_11.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<div class="row section-content"> |
||||
|
<div class="col-md-6 img-content"> |
||||
|
<h3>Our Odoo Services</h3> |
||||
|
</div> |
||||
|
<div class="bc-span col-md-12"><div class="inner-span"> |
||||
|
<a target="_blank" href="https://www.openhrms.com"><img class="img-border img-responsive thumbnail" src="cybro-service.png"></a> |
||||
|
</div></div> |
||||
|
</div> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2> |
||||
|
<div class="oe_slogan" style="margin-top:10px !important;"> |
||||
|
<div> |
||||
|
<a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/contact/" target="_blank"><i |
||||
|
class="fa fa-phone"></i> Contact Us </a> |
||||
|
|
||||
|
<a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.odoo.com/apps/modules/browse?search=open+hrms" target="_blank"><i |
||||
|
class="fa fa-suitcase"></i> Other Open HRMS Addons </a> |
||||
|
|
||||
|
<a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/odoo-customization-and-installation/" target="_blank"><i |
||||
|
class="fa fa-wrench"></i> Request Customization </a> |
||||
|
|
||||
|
</div> |
||||
|
<br> |
||||
|
<a href="https://www.cybrosys.com/" target="_blank"> |
||||
|
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block"> |
||||
|
</a> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 58 KiB |
@ -0,0 +1,121 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="view_hr_flight_ticket_form" model="ir.ui.view"> |
||||
|
<field name="name">hr.flight.ticket.form</field> |
||||
|
<field name="model">hr.flight.ticket</field> |
||||
|
<field name="priority" eval="0"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Flight Ticket" create="0"> |
||||
|
<header> |
||||
|
<button name="confirm_ticket" states="booked" string="Confirm" type="object" class="oe_highlight"/> |
||||
|
<button name="cancel_ticket" string="Cancel" type="object" states="booked,confirmed"/> |
||||
|
<field name="state" widget="statusbar" |
||||
|
statusbar_visible="booked,confirmed,started,completed"/> |
||||
|
</header> |
||||
|
<sheet> |
||||
|
<div class="oe_button_box" name="button_box"> |
||||
|
<button name="action_view_invoice" |
||||
|
string="Invoice" |
||||
|
type="object" |
||||
|
class="oe_stat_button" |
||||
|
icon="fa-pencil-square-o" |
||||
|
attrs="{'invisible':[('invoice_id','=', False)]}"> |
||||
|
</button> |
||||
|
<field name="invoice_id" invisible="1"/> |
||||
|
</div> |
||||
|
<div class="oe_title"> |
||||
|
<h1><field name="display_name"/></h1> |
||||
|
</div> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="employee_id" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="depart_from" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="date_start" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="ticket_class" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="flight_details" attrs="{'readonly': [('state','=','completed')]}"/> |
||||
|
<field name="company_id" invisible="1"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="ticket_type" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="destination" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="date_return" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="ticket_fare" attrs="{'readonly': [('state','in',('confirmed','started','completed'))]}"/> |
||||
|
<field name="leave_id" invisible="1"/> |
||||
|
<field name="return_flight_details" |
||||
|
attrs="{'readonly': [('state','=','completed')], |
||||
|
'invisible':[('ticket_type', '=', 'one')]}"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_hr_flight_ticket_tree" model="ir.ui.view"> |
||||
|
<field name="name">hr.flight.ticket.form</field> |
||||
|
<field name="model">hr.flight.ticket</field> |
||||
|
<field name="priority" eval="1"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree string="Flight Ticket" create="0"> |
||||
|
<field name="employee_id"/> |
||||
|
<field name="date_start"/> |
||||
|
<field name="date_return"/> |
||||
|
<field name="ticket_type"/> |
||||
|
<field name="state"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_hr_book_flight_ticket_form" model="ir.ui.view"> |
||||
|
<field name="name">hr.flight.ticket.form</field> |
||||
|
<field name="model">hr.flight.ticket</field> |
||||
|
<field name="priority" eval="2"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Flight Ticket" create="0"> |
||||
|
<div class="oe_title"> |
||||
|
<h1><field name="name" readonly="1"/></h1> |
||||
|
</div> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="employee_id" readonly="1"/> |
||||
|
<field name="depart_from"/> |
||||
|
<field name="date_start"/> |
||||
|
<field name="ticket_class"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="ticket_type"/> |
||||
|
<field name="destination"/> |
||||
|
<field name="date_return"/> |
||||
|
<field name="leave_id" invisible="1"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
<footer> |
||||
|
<button name="book_ticket" type="object" string="Book Ticket" class="btn-primary"/> |
||||
|
<button string="Cancel" class="btn-default" special="cancel"/> |
||||
|
</footer> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="action_hr_flight_tickets" model="ir.actions.act_window"> |
||||
|
<field name="name">Flight Tickets</field> |
||||
|
<field name="res_model">hr.flight.ticket</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="help" type="html"> |
||||
|
<p class="oe_view_nocontent_create"> |
||||
|
There is no previously booked flight ticket details available. |
||||
|
</p> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<menuitem |
||||
|
id="menu_hr_flight_tickets" |
||||
|
name="Flight Tickets" |
||||
|
parent="hr_holidays.menu_hr_holidays_root" |
||||
|
action="action_hr_flight_tickets" |
||||
|
groups="hr_holidays.group_hr_holidays_manager" |
||||
|
sequence="5"/> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,39 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record model="ir.ui.view" id="view_hr_payslip_leave_salary_form"> |
||||
|
<field name="name">hr.payslip.form</field> |
||||
|
<field name="model">hr.payslip</field> |
||||
|
<field name="inherit_id" ref="hr_payroll.view_hr_payslip_form"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<field name="credit_note" position="after"> |
||||
|
<field name="leave_salary" attrs="{'readonly': [('state','=', 'done')]}"/> |
||||
|
</field> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="res_config_settings_view_form" model="ir.ui.view"> |
||||
|
<field name="name">Configure Payroll</field> |
||||
|
<field name="model">res.config.settings</field> |
||||
|
<field name="inherit_id" ref="hr_payroll.res_config_settings_view_form"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<xpath expr="//div[@id='hr_payroll_localization']" position="after"> |
||||
|
<h2>Leaves</h2> |
||||
|
<div class="row mt16 o_settings_container" id="hr_leave_salary"> |
||||
|
<div class="col-md-6 col-xs-12 o_setting_box"> |
||||
|
<div class="o_setting_right_pane"> |
||||
|
<label string="Leave Salary"/> |
||||
|
<div class="text-muted"> |
||||
|
Leave salary calculation |
||||
|
</div> |
||||
|
<div class="mt16"> |
||||
|
<field name="default_leave_salary" widget="radio"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</xpath> |
||||
|
</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="email_template_hr_leave_reminder_mail" model="mail.template"> |
||||
|
<field name="name">Leave : Reminder</field> |
||||
|
<field name="model_id" ref="hr_holidays.model_hr_holidays"/> |
||||
|
<field name="auto_delete" eval="True"/> |
||||
|
<field name="email_from">${object.employee_id.company_id.email}</field> |
||||
|
<field name="subject">Reminder: ${object.display_name}</field> |
||||
|
<field name="body_html"><![CDATA[ |
||||
|
<p>Hello ,</p> |
||||
|
<p>The employee <strong>${object.employee_id.name}</strong> has taken <strong>${object.no_of_days_temp}</strong> days leave starting from <strong>${object.date_from}</strong> to <strong>${object.date_to}</strong>.</p> |
||||
|
|
||||
|
<p>Kindly do the needful.</p> |
||||
|
|
||||
|
</p><p>Thank you!</p> |
||||
|
]]></field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,114 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="hr_vacation_form_view" model="ir.ui.view"> |
||||
|
<field name="name">Leave Request</field> |
||||
|
<field name="model">hr.holidays</field> |
||||
|
<field name="inherit_id" ref="hr_holidays.edit_holiday_new"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<xpath expr="//div[hasclass('oe_title')]" position="before"> |
||||
|
<div class="oe_button_box" name="button_box"> |
||||
|
<button type="object" icon="fa-pencil-square-o" name="view_flight_ticket" |
||||
|
string="Flight Ticket" class="oe_stat_button" |
||||
|
attrs="{'invisible': [('flight_ticket','=', [])]}" |
||||
|
groups="hr_holidays.group_hr_holidays_manager"> |
||||
|
</button> |
||||
|
</div> |
||||
|
</xpath> |
||||
|
<button name="action_draft" position="after"> |
||||
|
<button string="Book Flight Ticket" name="book_ticket" type="object" |
||||
|
groups="hr_holidays.group_hr_holidays_manager" class="oe_highlight" |
||||
|
attrs="{'invisible': ['|', '|', ('type','=','add'),('state','!=','validate'), ('flight_ticket','!=',[])]}"/> |
||||
|
</button> |
||||
|
<field name="employee_id" position="after"> |
||||
|
<field name="remaining_leaves" attrs="{'invisible': [('state','in',('validate', 'refuse'))]}" |
||||
|
readonly="1" groups="hr_holidays.group_hr_holidays_user,hr_holidays.group_hr_holidays_manager"/> |
||||
|
<field name="flight_ticket" invisible="1"/> |
||||
|
</field> |
||||
|
<xpath expr="//sheet" position="inside"> |
||||
|
<notebook attrs="{'invisible': [('type','=','add')], 'readonly': [('state','in',('validate', 'refuse'))]}"> |
||||
|
<page string="Pending Works"> |
||||
|
<field name="pending_tasks" widget="one2many_list" attrs="{'readonly': [('state','in',('validate', 'validate1','refuse'))]}"> |
||||
|
<tree editable="bottom"> |
||||
|
<field name="name"/> |
||||
|
<field name="project_id"/> |
||||
|
<field name="description"/> |
||||
|
<field name="leave_id" invisible="1"/> |
||||
|
<field name="dept_id" invisible="1"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</page> |
||||
|
<page string="Overlapping Leaves" |
||||
|
attrs="{'invisible': ['|',('state','in',('validate', 'refuse')),('overlapping_leaves','=',[])]}" |
||||
|
groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"> |
||||
|
<field name="overlapping_leaves"/> |
||||
|
</page> |
||||
|
</notebook> |
||||
|
</xpath> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_pending_task" model="ir.ui.view"> |
||||
|
<field name="name">pending.task.form</field> |
||||
|
<field name="model">pending.task</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Pending Tasks"> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="name"/> |
||||
|
<field name="leave_id"/> |
||||
|
<field name="project_id"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="dept_id"/> |
||||
|
<field name="assigned_to" groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/> |
||||
|
<field name="unavailable_employee" widget="many2many_tags" |
||||
|
groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/> |
||||
|
</group> |
||||
|
<group colspan="4"> |
||||
|
<field name="description"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_hr_leave_configuration" model="ir.ui.view"> |
||||
|
<field name="name">Configure Leave</field> |
||||
|
<field name="model">res.config.settings</field> |
||||
|
<field name="inherit_id" ref="hr_leave_request_aliasing.view_hr_leave_configuration"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<xpath expr="//div[@name='config_leave']" position="inside"> |
||||
|
<div class="col-xs-12 col-md-6 o_setting_box" name="config_leave_reminder"> |
||||
|
<div class="o_setting_left_pane"> |
||||
|
<field name="leave_reminder"/> |
||||
|
</div> |
||||
|
<div class="o_setting_right_pane"> |
||||
|
<label string="Leaves Reminder"/> |
||||
|
<div class="text-muted"> |
||||
|
Send leave remainder emails to holiday managers |
||||
|
</div> |
||||
|
<div class="content-group"> |
||||
|
<div class="row mt16" attrs="{'invisible': [('leave_reminder', '=', False)]}"> |
||||
|
<label string="Days Before" class="col-md-3 o_light_label"/> |
||||
|
<field name="reminder_day_before"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-xs-12 col-md-6 o_setting_box" name="config_leave_flight_ticket"> |
||||
|
<div class="o_setting_right_pane"> |
||||
|
<label string="Flight Ticket"/> |
||||
|
<div class="content-group"> |
||||
|
<div class="row mt16"> |
||||
|
<label string="Expense Account" class="col-md-3 o_light_label"/> |
||||
|
<field name="default_expense_account"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</xpath> |
||||
|
</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import reassign_task |
@ -0,0 +1,59 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from odoo import models, fields, api, _ |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class ReAssignTask(models.TransientModel): |
||||
|
_name = 'task.reassign' |
||||
|
|
||||
|
pending_tasks = fields.One2many('pending.task', related='leave_req_id.pending_tasks', string='Pending Tasks') |
||||
|
leave_req_id = fields.Many2one('hr.holidays', string='Leave Request') |
||||
|
|
||||
|
@api.multi |
||||
|
def action_approve(self): |
||||
|
task_pending = False |
||||
|
e_unavail = False |
||||
|
emp_unavail = [] |
||||
|
for task in self.pending_tasks: |
||||
|
if not task.assigned_to: |
||||
|
task_pending = True |
||||
|
if task_pending: |
||||
|
raise UserError(_('Please assign pending task to employees.')) |
||||
|
else: |
||||
|
for task in self.pending_tasks: |
||||
|
if task.assigned_to in task.unavailable_employee: |
||||
|
emp_unavail.append(task.assigned_to.name) |
||||
|
e_unavail = True |
||||
|
emp_unavail = set(emp_unavail) |
||||
|
emp_unavail_count = len(emp_unavail) |
||||
|
if e_unavail: |
||||
|
if emp_unavail_count == 1: |
||||
|
raise UserError(_('Selected employee %s is not available') % (', '.join(emp_unavail),)) |
||||
|
else: |
||||
|
raise UserError(_('Selected employees %s are not available') % (', '.join(emp_unavail),)) |
||||
|
|
||||
|
else: |
||||
|
manager = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) |
||||
|
holiday = self.leave_req_id |
||||
|
tasks = self.env['project.task'] |
||||
|
for task in self.pending_tasks: |
||||
|
if not task.assigned_to.user_id: |
||||
|
raise UserError(_('Please configure user for the employee %s') % (task.assigned_to.name,)) |
||||
|
vals = { |
||||
|
'name': task.name, |
||||
|
'user_id': task.assigned_to.user_id.id, |
||||
|
'project_id': task.project_id.id, |
||||
|
'description': task.description, |
||||
|
} |
||||
|
tasks.sudo().create(vals) |
||||
|
if holiday.double_validation: |
||||
|
return holiday.write({'state': 'validate1', 'manager_id': manager.id if manager else False}) |
||||
|
else: |
||||
|
holiday.action_validate() |
||||
|
|
||||
|
@api.multi |
||||
|
def cancel(self): |
||||
|
for task in self.pending_tasks: |
||||
|
task.update({'assigned_to': False}) |
||||
|
return {'type': 'ir.actions.act_window_close'} |
@ -0,0 +1,31 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="reassign_task_form" model="ir.ui.view"> |
||||
|
<field name="name">Re-Assign Task</field> |
||||
|
<field name="model">task.reassign</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<h3>Confirm leave request and reassign pending works of the employee.</h3> |
||||
|
<group> |
||||
|
<field name="leave_req_id" invisible="1"/> |
||||
|
<field name="pending_tasks" widget="one2many_list"> |
||||
|
<tree create="0" delete="0" editable="bottom"> |
||||
|
<field name="name" readonly="1"/> |
||||
|
<field name="project_id" readonly="1"/> |
||||
|
<field name="assigned_to"/> |
||||
|
<field name="leave_id" invisible="1"/> |
||||
|
<field name="dept_id" invisible="1"/> |
||||
|
<field name="unavailable_employee" invisible="1"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</group> |
||||
|
<footer> |
||||
|
<button string="Confirm" name="action_approve" type="object" class="btn-primary"/> |
||||
|
<button string="Cancel" type="object" name="cancel" class="oe_link"/> |
||||
|
</footer> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |