mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-24 02:32:05 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -1,5 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_plan_wizard
|
||||
from . import hr_departure_wizard
|
||||
from . import mail_activity_schedule
|
||||
from . import hr_contract_template_wizard
|
||||
from . import hr_bank_account_allocation_wizard_line
|
||||
from . import hr_bank_account_wizard
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_bank_account_allocation_wizard" model="ir.ui.view">
|
||||
<field name="name">hr.bank.account.allocation.wizard.form</field>
|
||||
<field name="model">hr.bank.account.allocation.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Bank Account Allocation">
|
||||
<group>
|
||||
<field name="employee_id" invisible="1"/>
|
||||
<field name="allocation_ids" context="{'default_wizard_id': id}" nolabel="1">
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Save" type="object" name="action_save" class="btn-primary"/>
|
||||
<button string="Cancel" special="cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_bank_account_allocation_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Bank Account Allocations</field>
|
||||
<field name="res_model">hr.bank.account.allocation.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'active_id': active_id}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class BankAccountAllocationLineWizard(models.TransientModel):
|
||||
_name = 'hr.bank.account.allocation.wizard.line'
|
||||
_description = 'Bank Account Allocation Line (Wizard)'
|
||||
_order = "sequence, id"
|
||||
|
||||
wizard_id = fields.Many2one('hr.bank.account.allocation.wizard', required=True, ondelete="cascade")
|
||||
bank_account_id = fields.Many2one('res.partner.bank', required=True, readonly=True)
|
||||
|
||||
acc_number = fields.Char(related='bank_account_id.acc_number', readonly=True)
|
||||
amount = fields.Float(string="Amount", readonly=False, digits=(16, 2))
|
||||
amount_type = fields.Selection(selection='_get_amount_type_selection_vals', readonly=False)
|
||||
symbol = fields.Char(compute="_compute_symbol", readonly=True)
|
||||
trusted = fields.Boolean(string="Trusted")
|
||||
sequence = fields.Integer(default=10)
|
||||
|
||||
@api.depends('amount_type', 'bank_account_id.symbol')
|
||||
def _compute_symbol(self):
|
||||
for line in self:
|
||||
if line.amount_type == 'fixed':
|
||||
line.symbol = line.bank_account_id.currency_id.symbol \
|
||||
or line.wizard_id.employee_id.company_id.currency_id.symbol
|
||||
else:
|
||||
line.symbol = '%'
|
||||
|
||||
def _get_amount_type_selection_vals(self):
|
||||
return [('percentage', 'Percentage'), ('fixed', 'Fixed')]
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="view_bank_account_allocation_line_list" model="ir.ui.view">
|
||||
<field name="name">hr.bank.account.allocation.wizard.line.list</field>
|
||||
<field name="model">hr.bank.account.allocation.wizard.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list editable="bottom" create='0' delete='0'>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="acc_number" readonly="1" string="Account Number"/>
|
||||
<field name="amount" string="Amount" widget='monetary'/>
|
||||
<field name="symbol" string="" width="50px"/>
|
||||
<field name="amount_type" string="Type"/>
|
||||
<field name="trusted" widget="boolean_toggle" string="Trusted"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
63
odoo-bringout-oca-ocb-hr/hr/wizard/hr_bank_account_wizard.py
Normal file
63
odoo-bringout-oca-ocb-hr/hr/wizard/hr_bank_account_wizard.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from odoo import api, models, fields, Command
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.float_utils import float_is_zero, float_round
|
||||
|
||||
|
||||
class BankAccountAllocationWizard(models.TransientModel):
|
||||
_name = 'hr.bank.account.allocation.wizard'
|
||||
_description = 'Bank Account Allocation Wizard'
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', required=True)
|
||||
allocation_ids = fields.One2many('hr.bank.account.allocation.wizard.line', 'wizard_id', string="Allocations", readonly=False)
|
||||
|
||||
def _prepare_allocations_from_employee(self):
|
||||
self.ensure_one()
|
||||
wizard_lines = []
|
||||
distribution = self.employee_id.salary_distribution or {}
|
||||
for ba in self.employee_id.bank_account_ids:
|
||||
if str(ba.id) not in distribution:
|
||||
raise ValidationError(self.env._("Bank account %s not found within the salary distribution of the employee", ba))
|
||||
dist_entry = distribution.get(str(ba.id))
|
||||
amount = dist_entry.get('amount')
|
||||
is_percentage = dist_entry.get('amount_is_percentage')
|
||||
sequence = dist_entry.get('sequence')
|
||||
wizard_lines.append(Command.create({
|
||||
'bank_account_id': ba.id,
|
||||
'amount': amount,
|
||||
'amount_type': 'percentage' if is_percentage else 'fixed',
|
||||
'trusted': ba.allow_out_payment,
|
||||
'sequence': sequence,
|
||||
}))
|
||||
self.write({'allocation_ids': wizard_lines})
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
records = super().create(vals_list)
|
||||
for wizard in records:
|
||||
wizard._prepare_allocations_from_employee()
|
||||
return records
|
||||
|
||||
def action_save(self):
|
||||
self.ensure_one()
|
||||
|
||||
distribution = {}
|
||||
total = 0.0
|
||||
check_for_total = False
|
||||
|
||||
for index, line in enumerate(self.allocation_ids):
|
||||
line_amount = float_round(line.amount, precision_digits=2, rounding_method="DOWN")
|
||||
distribution[str(line.bank_account_id.id)] = {
|
||||
'amount': line_amount,
|
||||
'sequence': line.sequence,
|
||||
'amount_is_percentage': line.amount_type == 'percentage'
|
||||
}
|
||||
if line.amount_type == 'percentage':
|
||||
total += line_amount
|
||||
check_for_total = True
|
||||
line.bank_account_id.sudo().write({
|
||||
'allow_out_payment': line.trusted
|
||||
})
|
||||
if check_for_total and not float_is_zero(total - 100.0, precision_digits=4):
|
||||
raise ValidationError(self.env._("Total percentage allocation must equal 100%."))
|
||||
|
||||
self.employee_id.salary_distribution = distribution
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class HrDepartureWizard(models.TransientModel):
|
||||
_name = 'hr.version.wizard'
|
||||
_description = 'Contract Template Wizard'
|
||||
|
||||
contract_template_id = fields.Many2one(
|
||||
'hr.version', string="Contract Template", groups="hr.group_hr_user", required=True,
|
||||
domain=lambda self: [('company_id', '=', self.env.company.id), ('employee_id', '=', False)],
|
||||
help="Select a contract template to auto-fill the contract form with predefined values. You can still edit the fields as needed after applying the template.")
|
||||
|
||||
def action_load_template(self):
|
||||
employee_id = self.env.context.get('active_id')
|
||||
if not employee_id or not self.contract_template_id:
|
||||
return
|
||||
employee = self.env['hr.employee'].browse(employee_id)
|
||||
Version = self.env['hr.version']
|
||||
whitelist = Version._get_whitelist_fields_from_template()
|
||||
contract_template_vals = self.contract_template_id.copy_data()[0]
|
||||
val_list = {
|
||||
field: value
|
||||
for field, value in contract_template_vals.items()
|
||||
if field in whitelist and not self.env['hr.version']._fields[field].related
|
||||
}
|
||||
employee.write(val_list)
|
||||
employee.version_id.contract_template_id = self.contract_template_id
|
||||
return
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="hr_version_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Contract Template Load</field>
|
||||
<field name="res_model">hr.version.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_version_wizard_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.version.wizard.view.form</field>
|
||||
<field name="model">hr.version.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="contract_template_id"
|
||||
options="{'no_create': True, 'no_create_edit': True}"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_load_template" string="Load" type="object" class="oe_highlight" data-hotkey="q"/>
|
||||
<button string="Discard" class="btn-secondary" special="cancel" data-hotkey="x"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,41 +1,133 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class HrDepartureWizard(models.TransientModel):
|
||||
_name = 'hr.departure.wizard'
|
||||
_description = 'Departure Wizard'
|
||||
|
||||
def _get_employee_departure_date(self):
|
||||
return self.env['hr.employee'].browse(self.env.context['active_id']).departure_date
|
||||
|
||||
def _get_default_departure_date(self):
|
||||
departure_date = False
|
||||
if self.env.context.get('active_id'):
|
||||
departure_date = self._get_employee_departure_date()
|
||||
if len(active_ids := self.env.context.get('active_ids', [])) == 1:
|
||||
employee = self.env['hr.employee'].browse(active_ids[0])
|
||||
departure_date = employee and employee._get_departure_date()
|
||||
else:
|
||||
departure_date = False
|
||||
|
||||
return departure_date or fields.Date.today()
|
||||
|
||||
departure_reason_id = fields.Many2one("hr.departure.reason", default=lambda self: self.env['hr.departure.reason'].search([], limit=1), required=True)
|
||||
departure_description = fields.Html(string="Additional Information")
|
||||
departure_date = fields.Date(string="Departure Date", required=True, default=_get_default_departure_date)
|
||||
employee_id = fields.Many2one(
|
||||
'hr.employee', string='Employee', required=True,
|
||||
default=lambda self: self.env.context.get('active_id', None),
|
||||
def _get_default_employee_ids(self):
|
||||
active_ids = self.env.context.get('active_ids', [])
|
||||
if active_ids:
|
||||
return self.env['hr.employee'].browse(active_ids).filtered(lambda e: e.company_id in self.env.companies)
|
||||
return self.env['hr.employee']
|
||||
|
||||
def _get_domain_employee_ids(self):
|
||||
return [('active', '=', True), ('company_id', 'in', self.env.companies.ids)]
|
||||
|
||||
departure_reason_id = fields.Many2one("hr.departure.reason", required=True,
|
||||
default=lambda self: self.env['hr.departure.reason'].search([], limit=1),
|
||||
)
|
||||
archive_private_address = fields.Boolean('Archive Private Address', default=True)
|
||||
departure_description = fields.Html(string="Additional Information")
|
||||
departure_date = fields.Date(string="Contract End Date", required=True, default=_get_default_departure_date)
|
||||
employee_ids = fields.Many2many(
|
||||
'hr.employee', string='Employees', required=True,
|
||||
default=_get_default_employee_ids,
|
||||
context={'active_test': False},
|
||||
domain=_get_domain_employee_ids,
|
||||
)
|
||||
|
||||
is_user_employee = fields.Boolean(
|
||||
string="User Employee",
|
||||
compute='_compute_is_user_employee',
|
||||
)
|
||||
remove_related_user = fields.Boolean(
|
||||
string="Related User",
|
||||
help="If checked, the related user will be removed from the system.",
|
||||
)
|
||||
|
||||
set_date_end = fields.Boolean(string="Set Contract End Date", default=lambda self: self.env.user.has_group('hr.group_hr_user'),
|
||||
help="Set the end date on the current contract.")
|
||||
|
||||
@api.depends('employee_ids.user_id')
|
||||
def _compute_is_user_employee(self):
|
||||
for wizard in self:
|
||||
# Check if at least one employee in the wizard has a user and all the employees related to this user are in the wizard
|
||||
# This is to ensure that the user is not removed if there are other employees related to it
|
||||
related_users = wizard.employee_ids.user_id
|
||||
wizard.is_user_employee = bool(related_users)
|
||||
|
||||
def action_register_departure(self):
|
||||
employee = self.employee_id
|
||||
if self.env.context.get('toggle_active', False) and employee.active:
|
||||
employee.with_context(no_wizard=True).toggle_active()
|
||||
employee.departure_reason_id = self.departure_reason_id
|
||||
employee.departure_description = self.departure_description
|
||||
employee.departure_date = self.departure_date
|
||||
def _get_user_archive_notification_action(message, message_type, next_action):
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': self.env._("User Archive Notification"),
|
||||
'type': message_type,
|
||||
'message': message,
|
||||
'next': next_action,
|
||||
},
|
||||
}
|
||||
|
||||
if self.archive_private_address:
|
||||
# ignore contact links to internal users
|
||||
private_address = employee.address_home_id
|
||||
if private_address and private_address.active and not self.env['res.users'].search([('partner_id', '=', private_address.id)]):
|
||||
private_address.sudo().toggle_active()
|
||||
employee_ids = self.employee_ids
|
||||
active_versions = employee_ids.version_id
|
||||
|
||||
if any(v.contract_date_start and v.contract_date_start > self.departure_date for v in active_versions):
|
||||
raise UserError(self.env._("Departure date can't be earlier than the start date of current contract."))
|
||||
|
||||
allow_archived_users = self.env['res.users']
|
||||
unarchived_users = self.env['res.users']
|
||||
if self.remove_related_user:
|
||||
related_users = employee_ids.grouped('user_id')
|
||||
related_employees_count = dict(self.env['hr.employee'].sudo()._read_group(
|
||||
domain=[('user_id', 'in', employee_ids.user_id.ids)],
|
||||
groupby=['user_id'],
|
||||
aggregates=['id:count'],
|
||||
))
|
||||
for user, emps in related_users.items():
|
||||
if not user:
|
||||
continue
|
||||
if len(emps) == related_employees_count.get(user, 0):
|
||||
allow_archived_users |= user
|
||||
else:
|
||||
unarchived_users |= user
|
||||
|
||||
archived_employees = self.env['hr.employee']
|
||||
archived_users = self.env['res.users']
|
||||
for employee in employee_ids.filtered(lambda emp: emp.active):
|
||||
if self.env.context.get('employee_termination', False):
|
||||
archived_employees |= employee
|
||||
if self.remove_related_user and employee.user_id in allow_archived_users:
|
||||
archived_users |= employee.user_id
|
||||
|
||||
archived_employees.with_context(no_wizard=True).action_archive()
|
||||
archived_users.action_archive()
|
||||
|
||||
employee_ids.write({
|
||||
'departure_reason_id': self.departure_reason_id,
|
||||
'departure_description': self.departure_description,
|
||||
'departure_date': self.departure_date,
|
||||
})
|
||||
|
||||
if self.set_date_end:
|
||||
# Write date and update state of current contracts
|
||||
active_versions = active_versions.filtered(lambda v: v.contract_date_start)
|
||||
active_versions.write({'contract_date_end': self.departure_date})
|
||||
|
||||
next_action = {'type': 'ir.actions.act_window_close'}
|
||||
if archived_users:
|
||||
message = self.env._(
|
||||
"The following users have been archived: %s",
|
||||
', '.join(archived_users.mapped('name'))
|
||||
)
|
||||
next_action = _get_user_archive_notification_action(message, 'success', next_action)
|
||||
if unarchived_users:
|
||||
message = self.env._(
|
||||
"The following users have not been archived as they are still linked to another active employees: %s",
|
||||
', '.join(unarchived_users.mapped('name'))
|
||||
)
|
||||
next_action = _get_user_archive_notification_action(message, 'danger', next_action)
|
||||
|
||||
return next_action
|
||||
|
|
|
|||
|
|
@ -7,49 +7,41 @@
|
|||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<h1><field name="employee_id" readonly="1" options="{'no_open': True}"/></h1>
|
||||
<group>
|
||||
<group id="info">
|
||||
<field name="employee_ids" widget="many2many_tags"/>
|
||||
<field name="departure_reason_id" options="{'no_edit': True, 'no_create': True, 'no_open': True}"/>
|
||||
<field name="departure_date"/>
|
||||
</group>
|
||||
<group id="action">
|
||||
<!-- Override invisible="1" when inheriting -->
|
||||
<div class="o_td_label" id="activities_label" invisible="1">
|
||||
<div class="o_td_label" id="activities_label">
|
||||
<span class="o_form_label o_hr_form_label cursor-default">Close Activities</span>
|
||||
</div>
|
||||
<!-- Override invisible="1" when inheriting -->
|
||||
<div class="column" id="activities" invisible="1">
|
||||
</div>
|
||||
<separator colspan="2"/>
|
||||
<div class="o_td_label" id="label_info">
|
||||
<span class="o_form_label o_hr_form_label cursor-default">HR Info</span>
|
||||
</div>
|
||||
<div class="column" id="info">
|
||||
<div><field name="archive_private_address"/><label for="archive_private_address"/></div>
|
||||
<div class="column" id="activities">
|
||||
<div>
|
||||
<field name="set_date_end"/><label for="set_date_end" string="Contract"/>
|
||||
</div>
|
||||
<div invisible="not is_user_employee">
|
||||
<field name="remove_related_user"/>
|
||||
<label for="remove_related_user" string="Related User"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" id="info"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<div id="detailed_reason" colspan="2">
|
||||
<span class="o_form_label o_hr_form_label cursor-default">Detailed Reason</span>
|
||||
<field name="departure_description"/>
|
||||
<field name="departure_description" placeholder="Give more details about the reason of archiving the employee."/>
|
||||
</div>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_register_departure" string="Apply" type="object" class="oe_highlight" data-hotkey="q"/>
|
||||
<button string="Discard" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
<button string="Discard" class="btn-secondary" special="cancel" data-hotkey="x"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_departure_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Register Departure</field>
|
||||
<field name="res_model">hr.departure.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class HrPlanWizard(models.TransientModel):
|
||||
_name = 'hr.plan.wizard'
|
||||
_description = 'Plan Wizard'
|
||||
|
||||
def _default_plan_id(self):
|
||||
# We know that all employees belong to the same company
|
||||
employees = self.env['hr.employee'].browse(self.env.context.get('active_ids') if self.env.context.get('active_ids') else [])
|
||||
if not employees:
|
||||
return None
|
||||
if len(employees.department_id) > 1:
|
||||
return self.env['hr.plan'].search([
|
||||
('company_id', '=', employees[0].company_id.id),
|
||||
('department_id', '=', False)
|
||||
], limit=1)
|
||||
else:
|
||||
return self.env['hr.plan'].search([
|
||||
('company_id', '=', employees[0].company_id.id),
|
||||
'|',
|
||||
('department_id', '=', employees[0].department_id.id),
|
||||
('department_id', '=', False)
|
||||
], limit=1)
|
||||
|
||||
plan_id = fields.Many2one('hr.plan', default=lambda self: self._default_plan_id(),
|
||||
domain="[('company_id', 'in', [False, company_id]), '|', ('department_id', '=', department_id), ('department_id', '=', False)]")
|
||||
department_id = fields.Many2one('hr.department', compute='_compute_department_id')
|
||||
employee_ids = fields.Many2many(
|
||||
'hr.employee', 'hr_employee_hr_plan_wizard_rel', 'employee_id', 'plan_wizard_id', string='Employee', required=True,
|
||||
default=lambda self: self.env.context.get('active_ids', []),
|
||||
)
|
||||
company_id = fields.Many2one('res.company', 'Company', compute='_compute_company_id', required=True)
|
||||
warning = fields.Html(compute='_compute_warning')
|
||||
|
||||
@api.depends('employee_ids')
|
||||
def _compute_department_id(self):
|
||||
for wizard in self:
|
||||
all_departments = wizard.employee_ids.department_id
|
||||
wizard.department_id = False if len(all_departments) > 1 else all_departments
|
||||
|
||||
@api.constrains('employee_ids')
|
||||
def _check_employee_companies(self):
|
||||
for wizard in self:
|
||||
if len(wizard.employee_ids.mapped('company_id')) > 1:
|
||||
raise ValidationError(_('The employees should belong to the same company.'))
|
||||
|
||||
@api.depends('employee_ids')
|
||||
def _compute_company_id(self):
|
||||
for wizard in self:
|
||||
wizard.company_id = wizard.employee_ids and wizard.employee_ids[0].company_id or self.env.company
|
||||
|
||||
def _get_warnings(self):
|
||||
self.ensure_one()
|
||||
warnings = set()
|
||||
for employee in self.employee_ids:
|
||||
for activity_type in self.plan_id.plan_activity_type_ids:
|
||||
warning = activity_type.get_responsible_id(employee)['warning']
|
||||
if warning:
|
||||
warnings.add(warning)
|
||||
return warnings
|
||||
|
||||
@api.depends('employee_ids', 'plan_id')
|
||||
def _compute_warning(self):
|
||||
for wizard in self:
|
||||
warnings = wizard._get_warnings()
|
||||
|
||||
if warnings:
|
||||
warning_display = _('The plan %s cannot be launched: <br><ul>', wizard.plan_id.name)
|
||||
for warning in warnings:
|
||||
warning_display += '<li>%s</li>' % warning
|
||||
warning_display += '</ul>'
|
||||
else:
|
||||
warning_display = False
|
||||
wizard.warning = warning_display
|
||||
|
||||
def _get_activities_to_schedule(self):
|
||||
return self.plan_id.plan_activity_type_ids
|
||||
|
||||
def action_launch(self):
|
||||
self.ensure_one()
|
||||
for employee in self.employee_ids:
|
||||
body = _('The plan %s has been started', self.plan_id.name)
|
||||
activities = set()
|
||||
for activity_type in self._get_activities_to_schedule():
|
||||
responsible = activity_type.get_responsible_id(employee)['responsible']
|
||||
if self.env['hr.employee'].with_user(responsible).check_access_rights('read', raise_exception=False):
|
||||
date_deadline = self.env['mail.activity']._calculate_date_deadline(activity_type.activity_type_id)
|
||||
employee.activity_schedule(
|
||||
activity_type_id=activity_type.activity_type_id.id,
|
||||
summary=activity_type.summary,
|
||||
note=activity_type.note,
|
||||
user_id=responsible.id,
|
||||
date_deadline=date_deadline
|
||||
)
|
||||
activity = _('%(activity)s, assigned to %(name)s, due on the %(deadline)s', activity=activity_type.summary, name=responsible.name, deadline=date_deadline)
|
||||
activities.add(activity)
|
||||
|
||||
if activities:
|
||||
body += '<ul>'
|
||||
for activity in activities:
|
||||
body += '<li>%s</li>' % activity
|
||||
body += '</ul>'
|
||||
employee.message_post(body=body)
|
||||
|
||||
if len(self.employee_ids) == 1:
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.employee',
|
||||
'res_id': self.employee_ids.id,
|
||||
'name': self.employee_ids.display_name,
|
||||
'view_mode': 'form',
|
||||
'views': [(False, "form")],
|
||||
}
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.employee',
|
||||
'name': _('Launch Plans'),
|
||||
'view_mode': 'tree,form',
|
||||
'target': 'current',
|
||||
'domain': [('id', 'in', self.employee_ids.ids)],
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="plan_wizard" model="ir.ui.view">
|
||||
<field name="name">plan wizard</field>
|
||||
<field name="model">hr.plan.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="department_id" attrs="{'invisible': [('department_id', '=', False)]}"/>
|
||||
<field name="plan_id"/>
|
||||
<field name="employee_ids" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
</group>
|
||||
<div role="alert" class="alert alert-danger mb8" attrs="{'invisible': [('warning', '=', False)]}">
|
||||
<field name="warning"/>
|
||||
</div>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_launch" string="Launch Plan" type="object" class="oe_highlight" attrs="{'invisible': [('warning', '!=', False)]}" groups="hr.group_hr_user" data-hotkey="q"/>
|
||||
<button name="action_launch" string="Launch Plan" type="object" class="oe_highlight disabled" attrs="{'invisible': [('warning', '=', False)]}" groups="hr.group_hr_user" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="plan_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Launch Plan</field>
|
||||
<field name="res_model">hr.plan.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
54
odoo-bringout-oca-ocb-hr/hr/wizard/mail_activity_schedule.py
Normal file
54
odoo-bringout-oca-ocb-hr/hr/wizard/mail_activity_schedule.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.fields import Domain
|
||||
|
||||
|
||||
class MailActivitySchedule(models.TransientModel):
|
||||
_inherit = 'mail.activity.schedule'
|
||||
|
||||
department_id = fields.Many2one('hr.department', compute='_compute_department_id')
|
||||
plan_department_filterable = fields.Boolean(compute='_compute_plan_department_filterable')
|
||||
|
||||
@api.depends('department_id')
|
||||
def _compute_plan_available_ids(self):
|
||||
todo = self.filtered(lambda s: s.plan_department_filterable)
|
||||
for scheduler in todo:
|
||||
domain = scheduler._get_plan_available_base_domain()
|
||||
if not scheduler.department_id:
|
||||
domain &= Domain('department_id', '=', False)
|
||||
else:
|
||||
domain &= Domain('department_id', '=', False) | Domain('department_id', '=', scheduler.department_id.id)
|
||||
scheduler.plan_available_ids = self.env['mail.activity.plan'].search(domain)
|
||||
super(MailActivitySchedule, self - todo)._compute_plan_available_ids()
|
||||
|
||||
@api.depends('res_model')
|
||||
def _compute_plan_department_filterable(self):
|
||||
for wizard in self:
|
||||
wizard.plan_department_filterable = wizard.res_model == 'hr.employee'
|
||||
|
||||
@api.depends('res_model_id', 'res_ids')
|
||||
def _compute_department_id(self):
|
||||
for wizard in self:
|
||||
if wizard.plan_department_filterable:
|
||||
applied_on = wizard._get_applied_on_records()
|
||||
all_departments = applied_on.department_id
|
||||
wizard.department_id = False if len(all_departments) > 1 else all_departments
|
||||
else:
|
||||
wizard.department_id = False
|
||||
|
||||
def _compute_plan_date(self):
|
||||
todo = self.filtered(lambda s: s.res_model == 'hr.employee')
|
||||
for scheduler in todo:
|
||||
selected_employees = scheduler._get_applied_on_records()
|
||||
start_dates = selected_employees.filtered('date_start').mapped('date_start')
|
||||
if start_dates:
|
||||
today = fields.Date.today()
|
||||
planned_due_date = min(start_dates)
|
||||
if planned_due_date < today or (planned_due_date - today).days < 30:
|
||||
scheduler.plan_date = today + relativedelta(days=+30)
|
||||
else:
|
||||
scheduler.plan_date = planned_due_date
|
||||
super(MailActivitySchedule, self - todo)._compute_plan_date()
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="mail_activity_schedule_view_form" model="ir.ui.view">
|
||||
<field name="name">mail.activity.schedule.view.form.inherit.hr</field>
|
||||
<field name="model">mail.activity.schedule</field>
|
||||
<field name="inherit_id" ref="mail.mail_activity_schedule_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='plan_available_ids']" position="after">
|
||||
<field name="department_id" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
Loading…
Add table
Add a link
Reference in a new issue