[IMP] hr_payroll: improve general usability
This commit improves general usability.
Specification
=============
Screens usability:
1/ Benefits
1.1/ Calendar
- Add a sidebar filter to filter employees. The filters are saved per user.
If no filter exists for the user the 'Everybody' filter should be true by default.
- Generate benefits when clicking on the menu -> remove the wizard (popup): generate benefits for all running contracts
- Once generated, don't show again the generate benefits button: only show the Validate button
- Generate Payslips button: remove the wizard. It generates payslip of validated benefits
and jumps to the payslip batch form view.
1.2/ Form:
- rename: Start into "From"
- rename: End into "To"
- rename: Hours into "Period" (add 'hours' as unit on the right of the field)
2/ Payslip
2.1/ Form:
- Remove Payslip stat button.
- a computed payslip should be editable
2.2/ List:
- show: Reference | Employee | Batch Name | From | To | Gross Salary | Net Salary | Status
- In action buttons add a "Confirm" to confirm multiple payslips
- add a quick search on contract
- filters: rename 'draft' into "To Compute"
- filter: add "To Confirm"
Menus:
1/ Salary Computation
- Benefits (object: hr.benefit ; view: calendar, list, form, pivot. Group: hr officer ; filters: group by employee in the view)
- Payslips (objetc: hr.payslip ; Views: list, form, pivot. Group: hr officer ; filters: last batch ; Sort: employee name alphabetically)
2/ Employee Payslips
- Batches (objetc: hr.payslip.run ; Views: list, form. Group: hr officer ; Sort: creation date (last one first)
- All Payslips (objetc: hr.payslip ; Views: list, form, pivot. Group: hr officer ; filters: no filters; Sort: employee name alphabetically)
3/ Configuration
- Settings
---Salary---
- Rules
- Structure
- Contributions Registers
---Benefits---
- Benefits Type
---Repository---
- Employees (list)
- Contracts
When a benefit linked to a leave is cancelled, it should refuse the leave.
When a payslip batch is deleted, also delete associated payslips.
+ Refactoring of benefit js to use "Odoo style" event binding.
TaskID: 1916136
closes odoo/odoo#30110
This commit is contained in:
@@ -36,15 +36,18 @@ class Employee(models.Model):
|
||||
"""
|
||||
Returns the contracts of the employee between date_from and date_to
|
||||
"""
|
||||
self.ensure_one()
|
||||
# a contract is valid if it ends between the given dates
|
||||
if not self:
|
||||
employees = self.env['hr.employee'].search([])
|
||||
else:
|
||||
employees = self
|
||||
clause_1 = ['&', ('date_end', '<=', date_to), ('date_end', '>=', date_from)]
|
||||
# OR if it starts between the given dates
|
||||
clause_2 = ['&', ('date_start', '<=', date_to), ('date_start', '>=', date_from)]
|
||||
# OR if it starts before the date_from and finish after the date_end (or never finish)
|
||||
clause_3 = ['&', ('date_start', '<=', date_from), '|', ('date_end', '=', False), ('date_end', '>=', date_to)]
|
||||
clause_final = expression.AND([
|
||||
[('employee_id', '=', self.id), ('state', '=', 'open')],
|
||||
[('employee_id', 'in', employees.ids), ('state', '=', 'open')],
|
||||
expression.OR([clause_1, clause_2, clause_3])])
|
||||
return self.env['hr.contract'].search(clause_final)
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id" operator="child_of"/>
|
||||
<field name="state"/>
|
||||
<filter string="Running" name="running" domain="[('state', '=', 'open')]"/>
|
||||
<filter string="To Renew" name="to_renew" domain="[('state', '=', 'pending')]"/>
|
||||
<separator />
|
||||
<filter string="Current Employee" name="current_employee" domain="[('employee_id.active','=',True)]"/>
|
||||
@@ -151,7 +152,11 @@
|
||||
<field name="name">hr.contract.tree</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Contracts" decoration-bf="message_needaction == True">
|
||||
<tree string="Contracts"
|
||||
decoration-bf="message_needaction == True"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-muted="state in ('close', 'cancel')"
|
||||
decoration-danger="state == 'pending'">
|
||||
<field name="name"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="job_id"/>
|
||||
@@ -159,6 +164,7 @@
|
||||
<field name="resource_calendar_id"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="state"/>
|
||||
<field name="wage" invisible="1"/>
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
</tree>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
'sequence': 38,
|
||||
'summary': 'Manage your employee payroll records',
|
||||
'description': "",
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'depends': [
|
||||
'hr_contract',
|
||||
'hr_holidays',
|
||||
@@ -32,7 +34,7 @@
|
||||
'views/hr_leave_views.xml',
|
||||
'views/resource_views.xml',
|
||||
'views/hr_benefit_template.xml',
|
||||
'wizard/hr_benefit_employee_views.xml',
|
||||
'views/hr_payroll_menu.xml',
|
||||
],
|
||||
'demo': ['data/hr_payroll_demo.xml'],
|
||||
'qweb': [
|
||||
|
||||
@@ -18,9 +18,9 @@ class HrBenefit(models.Model):
|
||||
active = fields.Boolean(default=True)
|
||||
employee_id = fields.Many2one('hr.employee', required=True,
|
||||
domain=lambda self: [('contract_ids.state', 'in', ('open', 'pending')), ('company_id', '=', self.env.user.company_id.id)])
|
||||
date_start = fields.Datetime(required=True, string='Start')
|
||||
date_stop = fields.Datetime(string='End')
|
||||
duration = fields.Float(compute='_compute_duration', inverse='_inverse_duration', store=True, string="Hours")
|
||||
date_start = fields.Datetime(required=True, string='From')
|
||||
date_stop = fields.Datetime(string='To')
|
||||
duration = fields.Float(compute='_compute_duration', inverse='_inverse_duration', store=True, string="Period")
|
||||
benefit_type_id = fields.Many2one('hr.benefit.type')
|
||||
color = fields.Integer(related='benefit_type_id.color', readonly=True)
|
||||
state = fields.Selection([
|
||||
@@ -73,6 +73,7 @@ class HrBenefit(models.Model):
|
||||
vals['active'] = True
|
||||
if vals['state'] == 'cancelled':
|
||||
vals['active'] = False
|
||||
self.mapped('leave_id').action_refuse()
|
||||
return super(HrBenefit, self).write(vals)
|
||||
|
||||
@api.multi
|
||||
@@ -283,3 +284,16 @@ class HrBenefitType(models.Model):
|
||||
help="If the active field is set to false, it will allow you to hide the benefit type without removing it.")
|
||||
is_leave = fields.Boolean(default=False, string="Leave")
|
||||
|
||||
class Contacts(models.Model):
|
||||
""" Personnal calendar filter """
|
||||
|
||||
_name = 'hr.user.benefit.employee'
|
||||
_description = 'Benefits Employees'
|
||||
|
||||
user_id = fields.Many2one('res.users', 'Me', required=True, default=lambda self: self.env.user)
|
||||
employee_id = fields.Many2one('hr.employee', 'Employee', required=True)
|
||||
active = fields.Boolean('Active', default=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('user_id_employee_id_unique', 'UNIQUE(user_id,employee_id)', 'You cannot have twice the same employee.')
|
||||
]
|
||||
|
||||
@@ -25,15 +25,36 @@ class HrEmployee(models.Model):
|
||||
for employee in self:
|
||||
employee.payslip_count = len(employee.slip_ids)
|
||||
|
||||
def has_non_validated_benefits(self, date_from, date_to):
|
||||
return bool(self.env['hr.benefit'].search_count([
|
||||
('employee_id', 'in', self.ids),
|
||||
('date_start', '<=', date_to),
|
||||
('date_stop', '>=', date_from),
|
||||
('state', 'in', ['draft', 'confirmed'])
|
||||
]))
|
||||
|
||||
@api.multi
|
||||
|
||||
|
||||
|
||||
@api.model
|
||||
def generate_benefit(self, date_start, date_stop):
|
||||
|
||||
date_start = date_start.replace(tzinfo=pytz.utc)
|
||||
date_stop = date_stop.replace(tzinfo=pytz.utc)
|
||||
def _format_datetime(date):
|
||||
fmt = '%Y-%m-%d %H:%M:%S'
|
||||
date = datetime.strptime(date, fmt) if isinstance(date, str) else date
|
||||
return date.replace(tzinfo=pytz.utc) if not date.tzinfo else date
|
||||
|
||||
for employee in self:
|
||||
date_start = _format_datetime(date_start)
|
||||
date_stop = _format_datetime(date_stop)
|
||||
|
||||
current_contracts = self.env['hr.employee']._get_contracts(date_start, date_stop)
|
||||
current_employees = current_contracts.mapped('employee_id')
|
||||
mapped_data = dict.fromkeys(current_employees, self.env['hr.contract'])
|
||||
|
||||
for contract in current_contracts:
|
||||
mapped_data[contract.employee_id] |= contract
|
||||
|
||||
for employee, contracts in mapped_data.items():
|
||||
# Approved leaves
|
||||
emp_leaves = employee.resource_calendar_id.leave_ids.filtered(
|
||||
lambda r:
|
||||
@@ -47,7 +68,7 @@ class HrEmployee(models.Model):
|
||||
hr_leave.copy_to_benefits()
|
||||
|
||||
new_benefits = self.env['hr.benefit']
|
||||
for contract in employee._get_contracts(date_start, date_stop):
|
||||
for contract in contracts:
|
||||
|
||||
date_start_benefits = max(date_start, datetime.combine(contract.date_start, datetime.min.time()).replace(tzinfo=pytz.utc))
|
||||
date_stop_benefits = min(date_stop, datetime.combine(contract.date_end or datetime.max.date(), datetime.max.time()).replace(tzinfo=pytz.utc))
|
||||
|
||||
@@ -69,5 +69,5 @@ class HrLeave(models.Model):
|
||||
def action_refuse(self):
|
||||
super(HrLeave, self).action_refuse()
|
||||
benefits = self.env['hr.benefit'].search([('leave_id', 'in', self.ids)])
|
||||
benefits.write({'display_warning': False, 'leave_id': None})
|
||||
benefits.write({'display_warning': False, 'active': False})
|
||||
return True
|
||||
|
||||
@@ -18,22 +18,22 @@ class HrPayslip(models.Model):
|
||||
_description = 'Pay Slip'
|
||||
|
||||
struct_id = fields.Many2one('hr.payroll.structure', string='Structure',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
readonly=True, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]},
|
||||
help='Defines the rules that have to be applied to this payslip, accordingly '
|
||||
'to the contract chosen. If you let empty the field contract, this field isn\'t '
|
||||
'mandatory anymore and thus the rules applied will be all the rules set on the '
|
||||
'structure of all contracts of the employee valid for the chosen period')
|
||||
name = fields.Char(string='Payslip Name', readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
number = fields.Char(string='Reference', readonly=True, copy=False,
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
employee_id = fields.Many2one('hr.employee', string='Employee', required=True, readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
date_from = fields.Date(string='Date From', readonly=True, required=True,
|
||||
default=lambda self: fields.Date.to_string(date.today().replace(day=1)), states={'draft': [('readonly', False)]})
|
||||
date_to = fields.Date(string='Date To', readonly=True, required=True,
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
date_from = fields.Date(string='From', readonly=True, required=True,
|
||||
default=lambda self: fields.Date.to_string(date.today().replace(day=1)), states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
date_to = fields.Date(string='To', readonly=True, required=True,
|
||||
default=lambda self: fields.Date.to_string((datetime.now() + relativedelta(months=+1, day=1, days=-1)).date()),
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
# this is chaos: 4 states are defined, 3 are used ('verify' isn't) and 5 exist ('confirm' seems to have existed)
|
||||
state = fields.Selection([
|
||||
('draft', 'Draft'),
|
||||
@@ -46,32 +46,36 @@ class HrPayslip(models.Model):
|
||||
\n* If the payslip is confirmed then status is set to \'Done\'.
|
||||
\n* When user cancel payslip the status is \'Rejected\'.""")
|
||||
line_ids = fields.One2many('hr.payslip.line', 'slip_id', string='Payslip Lines', readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True, copy=False,
|
||||
default=lambda self: self.env['res.company']._company_default_get(),
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
worked_days_line_ids = fields.One2many('hr.payslip.worked_days', 'payslip_id',
|
||||
string='Payslip Worked Days', copy=True, readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
input_line_ids = fields.One2many('hr.payslip.input', 'payslip_id', string='Payslip Inputs',
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
readonly=True, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
paid = fields.Boolean(string='Made Payment Order ? ', readonly=True, copy=False,
|
||||
states={'draft': [('readonly', False)]})
|
||||
note = fields.Text(string='Internal Note', readonly=True, states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
note = fields.Text(string='Internal Note', readonly=True, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
contract_id = fields.Many2one('hr.contract', string='Contract', readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
|
||||
credit_note = fields.Boolean(string='Credit Note', readonly=True,
|
||||
states={'draft': [('readonly', False)]},
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]},
|
||||
help="Indicates this payslip has a refund of another")
|
||||
payslip_run_id = fields.Many2one('hr.payslip.run', string='Payslip Batches', readonly=True,
|
||||
copy=False, states={'draft': [('readonly', False)]})
|
||||
payslip_count = fields.Integer(compute='_compute_payslip_count', string="Payslip Computation Details")
|
||||
payslip_run_id = fields.Many2one('hr.payslip.run', string='Batche Name', readonly=True,
|
||||
copy=False, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]}, ondelete='cascade')
|
||||
compute_date = fields.Date('Computed On')
|
||||
|
||||
basic_wage = fields.Monetary(compute='_compute_basic_net')
|
||||
net_wage = fields.Monetary(compute='_compute_basic_net')
|
||||
currency_id = fields.Many2one(related='contract_id.currency_id')
|
||||
|
||||
@api.multi
|
||||
def _compute_payslip_count(self):
|
||||
def _compute_basic_net(self):
|
||||
for payslip in self:
|
||||
payslip.payslip_count = len(payslip.line_ids)
|
||||
payslip.basic_wage = payslip.get_salary_line_total('BASIC')
|
||||
payslip.net_wage = payslip.get_salary_line_total('NET')
|
||||
|
||||
@api.constrains('date_from', 'date_to')
|
||||
def _check_dates(self):
|
||||
|
||||
@@ -44,5 +44,16 @@
|
||||
<field name="perm_read" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_user_benefit_employee" model="ir.rule">
|
||||
<field name="name">Benefits/Employee calendar filter: only self</field>
|
||||
<field name="model_id" ref="model_hr_user_benefit_employee"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -11,3 +11,4 @@ access_hr_payslip_run,hr.payslip.run,model_hr_payslip_run,hr_payroll.group_hr_pa
|
||||
access_hr_salary_rule_user,hr.salary.rule.user,model_hr_salary_rule,hr_payroll.group_hr_payroll_user,1,1,1,1
|
||||
access_hr_benefit,access_hr_benefit,model_hr_benefit,group_hr_payroll_user,1,1,1,1
|
||||
access_hr_benefit_type,access_hr_benefit_type,model_hr_benefit_type,group_hr_payroll_user,1,1,1,1
|
||||
access_hr_benefit_employee,hr.benefit.employee,model_hr_user_benefit_employee,group_hr_payroll_user,1,1,1,1
|
||||
|
||||
|
@@ -3,9 +3,19 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) {
|
||||
|
||||
var core = require('web.core');
|
||||
var CalendarController = require("web.CalendarController");
|
||||
var CalendarModel = require('web.CalendarModel');
|
||||
var CalendarRenderer = require('web.CalendarRenderer');
|
||||
var CalendarView = require('web.CalendarView');
|
||||
var viewRegistry = require('web.view_registry');
|
||||
|
||||
var _t = core._t;
|
||||
CalendarController.include({
|
||||
var BenefitCalendarController = CalendarController.extend({
|
||||
|
||||
events: {
|
||||
'click .btn-benefit-generate': '_onGenerateBenefits',
|
||||
'click .btn-benefit-validate': '_onValidateBenefits',
|
||||
'click .btn-payslip-generate': '_onGeneratePayslips',
|
||||
},
|
||||
|
||||
update: function () {
|
||||
var self = this;
|
||||
@@ -14,35 +24,9 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) {
|
||||
});
|
||||
},
|
||||
|
||||
_renderGenerateButton: function(date_from, date_to, employee_ids, secondary) {
|
||||
var self = this;
|
||||
var primary = !secondary ? 'btn-primary' : 'btn-secondary';
|
||||
var txt = _t("Generate Benefits");
|
||||
_renderBenefitButton: function (text, event_class) {
|
||||
this.$buttons.find('.o_calendar_button_month').after(
|
||||
$('<button class="btn ' + primary + ' btn-benefit" type="button">'+ txt +'</button>')
|
||||
.off('click')
|
||||
.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
var date_fmt = 'YYYY-MM-DD HH:mm:ss';
|
||||
var options = {
|
||||
on_close: function () {
|
||||
self.reload();
|
||||
},
|
||||
};
|
||||
self.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
name: txt,
|
||||
res_model: 'hr.benefit.employees',
|
||||
view_type: 'form',
|
||||
views: [[false,'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
'start_benefits': date_from.format(date_fmt),
|
||||
'stop_benefits':date_to.format(date_fmt),
|
||||
},
|
||||
}, options);
|
||||
})
|
||||
$('<button class="btn btn-primary btn-benefit ' + event_class + '" type="button">'+ _t(text) +'</button>')
|
||||
);
|
||||
},
|
||||
|
||||
@@ -51,58 +35,59 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
var firstDay = this.model.data.target_date.clone().startOf('month');
|
||||
var lastDay = this.model.data.target_date.clone().endOf('month');
|
||||
var events = this._checkDataInRange(firstDay, lastDay, this.model.data.data);
|
||||
var is_validated = this._checkValidation(events);
|
||||
this.firstDay = this.model.data.target_date.clone().startOf('month');
|
||||
this.lastDay = this.model.data.target_date.clone().endOf('month');
|
||||
this.events = this._checkDataInRange(this.firstDay, this.lastDay, this.model.data.data);
|
||||
var is_validated = this._checkValidation(this.events);
|
||||
this.$buttons.find('.btn-benefit').remove();
|
||||
var employee_ids = _.map(events, function (event) { return event.record.employee_id[0]; });
|
||||
employee_ids = _.uniq(employee_ids);
|
||||
if (this.events.length === 0) {
|
||||
this._renderBenefitButton("Generate Benefits", 'btn-benefit-generate');
|
||||
}
|
||||
if (is_validated && this.events.length !== 0) {
|
||||
this._renderBenefitButton("Generate Payslips", 'btn-payslip-generate');
|
||||
}
|
||||
else if (!is_validated) {
|
||||
this._renderBenefitButton("Validate Benefits", 'btn-benefit-validate');
|
||||
}
|
||||
},
|
||||
|
||||
_onGeneratePayslips: function (e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
var date_fmt = 'YYYY-MM-DD';
|
||||
this.do_action('hr_payroll.action_generate_payslips_from_benefits', {
|
||||
additional_context: {
|
||||
default_date_start: this.firstDay.format(date_fmt),
|
||||
default_date_end: this.lastDay.format(date_fmt),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_onValidateBenefits: function (e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
var self = this;
|
||||
if (this.model.data.domain.length !== 0) { // select by default the employee in the domain
|
||||
var employee_search_id = (this.model.data.domain[0][0] === 'employee_id' && this.model.data.domain[0][1] === '=')? [this.model.data.domain[0][2]]: null;
|
||||
}
|
||||
if (events.length === 0) { // Generate button
|
||||
this._renderGenerateButton(firstDay, lastDay, employee_search_id);
|
||||
} else {
|
||||
this._renderGenerateButton(firstDay, lastDay, employee_ids, true);
|
||||
}
|
||||
if (is_validated && events.length !== 0) { // Generate Payslip button
|
||||
this.$buttons.find('.o_calendar_button_month').after(
|
||||
$('<button class="btn btn-primary btn-benefit" type="button">'+ _t('Generate Payslips') +'</button>')
|
||||
.off('click')
|
||||
// action_hr_payslip_by_employees
|
||||
.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
var date_fmt = 'YYYY-MM-DD';
|
||||
self.do_action('hr_payroll.action_hr_payslip_by_employees', {
|
||||
additional_context: {
|
||||
default_employee_ids: employee_ids || [],
|
||||
default_date_start: firstDay.format(date_fmt),
|
||||
default_date_end: lastDay.format(date_fmt),
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
else if (!is_validated) { // Validate button
|
||||
this.$buttons.find('.o_calendar_button_month').after(
|
||||
$('<button class="btn btn-primary btn-benefit" type="button">'+ _t('Validate Benefits') +'</button>')
|
||||
.off('click')
|
||||
.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
self._rpc({
|
||||
model: 'hr.benefit',
|
||||
method: 'action_validate',
|
||||
args: [_.map(events, function (event) { return event.record.id; })],
|
||||
}).then(function () {
|
||||
return self.reload();
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
this._rpc({
|
||||
model: 'hr.benefit',
|
||||
method: 'action_validate',
|
||||
args: [_.map(this.events, function (event) { return event.record.id; })],
|
||||
}).then(function () {
|
||||
return self.reload();
|
||||
});
|
||||
},
|
||||
|
||||
_onGenerateBenefits: function (e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
var date_fmt = 'YYYY-MM-DD HH:mm:ss';
|
||||
var self = this;
|
||||
this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'generate_benefit',
|
||||
args: [this.firstDay.format(date_fmt), this.lastDay.format(date_fmt)],
|
||||
}).then(function () {
|
||||
self.reload();
|
||||
});
|
||||
},
|
||||
|
||||
_checkDataInRange: function (firstDay, lastDay, events) {
|
||||
@@ -125,4 +110,41 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) {
|
||||
this._renderBenefitButtons();
|
||||
},
|
||||
});
|
||||
|
||||
var BenefitCalendarModel = CalendarModel.extend({
|
||||
/**
|
||||
* Display everybody's benefits if no employee filter exists
|
||||
* @private
|
||||
* @override
|
||||
* @param {any} filter
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_loadFilter: function (filter) {
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
var filters = filter.filters;
|
||||
var all_filter = filters[filters.length - 1];
|
||||
|
||||
if (all_filter) {
|
||||
all_filter.label = _t("Everybody's benefits");
|
||||
|
||||
if (filter.write_model && filter.filters.length <= 1 && all_filter.active === undefined) {
|
||||
filter.all = true;
|
||||
all_filter.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var BenefitCalendarView = CalendarView.extend({
|
||||
config: _.extend({}, CalendarView.prototype.config, {
|
||||
Controller: BenefitCalendarController,
|
||||
Model: BenefitCalendarModel,
|
||||
Renderer: CalendarRenderer,
|
||||
}),
|
||||
});
|
||||
|
||||
viewRegistry.add('benefits_calendar', BenefitCalendarView);
|
||||
|
||||
});
|
||||
@@ -27,8 +27,6 @@ class TestPayslipFlow(TestPayslipBase):
|
||||
context = {
|
||||
"lang": "en_US", "tz": False, "active_model": "ir.ui.menu",
|
||||
"department_id": False, "section_id": False,
|
||||
"active_ids": [self.ref("hr_payroll.menu_department_tree")],
|
||||
"active_id": self.ref("hr_payroll.menu_department_tree")
|
||||
}
|
||||
# I click on 'Compute Sheet' button on payslip
|
||||
richard_payslip.with_context(context).compute_sheet()
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record model="ir.actions.server" id="generate_benefit">
|
||||
<field name="name">Generate Benefit</field>
|
||||
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||
<field name="binding_model_id" ref="hr.model_hr_employee"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
if records:
|
||||
records.generate_benefit()
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_benefit_action" model="ir.actions.act_window">
|
||||
<field name="name">Benefit</field>
|
||||
<field name="res_model">hr.benefit</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">calendar,tree,form</field>
|
||||
<field name="view_mode">calendar,tree,form,pivot</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_benefit_calendar" model="ir.ui.view">
|
||||
@@ -24,6 +13,7 @@ if records:
|
||||
<field name="model">hr.benefit</field>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Benefit"
|
||||
js_class="benefits_calendar"
|
||||
date_start="date_start"
|
||||
date_stop="date_stop"
|
||||
mode="month"
|
||||
@@ -31,14 +21,13 @@ if records:
|
||||
color="benefit_type_id"
|
||||
event_limit="5">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="employee_id" invisible="1"/>
|
||||
<field name="display_warning" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<!-- Sidebar favorites filters -->
|
||||
<field name="employee_id" write_model="hr.user.benefit.employee" write_field="employee_id" avatar_field="image_small"/>
|
||||
<templates>
|
||||
<t t-name="calendar-box">
|
||||
<t t-set="color" t-value="widget.getColor(event.color_index)"/>
|
||||
<div t-att-style="typeof color === 'string' ? ('background-color:'+color)+';' : ''" t-attf-class="#{record.is_highlighted && record.is_highlighted.value ? 'o_event_hightlight' : ''} #{typeof color === 'number' ? 'o_calendar_color_'+color : ''}">
|
||||
<field name="name"/>
|
||||
<div class="fc-time"/>
|
||||
<div class="o_fields">
|
||||
<t t-if="record.display_warning">
|
||||
@@ -99,7 +88,10 @@ if records:
|
||||
<group>
|
||||
<field name="date_start" attrs="{'readonly': [('state', '!=', 'draft')]}" />
|
||||
<field name="date_stop" attrs="{'readonly': [('state', '!=', 'draft')]}" />
|
||||
<field name="duration" attrs="{'readonly': [('state', '!=', 'draft')]}" />
|
||||
<label for="duration" string="Period"/>
|
||||
<div>
|
||||
<field name="duration" nolabel="1" attrs="{'readonly': [('state', '!=', 'draft')]}" /><span class="ml8">Hours</span>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
@@ -115,6 +107,8 @@ if records:
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
<filter string="My Benefits" domain="[('employee_id.user_id','=',uid)]" name="my_benefits"/>
|
||||
<filter string="With error" domain="[('display_warning','=', True)]" name="benefits_error"/>
|
||||
<filter name="date_filter" string="Date" date="date_start"/>
|
||||
<filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
|
||||
</search>
|
||||
</field>
|
||||
@@ -145,19 +139,20 @@ if records:
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="hr_menu_benefit_menu" name="Benefits" action="hr_benefit_action" parent="menu_hr_payroll_root" sequence="25"/>
|
||||
<record model="ir.actions.server" id="action_generate_payslips_from_benefits">
|
||||
<field name="name">Generate payslips</field>
|
||||
<field name="model_id" ref="model_hr_payslip_employees"/>
|
||||
<field name="binding_model_id" ref="model_hr_payslip_employees" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = env['hr.payslip.employees'].create({}).compute_sheet()
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_hr_benefit_type" model="ir.actions.act_window">
|
||||
<field name="name">Benefit Types</field>
|
||||
<field name="res_model">hr.benefit.type</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_hr_benefit_type_view"
|
||||
action="action_view_hr_benefit_type"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="25"
|
||||
/>
|
||||
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Root Menus -->
|
||||
|
||||
<menuitem id="menu_hr_payroll_root" name="Payroll" sequence="45" web_icon="hr_payroll,static/description/icon.png"/>
|
||||
<menuitem id="menu_hr_payroll_configuration" name="Configuration" parent="menu_hr_payroll_root"
|
||||
sequence="100" groups="hr_payroll.group_hr_payroll_manager"/>
|
||||
|
||||
<!-- Contract View -->
|
||||
<record id="hr_contract_form_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.view.form.inherit</field>
|
||||
@@ -24,4 +18,19 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_hr_contract_repository" model="ir.actions.act_window">
|
||||
<field name="name">Contracts</field>
|
||||
<field name="res_model">hr.contract</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form,activity</field>
|
||||
<field name="domain">[('employee_id', '!=', False)]</field>
|
||||
<field name="context">{'search_default_running': 1, 'search_default_to_renew': 1}</field>
|
||||
<field name="search_view_id" ref="hr_contract.hr_contract_view_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new contract
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version='1.0' encoding='UTF-8' ?>
|
||||
<odoo>
|
||||
|
||||
<!-- Root Menus -->
|
||||
|
||||
<menuitem id="menu_hr_payroll_root" name="Payroll" sequence="45" web_icon="hr_payroll,static/description/icon.png"/>
|
||||
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_salary_computation"
|
||||
name="Salary Computation"
|
||||
parent="menu_hr_payroll_root"
|
||||
sequence="50"
|
||||
groups="hr_payroll.group_hr_payroll_manager"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_payslips"
|
||||
name="Employee Payslips"
|
||||
parent="menu_hr_payroll_root"
|
||||
sequence="60"
|
||||
groups="hr_payroll.group_hr_payroll_manager"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_configuration"
|
||||
name="Configuration"
|
||||
parent="menu_hr_payroll_root"
|
||||
sequence="100"
|
||||
groups="hr_payroll.group_hr_payroll_manager"/>
|
||||
|
||||
<!-- **** Salary Computation **** -->
|
||||
<menuitem
|
||||
id="menu_hr_payroll_employee_payslips"
|
||||
name="Payslips"
|
||||
parent="menu_hr_payroll_salary_computation"
|
||||
sequence="60"
|
||||
action="action_view_hr_payslip_month_form"
|
||||
groups="hr_payroll.group_hr_payroll_manager"/>
|
||||
<!-- TODO add filter-->
|
||||
|
||||
<menuitem
|
||||
id="hr_menu_benefit_menu"
|
||||
name="Benefits"
|
||||
action="hr_benefit_action"
|
||||
parent="menu_hr_payroll_salary_computation"
|
||||
sequence="25"/>
|
||||
|
||||
<!-- **** Employee Payslips **** -->
|
||||
<menuitem
|
||||
id="menu_hr_payslip_run"
|
||||
action="action_hr_payslip_run_tree"
|
||||
name="Batches"
|
||||
parent="menu_hr_payroll_payslips"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_employee_all_payslips"
|
||||
name="All Payslips"
|
||||
parent="menu_hr_payroll_payslips"
|
||||
sequence="60"
|
||||
action="action_view_hr_payslip_form"
|
||||
groups="hr_payroll.group_hr_payroll_manager"/>
|
||||
|
||||
<!-- **** Configuration **** -->
|
||||
<menuitem
|
||||
id="menu_hr_payroll_global_settings"
|
||||
name="Settings"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="0"
|
||||
action="action_hr_payroll_configuration"
|
||||
groups="base.group_system"/>
|
||||
|
||||
<!-- Benefits Configuration -->
|
||||
<menuitem
|
||||
id="menu_hr_benefit_confirguration"
|
||||
name="Benefits"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="50"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_hr_benefit_type_view"
|
||||
action="action_view_hr_benefit_type"
|
||||
parent="menu_hr_benefit_confirguration"
|
||||
/>
|
||||
|
||||
<!-- Salary Configuration -->
|
||||
<menuitem
|
||||
id="menu_hr_salary_configuration"
|
||||
name="Salary"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="30"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_action_hr_salary_rule_form"
|
||||
action="action_salary_rule_form"
|
||||
name="Rules"
|
||||
parent="menu_hr_salary_configuration"
|
||||
sequence="10"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_salary_rule_category"
|
||||
action="action_hr_salary_rule_category"
|
||||
name="Rule Categories"
|
||||
parent="menu_hr_salary_configuration"
|
||||
sequence="20"
|
||||
groups="base.group_no_one"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_structure_view"
|
||||
action="action_view_hr_payroll_structure_list_form"
|
||||
name="Structures"
|
||||
parent="menu_hr_salary_configuration"
|
||||
sequence="30"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_action_hr_contribution_register_form"
|
||||
action="action_contribution_register_form"
|
||||
parent="menu_hr_salary_configuration"
|
||||
sequence="50"
|
||||
/>
|
||||
|
||||
<!-- Repository Configuration -->
|
||||
<menuitem
|
||||
id="menu_hr_repository_confirguration"
|
||||
name="Repository"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="50"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_employees_configuration"
|
||||
action="hr.open_view_employee_list_my"
|
||||
parent="menu_hr_repository_confirguration"
|
||||
sequence="10"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_payroll_contracts_configuration"
|
||||
action="hr_payroll.action_hr_contract_repository"
|
||||
parent="menu_hr_repository_confirguration"
|
||||
sequence="20"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
</odoo>
|
||||
@@ -71,14 +71,16 @@
|
||||
<field name="model">hr.payslip</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-info="state in ('confirm','verify')" decoration-muted="state == 'cancel'" js_class="hr_payroll_payslip_tree" string="Payslips">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="number"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
<field name="payslip_run_id"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="basic_wage" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="net_wage" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="state"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
<field name="payslip_run_id" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -137,14 +139,6 @@
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(act_payslip_lines)d"
|
||||
class="oe_stat_button"
|
||||
icon="fa-money"
|
||||
type="action">
|
||||
<field name="payslip_count" widget="statinfo" string="Payslip" help="Payslip Computation Details"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="employee_id" class="oe_edit_only"/>
|
||||
<h1><field name="employee_id" placeholder="Employee"/></h1>
|
||||
@@ -154,9 +148,9 @@
|
||||
<div>
|
||||
<field name="date_from" class="oe_inline"/> - <field name="date_to" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="contract_id" domain="[('employee_id','=',employee_id),('date_start','<=',date_to),'|',('date_end','>=',date_from),('date_end','=',False)]" context="{'default_employee_id': employee_id}"/>
|
||||
<field name="contract_id" domain="[('employee_id','=',employee_id),('date_start','<=',date_to),'|',('date_end','>=',date_from),('date_end','=',False)]" context="{'default_employee_id': employee_id}" required="1"/>
|
||||
<field name="number"/>
|
||||
<field name="struct_id" attrs="{'required':[('contract_id','!=',False)]}"/>
|
||||
<field name="struct_id" required="1"/>
|
||||
<field name="name"/>
|
||||
<field name="credit_note" invisible="1"/>
|
||||
</group>
|
||||
@@ -259,9 +253,12 @@
|
||||
<field name="employee_id"/>
|
||||
<field name="name" string="Payslips" filter_domain="['|',('name','ilike',self),('number','ilike',self)]"/>
|
||||
<field name="date_from"/>
|
||||
<field name="contract_id"/>
|
||||
<field name="payslip_run_id"/>
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]" help="Draft Slip"/>
|
||||
<filter string="To Compute" name="draft" domain="[('state','=','draft')]" help="Draft Slip"/>
|
||||
<filter string="To Confirm" name="draft" domain="[('state','=','verify')]" help="Slips to Confirm"/>
|
||||
<filter string="Done" name="done" domain="[('state','=','done')]" help="Done Slip"/>
|
||||
<filter name="date_filter" string="Date" date="date_to" default_period="last_month"/>
|
||||
<separator/>
|
||||
<filter string="Credit Notes" name="credit_note" domain="[('credit_note', '=', True)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
@@ -277,12 +274,18 @@
|
||||
<field name="name">Employee Payslips</field>
|
||||
<field name="res_model">hr.payslip</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_mode">tree,kanban,form,pivot</field>
|
||||
<field name="search_view_id" ref="view_hr_payslip_filter"/>
|
||||
<field name="context">{'search_default_group_by_batch': 1}</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_view_hr_payslip_form" id="menu_department_tree" parent="menu_hr_payroll_root" groups="hr_payroll.group_hr_payroll_user"/>
|
||||
<record id="action_view_hr_payslip_month_form" model="ir.actions.act_window">
|
||||
<field name="name">Employee Payslips</field>
|
||||
<field name="res_model">hr.payslip</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="search_view_id" ref="view_hr_payslip_filter"/>
|
||||
<field name="context">{"search_default_group_by_batch":1}</field>
|
||||
</record>
|
||||
|
||||
<record id="act_hr_employee_payslip_list" model="ir.actions.act_window">
|
||||
<field name="res_model">hr.payslip</field>
|
||||
@@ -398,7 +401,17 @@
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="search_view_id" ref="hr_payslip_run_filter"/>
|
||||
</record>
|
||||
<menuitem action="action_hr_payslip_run_tree" id="menu_hr_payslip_run" parent="menu_hr_payroll_root"/>
|
||||
|
||||
<record model="ir.actions.server" id="action_hr_payroll_confirm_payroll">
|
||||
<field name="name">Confirm</field>
|
||||
<field name="model_id" ref="hr_payroll.model_hr_payslip"/>
|
||||
<field name="binding_model_id" ref="hr_payroll.model_hr_payslip"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
if records:
|
||||
records.action_payslip_done()
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Shortcuts -->
|
||||
|
||||
|
||||
@@ -100,12 +100,7 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_hr_payroll_structure_view"
|
||||
action="action_view_hr_payroll_structure_list_form"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="2"
|
||||
/>
|
||||
|
||||
<!-- Salary Category-->
|
||||
<record id="hr_salary_rule_category_form" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.category.form</field>
|
||||
@@ -155,13 +150,7 @@
|
||||
<field name="view_id" ref="hr_salary_rule_category_tree"/>
|
||||
<field name="search_view_id" ref="view_hr_salary_rule_category_filter"/>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_hr_salary_rule_category"
|
||||
action="action_hr_salary_rule_category"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="11"
|
||||
groups="base.group_no_one"
|
||||
/>
|
||||
|
||||
|
||||
<!--
|
||||
Contribution Register
|
||||
@@ -239,12 +228,6 @@
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_action_hr_contribution_register_form"
|
||||
action="action_contribution_register_form"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="14"
|
||||
/>
|
||||
|
||||
<!-- Salary Rules -->
|
||||
|
||||
@@ -380,8 +363,6 @@
|
||||
<field name="search_view_id" ref="view_hr_rule_filter"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_hr_salary_rule_form" action="action_salary_rule_form" parent="menu_hr_payroll_configuration" sequence="12"/>
|
||||
|
||||
<act_window name="All Children Rules"
|
||||
domain="[('parent_rule_id', '=', active_id)]"
|
||||
res_model="hr.salary.rule"
|
||||
|
||||
@@ -61,6 +61,4 @@
|
||||
<field name="context">{'module' : 'hr_payroll'}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_hr_payroll_global_settings" name="Settings"
|
||||
parent="menu_hr_payroll_configuration" sequence="0" action="action_hr_payroll_configuration" groups="base.group_system"/>
|
||||
</odoo>
|
||||
|
||||
@@ -3,4 +3,3 @@
|
||||
|
||||
from . import hr_payroll_payslips_by_employees
|
||||
from . import hr_payroll_contribution_register_report
|
||||
from . import hr_benefit_employee
|
||||
|
||||
@@ -1,26 +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 UserError
|
||||
|
||||
|
||||
class HrPayslipEmployees(models.TransientModel):
|
||||
_name = 'hr.benefit.employees'
|
||||
_description = 'Generate benefits for all selected employees'
|
||||
|
||||
def _get_domain(self):
|
||||
return [('contract_ids.state', 'in', ('open', 'pending')), ('company_id', '=', self.env.user.company_id.id)]
|
||||
|
||||
employee_ids = fields.Many2many('hr.employee', string='Employees', domain=lambda self: self._get_domain())
|
||||
|
||||
|
||||
def generate_benefit(self):
|
||||
if not self.employee_ids:
|
||||
raise UserError(_("You must select employee(s) to generate benefits."))
|
||||
date_start = self.env.context.get('start_benefits')
|
||||
date_stop = self.env.context.get('stop_benefits')
|
||||
|
||||
date_start = fields.Datetime.from_string(date_start)
|
||||
date_stop = fields.Datetime.from_string(date_stop)
|
||||
self.employee_ids.generate_benefit(date_start, date_stop)
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_hr_benefit_by_employees" model="ir.ui.view">
|
||||
<field name="name">hr_benefit_employees</field>
|
||||
<field name="model">hr.benefit.employees</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Benefits by Employees">
|
||||
<header>
|
||||
<button icon="fa-cogs" string="Generate" name="generate_benefit" type="object" class="oe_highlight" />
|
||||
</header>
|
||||
<group>
|
||||
<span colspan="4" nolabel="1">This wizard will generate benefits for all selected employee(s) based on attendances.</span>
|
||||
</group>
|
||||
<group colspan="4" >
|
||||
<separator string="Employees" colspan="4"/>
|
||||
<newline/>
|
||||
<field name="employee_ids" nolabel="1"/>
|
||||
</group>
|
||||
<footer></footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -11,7 +11,7 @@ class HrPayslipEmployees(models.TransientModel):
|
||||
_description = 'Generate payslips for all selected employees'
|
||||
|
||||
def _get_available_contracts_domain(self):
|
||||
return [('contract_ids.state', 'in', ('open', 'pending')), ('company_id', '=', self.env.user.company_id.id)]
|
||||
return [('contract_ids.state', 'in', ('open', 'pending', 'close')), ('company_id', '=', self.env.user.company_id.id)]
|
||||
|
||||
def _get_employees(self):
|
||||
return self.env['hr.employee'].search(self._get_available_contracts_domain())
|
||||
@@ -39,7 +39,7 @@ class HrPayslipEmployees(models.TransientModel):
|
||||
|
||||
payslips = self.env['hr.payslip']
|
||||
Payslip = self.env['hr.payslip']
|
||||
for employee in self.employee_ids:
|
||||
for employee in self.employee_ids.filtered(lambda e: not e.has_non_validated_benefits(payslip_run.date_start, payslip_run.date_end)):
|
||||
values = Payslip.default_get(Payslip.fields_get())
|
||||
values.update({
|
||||
'employee_id': employee.id,
|
||||
|
||||
@@ -27,10 +27,10 @@ class HrPayslipLine(models.Model):
|
||||
class HrPayslip(models.Model):
|
||||
_inherit = 'hr.payslip'
|
||||
|
||||
date = fields.Date('Date Account', states={'draft': [('readonly', False)]}, readonly=True,
|
||||
date = fields.Date('Date Account', states={'draft': [('readonly', False)], 'verify': [('readonly', False)]}, readonly=True,
|
||||
help="Keep empty to use the period of the validation(Payslip) date.")
|
||||
journal_id = fields.Many2one('account.journal', 'Salary Journal', readonly=True, required=True,
|
||||
states={'draft': [('readonly', False)]}, default=lambda self: self.env['account.journal'].search([('type', '=', 'general')], limit=1))
|
||||
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]}, default=lambda self: self.env['account.journal'].search([('type', '=', 'general')], limit=1))
|
||||
move_id = fields.Many2one('account.move', 'Accounting Entry', readonly=True, copy=False)
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -23,8 +23,6 @@ class TestHrPayrollAccount(common.TransactionCase):
|
||||
|
||||
self._load('account', 'test', 'account_minimal_test.xml')
|
||||
|
||||
self.payslip_action_id = self.ref('hr_payroll.menu_department_tree')
|
||||
|
||||
self.res_partner_bank = self.env['res.partner.bank'].create({
|
||||
'acc_number': '001-9876543-21',
|
||||
'partner_id': self.ref('base.res_partner_12'),
|
||||
@@ -93,7 +91,7 @@ class TestHrPayrollAccount(common.TransactionCase):
|
||||
self.assertEqual(self.hr_payslip.state, 'draft', 'State not changed!')
|
||||
|
||||
# I click on "Compute Sheet" button.
|
||||
context = {"lang": "en_US", "tz": False, "active_model": 'hr.payslip', "department_id": False, "active_ids": [self.payslip_action_id], "section_id": False, "active_id": self.payslip_action_id}
|
||||
context = {"lang": "en_US", "tz": False, "active_model": 'hr.payslip', "department_id": False, "section_id": False}
|
||||
self.hr_payslip.with_context(context).compute_sheet()
|
||||
|
||||
# I want to check cancel button. So I first cancel the sheet then make it set to draft.
|
||||
|
||||
@@ -481,7 +481,7 @@ return AbstractModel.extend({
|
||||
*/
|
||||
_loadFilter: function (filter) {
|
||||
if (!filter.write_model) {
|
||||
return;
|
||||
return $.when();
|
||||
}
|
||||
|
||||
var field = this.fields[filter.fieldName];
|
||||
|
||||
@@ -112,11 +112,13 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
|
||||
_onFieldChanged: function (event) {
|
||||
var self = this;
|
||||
event.stopPropagation();
|
||||
var createValues = {'user_id': session.uid};
|
||||
var value = event.data.changes[this.write_field].id;
|
||||
createValues[this.write_field] = value;
|
||||
this._rpc({
|
||||
model: this.write_model,
|
||||
method: 'create',
|
||||
args: [{'user_id': session.uid,'partner_id': value,}],
|
||||
args: [createValues],
|
||||
})
|
||||
.then(function () {
|
||||
self.trigger_up('changeFilter', {
|
||||
|
||||
Reference in New Issue
Block a user