diff --git a/addons/hr_contract/models/hr_contract.py b/addons/hr_contract/models/hr_contract.py index 93d65b81e97..21aaa77487d 100644 --- a/addons/hr_contract/models/hr_contract.py +++ b/addons/hr_contract/models/hr_contract.py @@ -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) diff --git a/addons/hr_contract/views/hr_contract_views.xml b/addons/hr_contract/views/hr_contract_views.xml index 0ae68e6c4e4..da004a5cbf6 100644 --- a/addons/hr_contract/views/hr_contract_views.xml +++ b/addons/hr_contract/views/hr_contract_views.xml @@ -56,6 +56,7 @@ + @@ -151,7 +152,11 @@ hr.contract.tree hr.contract - + @@ -159,6 +164,7 @@ + diff --git a/addons/hr_payroll/__manifest__.py b/addons/hr_payroll/__manifest__.py index 3cc7f2ba363..e8e69c66a45 100644 --- a/addons/hr_payroll/__manifest__.py +++ b/addons/hr_payroll/__manifest__.py @@ -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': [ diff --git a/addons/hr_payroll/models/hr_benefit.py b/addons/hr_payroll/models/hr_benefit.py index 1862c46d3eb..bd99e999a12 100644 --- a/addons/hr_payroll/models/hr_benefit.py +++ b/addons/hr_payroll/models/hr_benefit.py @@ -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.') + ] diff --git a/addons/hr_payroll/models/hr_employee.py b/addons/hr_payroll/models/hr_employee.py index 2521c227a6c..2ab7a292149 100644 --- a/addons/hr_payroll/models/hr_employee.py +++ b/addons/hr_payroll/models/hr_employee.py @@ -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)) diff --git a/addons/hr_payroll/models/hr_leave.py b/addons/hr_payroll/models/hr_leave.py index 6440b0d1ee5..aaa49384762 100644 --- a/addons/hr_payroll/models/hr_leave.py +++ b/addons/hr_payroll/models/hr_leave.py @@ -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 diff --git a/addons/hr_payroll/models/hr_payslip.py b/addons/hr_payroll/models/hr_payslip.py index b2b085b3639..70d637c2546 100644 --- a/addons/hr_payroll/models/hr_payslip.py +++ b/addons/hr_payroll/models/hr_payslip.py @@ -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): diff --git a/addons/hr_payroll/security/hr_payroll_security.xml b/addons/hr_payroll/security/hr_payroll_security.xml index 423cfa4dd13..ba26e5966a6 100644 --- a/addons/hr_payroll/security/hr_payroll_security.xml +++ b/addons/hr_payroll/security/hr_payroll_security.xml @@ -44,5 +44,16 @@ + + Benefits/Employee calendar filter: only self + + [('user_id', '=', user.id)] + + + + + + + diff --git a/addons/hr_payroll/security/ir.model.access.csv b/addons/hr_payroll/security/ir.model.access.csv index d1a03b160a5..751a64b4779 100644 --- a/addons/hr_payroll/security/ir.model.access.csv +++ b/addons/hr_payroll/security/ir.model.access.csv @@ -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 diff --git a/addons/hr_payroll/static/src/js/benefit.js b/addons/hr_payroll/static/src/js/benefit.js index 63f4799ddd7..6ba72ff3f4f 100644 --- a/addons/hr_payroll/static/src/js/benefit.js +++ b/addons/hr_payroll/static/src/js/benefit.js @@ -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( - $(''+ txt +'') - .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); - }) + $(''+ _t(text) +'') ); }, @@ -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( - $(''+ _t('Generate Payslips') +'') - .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( - $(''+ _t('Validate Benefits') +'') - .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); + }); \ No newline at end of file diff --git a/addons/hr_payroll/tests/test_payslip_flow.py b/addons/hr_payroll/tests/test_payslip_flow.py index a1b2e301cbe..89ee79084f3 100644 --- a/addons/hr_payroll/tests/test_payslip_flow.py +++ b/addons/hr_payroll/tests/test_payslip_flow.py @@ -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() diff --git a/addons/hr_payroll/views/hr_benefit_views.xml b/addons/hr_payroll/views/hr_benefit_views.xml index a6f30e416f7..a6608344be1 100644 --- a/addons/hr_payroll/views/hr_benefit_views.xml +++ b/addons/hr_payroll/views/hr_benefit_views.xml @@ -1,22 +1,11 @@ - - Generate Benefit - - - code - -if records: - records.generate_benefit() - - - Benefit hr.benefit form - calendar,tree,form + calendar,tree,form,pivot @@ -24,6 +13,7 @@ if records: hr.benefit - - + + - @@ -99,7 +88,10 @@ if records: - + + + Hours + @@ -115,6 +107,8 @@ if records: + + @@ -145,19 +139,20 @@ if records: - + + Generate payslips + + + code + + action = env['hr.payslip.employees'].create({}).compute_sheet() + + Benefit Types hr.benefit.type tree,kanban,form - - \ No newline at end of file + diff --git a/addons/hr_payroll/views/hr_contract_views.xml b/addons/hr_payroll/views/hr_contract_views.xml index 6a377e16744..6677e26f580 100644 --- a/addons/hr_payroll/views/hr_contract_views.xml +++ b/addons/hr_payroll/views/hr_contract_views.xml @@ -1,11 +1,5 @@ - - - - - hr.contract.view.form.inherit @@ -24,4 +18,19 @@ + + + Contracts + hr.contract + form + tree,kanban,form,activity + [('employee_id', '!=', False)] + {'search_default_running': 1, 'search_default_to_renew': 1} + + + + Create a new contract + + + \ No newline at end of file diff --git a/addons/hr_payroll/views/hr_payroll_menu.xml b/addons/hr_payroll/views/hr_payroll_menu.xml new file mode 100644 index 00000000000..4222ee02b1a --- /dev/null +++ b/addons/hr_payroll/views/hr_payroll_menu.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/hr_payroll/views/hr_payslip_views.xml b/addons/hr_payroll/views/hr_payslip_views.xml index 85c3f03fe71..60c5ff4ba2b 100644 --- a/addons/hr_payroll/views/hr_payslip_views.xml +++ b/addons/hr_payroll/views/hr_payslip_views.xml @@ -71,14 +71,16 @@ hr.payslip + - + + + - @@ -137,14 +139,6 @@ - - - - - @@ -154,9 +148,9 @@ - - + - + @@ -259,9 +253,12 @@ + - + + + @@ -277,12 +274,18 @@ Employee Payslips hr.payslip form - tree,kanban,form + tree,kanban,form,pivot {'search_default_group_by_batch': 1} - - + + Employee Payslips + hr.payslip + form + tree,kanban,form + + {"search_default_group_by_batch":1} + hr.payslip @@ -398,7 +401,17 @@ tree,kanban,form - + + + Confirm + + + code + + if records: + records.action_payslip_done() + + diff --git a/addons/hr_payroll/views/hr_salary_rule_views.xml b/addons/hr_payroll/views/hr_salary_rule_views.xml index 7b1797c6a94..29190e6893a 100644 --- a/addons/hr_payroll/views/hr_salary_rule_views.xml +++ b/addons/hr_payroll/views/hr_salary_rule_views.xml @@ -100,12 +100,7 @@ form tree,kanban,form - + hr.salary.rule.category.form @@ -155,13 +150,7 @@ - + @@ -380,8 +363,6 @@ - - {'module' : 'hr_payroll'} - diff --git a/addons/hr_payroll/wizard/__init__.py b/addons/hr_payroll/wizard/__init__.py index 86fd3512f4d..f433d2bbf05 100644 --- a/addons/hr_payroll/wizard/__init__.py +++ b/addons/hr_payroll/wizard/__init__.py @@ -3,4 +3,3 @@ from . import hr_payroll_payslips_by_employees from . import hr_payroll_contribution_register_report -from . import hr_benefit_employee diff --git a/addons/hr_payroll/wizard/hr_benefit_employee.py b/addons/hr_payroll/wizard/hr_benefit_employee.py deleted file mode 100644 index cdbf8b6345a..00000000000 --- a/addons/hr_payroll/wizard/hr_benefit_employee.py +++ /dev/null @@ -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) diff --git a/addons/hr_payroll/wizard/hr_benefit_employee_views.xml b/addons/hr_payroll/wizard/hr_benefit_employee_views.xml deleted file mode 100644 index 7db54da6264..00000000000 --- a/addons/hr_payroll/wizard/hr_benefit_employee_views.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - hr_benefit_employees - hr.benefit.employees - - - - - - - This wizard will generate benefits for all selected employee(s) based on attendances. - - - - - - - - - - - diff --git a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py index 63f89d746fa..af82dfedc4a 100644 --- a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py +++ b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py @@ -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, diff --git a/addons/hr_payroll_account/models/hr_payroll_account.py b/addons/hr_payroll_account/models/hr_payroll_account.py index 6af3baa086d..430396bbcc2 100644 --- a/addons/hr_payroll_account/models/hr_payroll_account.py +++ b/addons/hr_payroll_account/models/hr_payroll_account.py @@ -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 diff --git a/addons/hr_payroll_account/tests/test_hr_payroll_account.py b/addons/hr_payroll_account/tests/test_hr_payroll_account.py index ea1a08e6f04..5bb4bdcced2 100644 --- a/addons/hr_payroll_account/tests/test_hr_payroll_account.py +++ b/addons/hr_payroll_account/tests/test_hr_payroll_account.py @@ -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. diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js index 7c06a7d818d..aaf52057ad6 100644 --- a/addons/web/static/src/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/js/views/calendar/calendar_model.js @@ -481,7 +481,7 @@ return AbstractModel.extend({ */ _loadFilter: function (filter) { if (!filter.write_model) { - return; + return $.when(); } var field = this.fields[filter.fieldName]; diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js index c2c90ea7e62..2720224fe39 100644 --- a/addons/web/static/src/js/views/calendar/calendar_renderer.js +++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js @@ -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', {
+ Create a new contract +