[NEW] hr-luncheon-vouchers: create addons

This commit is contained in:
Stéphan Sainléger
2022-07-13 16:42:37 +02:00
parent 2a4f509178
commit 4a5f926b1d
20 changed files with 732 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
===================
HR Luncheon Voucher
===================
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--analytic-lightgray.png?logo=github
:target: https://github.com/elabore-coop/hr-tools
:alt: elabore/hr-tools
|badge1| |badge2| |badge3|
This module allows the management of Luncheon Vouchers attribution and distribution.
Employees can indicate which days are not concerned by luncheon vouchers.
HR managers can ajust the number of luncheon vouchers to distribute, and follow each employee credit.
Installation
============
Use Odoo normal module installation procedure to install ``hr-luncheon-voucher``.
Configuration
=============
1. Go to ``Configuration > Technical > Calendar > Meeting Types`` and define the meeting categories which cancel the daily luncheon voucher distribution.
2. Go to ``Configuration > General Settings > Employees`` and define if employees need to work the whole day to get a luncheon voucher.
3. Go to ``Employees`` and define for each employee the default number of luncheon vouchers to distribute in each distribution campaign.
Use
===
- when an calendar event makes the luncheon voucher distribution cancelled (Off-site or free lunch for instance), add the corresponding category to the event.
- for each distribution period, the HR manager should:
- go in ``Employees`` tree view
- select all the employees concerned by luncheon vouchers distribution
- click on header button ``Generate luncheon vouchers allocation``
- Fill the wizard form
- a voucher allocation request is created for each employee
- HR manager confirms an allocation request when the figures are confirmed
- HR manager marks the requests as "Distributed" when the vouchers has been effectively distributed
- HR manager can correct the allocation requests with ``Back to draft`` button
- employees' luncheon voucher counters are updated considering the vouchers acquired, dued and distributed at each campaign.
**Attribution rules:**
- a luncheon voucher is acquired for a working day if:
- the employee worked on one or all the attendances of the day (depending if option ``Half working days cancel luncheon vouchers`` is True or not)
- there is no meeting which cancel the voucher during that day (``Site off`` or ``Free lunch`` meeting for instance)
- an attendance is considered as worked as long as there is no leave on the whole attendance time slot
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/elabore-coop/hr-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Contributors
------------
- Stéphan Sainléger <https://github.com/stephansainleger>
Funders
-------
The development of this module has been financially supported by:
- Elabore (https://elabore.coop)
- Amaco (https://amaco.org)
Maintainer
----------
This module is maintained by ELABORE.

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizard

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
{
"name": "HR Luncheon Voucher",
"category": "Human Resources",
"version": "14.0.1.0",
"summary": "Manage luncheon vouchers credit and distribution",
"author": "Elabore",
"website": "https://elabore.coop/",
"installable": True,
"application": True,
"auto_install": False,
"depends": [
"base",
"calendar",
"hr",
"hr_holidays",
"resource",
],
"data": [
"security/ir.model.access.csv",
"views/event_type.xml",
"views/hr_employee_views.xml",
"views/hr_lv_allocation_views.xml",
"views/res_config_settings_views.xml",
"views/menus.xml",
"wizard/generate_lv_allocations_wizard.xml",
"data/event_type_data.xml",
],
"qweb": [],
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<odoo>
<data>
<record id="categ_meet_free_lunch" model="calendar.event.type">
<field name="name">Free lunch</field>
<field name="ref">categ_meet_free_lunch</field>
<field name="remove_luncheon_voucher">1</field>
</record>
<record id="categ_meet_offsite" model="calendar.event.type">
<field name="name">Off-site</field>
<field name="ref">categ_meet_offsite</field>
<field name="remove_luncheon_voucher">1</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from . import calendar_event_type
from . import hr_employee
from . import hr_lv_allocation
from . import resource
from . import res_company
from . import res_config_settings

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from odoo import fields, models, _
class MeetingType(models.Model):
_inherit = "calendar.event.type"
ref = fields.Char(
string=_("Reference"),
copy=False,
store=True,
)
remove_luncheon_voucher = fields.Boolean(
string=_("Remove luncheon voucher"),
copy=True,
store=True,
)

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
from xml.dom.minicompat import EmptyNodeList
from odoo import fields, models, api, _
class HrEmployeeBase(models.AbstractModel):
_inherit = "hr.employee.base"
lv_allocations_ids = fields.One2many("hr.lv.allocation", "employee_id")
total_acquired_lv = fields.Integer(
string=_("Total allocated luncheon vouchers"), store=True, copy=False
)
distributed_lv = fields.Integer(
string=_("Distributed luncheon vouchers"), store=True, copy=False
)
dued_lv = fields.Integer(
string=_("Remaining luncheon vouchers"), store=True, copy=False
)
default_monthly_lv = fields.Integer(
string=_("Default monthly distribution"), store=True, copy=True
)
def refresh_lv_values(self):
for record in self:
record._compute_total_acquired_lv()
record._compute_distributed_lv()
record._compute_dued_lv()
def _compute_total_acquired_lv(self):
for record in self:
allocations = self.env["hr.lv.allocation"].search(
[("employee_id", "=", record.id), ("state", "=", ["confirmed", "distributed"])]
)
record.total_acquired_lv = sum(allocations.mapped("number_acquired_lv"))
def _compute_distributed_lv(self):
for record in self:
allocations = self.env["hr.lv.allocation"].search(
[("employee_id", "=", record.id), ("state", "=", "distributed")]
)
record.distributed_lv = sum(allocations.mapped("number_distributed_lv"))
def _compute_dued_lv(self):
for record in self:
record.dued_lv = record.total_acquired_lv - record.distributed_lv
def generate_mass_lv_allocation(self, values):
for record in self:
record.generate_lv_allocation(values)
def generate_lv_allocation(self, values):
self.ensure_one()
values["employee_id"] = self.id
values["name"] = values["distrib_campaign_name"] + " - " + self.name
self.env["hr.lv.allocation"].create(values)
def action_lv_allocations(self):
action = self.env["ir.actions.act_window"]._for_xml_id("hr-luncheon-voucher.act_lv_allocations")
action['context'] = {
'search_default_employee_id': self.id,
'default_employee_id': self.id,
}
action['domain'] = [('employee_id', '=', self.id)]
return action
def action_lv_allocations_requests_wizard(self):
action = self.env["ir.actions.act_window"]._for_xml_id(
"hr-luncheon-voucher.lv_allocations_requests_wizard_action"
)
ctx = dict(self.env.context)
ctx["active_ids"] = self.ids
action["context"] = ctx
return action

View File

@@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
import math
from datetime import datetime, date, timedelta, time
from dateutil.rrule import rrule, DAILY
from pytz import timezone, UTC, utc
from odoo import fields, models, api, _
class LuncheonVouchersAllocation(models.Model):
_name = "hr.lv.allocation"
_description = "Luncheon Vouchers Allocation"
_order = "create_date desc"
_inherit = ["mail.thread", "mail.activity.mixin"]
_mail_post_access = "read"
name = fields.Char('Name')
distrib_campaign_name = fields.Char('Distribution campaign')
state = fields.Selection(
[
("draft", "Draft"),
("confirmed", "Confirmed"),
("distributed", "Distributed"),
],
string="Status",
readonly=True,
tracking=True,
copy=False,
default="draft",
help="The status is set to 'Draft', when an allocation request is created."
+ "\nThe status is 'Confirmed', when an allocation request is confirmed by HR manager."
+ "\nThe status is 'Distributed', when the luncheon vouchers have been distributed.",
)
date_from = fields.Datetime(
string=_("Start Date"),
store=True,
readonly=False,
copy=False,
tracking=True,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
date_to = fields.Datetime(
string=_("End Date"),
store=True,
readonly=False,
copy=False,
tracking=True,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
employee_id = fields.Many2one(
"hr.employee",
store=True,
string=_("Employee"),
index=True,
readonly=False,
ondelete="restrict",
tracking=True,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
number_acquired_lv = fields.Integer(
string=_("Acquired Vouchers"),
store=True,
readonly=False,
tracking=True,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
number_dued_lv = fields.Integer(
string=_("Dued Vouchers"),
store=True,
readonly=False,
tracking=True,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
number_distributed_lv = fields.Integer(
string=_("Distributed Vouchers"),
store=True,
readonly=False,
tracking=True,
states={
"confirmed": [("readonly", False)],
"distributed": [("readonly", True)],
},
)
def create(self, values):
res = super(LuncheonVouchersAllocation, self).create(values)
res._calculate_number_acquired_lv()
res._calculate_number_dued_lv()
res._default_number_distributed_lv()
return res
@api.depends("employee_id")
def _default_number_distributed_lv(self):
for record in self:
record.number_distributed_lv = record.employee_id.default_monthly_lv
def _has_cancelling_voucher_event(self, day):
category_no_voucher_ids = self.env["calendar.event.type"].search([("remove_luncheon_voucher", "=", True)])
events = self.env["calendar.event"].search([("categ_ids", "in", category_no_voucher_ids.ids)])
day_start = fields.Datetime.to_datetime(day.date())
day_end = fields.Datetime.to_datetime(day.date()) + timedelta(hours=24)
cancelling_events = events.filtered(lambda x: not((x.start < day_start) and (x.stop <= day_start)) and not((x.start >= day_end) and (x.stop > day_end)) )
if len(cancelling_events) > 0:
return True
else:
return False
def _calculate_number_acquired_lv(self):
nb_eligible_days = 0
dfrom = datetime.combine(fields.Date.from_string(self.date_from), time.min).replace(tzinfo=UTC)
dto = datetime.combine(fields.Date.from_string(self.date_to), time.max).replace(tzinfo=UTC)
period_days = rrule(DAILY, dfrom, until=dto)
calendar_resource = self.employee_id.resource_calendar_id
for day in period_days:
# Check if this days is a working day
if not calendar_resource.is_working_day(day):
continue
# The employee should work this day but...
if self.env.company.hr_half_day_cancels_voucher and not calendar_resource.is_full_working_day(day):
# The luncheon voucher is acquired only if the employee has worked the entire day
continue
# Check leaves
if not calendar_resource.is_worked_day(self.employee_id, day):
continue
# The employee has worked this day but...
if self.env.company.hr_half_day_cancels_voucher and not calendar_resource.all_attendances_worked(self.employee_id.resource_id, day):
# The luncheon voucher is acquired only if the employee has worked the entire day
continue
# Check there is no event cancelling the voucher
if self._has_cancelling_voucher_event(day):
continue
# All checks passed, the days is eligible for a voucher
nb_eligible_days += 1
self.number_acquired_lv = nb_eligible_days
def _calculate_number_dued_lv(self):
for record in self:
if record.state == "distributed":
record.number_dued_lv = record.employee_id.dued_lv
else:
record.number_dued_lv = (
record.employee_id.dued_lv + record.number_acquired_lv
)
def confirm_allocation(self):
for record in self:
if record.state == "draft":
record.state = "confirmed"
record.employee_id._compute_total_acquired_lv()
record.employee_id._compute_dued_lv()
def back_to_draft(self):
for record in self:
if record.state in ["confirmed","distributed"]:
record.state = "draft"
record.employee_id._compute_total_acquired_lv()
record.employee_id._compute_distributed_lv()
record.employee_id._compute_dued_lv()
def distribute_allocation(self):
for record in self:
if record.state == "confirmed":
record.state = "distributed"
record.employee_id._compute_distributed_lv()
record.employee_id._compute_dued_lv()
def adjust_distribution(self):
for record in self:
for record in self:
if record.state == "draft":
record.number_distributed_lv = record.employee_id.dued_lv + record.number_acquired_lv

View File

@@ -0,0 +1,7 @@
from odoo import fields, models
class Company(models.Model):
_inherit = 'res.company'
hr_half_day_cancels_voucher = fields.Boolean(string="Half working days cancel luncheon vouchers")

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
hr_half_day_cancels_voucher = fields.Boolean(string="Half working days cancel luncheon vouchers", related="company_id.hr_half_day_cancels_voucher", readonly=False)

View File

@@ -0,0 +1,59 @@
from datetime import timedelta
import math
from odoo import models, fields
class ResourceCalendar(models.Model):
_inherit = "resource.calendar"
def _retrieve_day_matching_attendances(self, day):
domain = [("calendar_id", "=", self.id),("dayofweek", "=", day.weekday())]
if self.two_weeks_calendar:
# Employee has Even/Odd weekly calendar
week_type = 1 if int(math.floor((day.toordinal() - 1) / 7) % 2) else 0
domain.append(("week_type", "=", week_type))
result = self.env["resource.calendar.attendance"].search(domain)
return result
def is_working_day(self, day):
day_attendances = self._retrieve_day_matching_attendances(day)
if len(day_attendances) == 0:
# This day of the week is not supposed to be a working day
return False
else:
# This day of the week is supposed to be a working day
return True
def is_full_working_day(self, day):
day_attendances = self._retrieve_day_matching_attendances(day)
morning_worked = len(day_attendances.filtered(lambda x: x.day_period == "morning")) > 0
afternoon_worked = len(day_attendances.filtered(lambda x: x.day_period == "afternoon")) > 0
return morning_worked and afternoon_worked
def _is_worked_attendance(self, resource, day, attendance):
attendance_start = fields.Datetime.to_datetime(day.date()) + timedelta(hours=attendance.hour_from)
attendance_end = fields.Datetime.to_datetime(day.date()) + timedelta(hours=attendance.hour_to)
resource_leaves = self.env["resource.calendar.leaves"].search([("resource_id", "=", resource.id), ("date_from", "<=", attendance_start), ("date_to", ">=", attendance_end)])
if resource_leaves:
return False
else:
# a part or the whole attendance is worked
return True
def is_worked_day(self, resource, day):
day_attendances = self._retrieve_day_matching_attendances(day)
# If at least one attendance is worked, return True
for attendance in day_attendances:
if self._is_worked_attendance(resource, day, attendance):
return True
return False
def all_attendances_worked(self, resource, day):
day_attendances = self._retrieve_day_matching_attendances(day)
# If at least one attendance is not worked, return False
for attendance in day_attendances:
if not self._is_worked_attendance(resource, day, attendance):
return False
return True

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_lv_allocation_user,access_lv_allocation_user,model_hr_lv_allocation,hr.group_hr_user,1,0,0,0
access_lv_allocation_manager,access_lv_allocation_manager,model_hr_lv_allocation,hr.group_hr_manager,1,1,1,1
access_lv_allocation_wizard_user,access_lv_allocation_wizard_user,model_generate_lv_allocation_requests,hr.group_hr_user,1,0,0,0
access_lv_allocation_wizard_manager,access_lv_allocation_wizard_manager,model_generate_lv_allocation_requests,hr.group_hr_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_lv_allocation_user access_lv_allocation_user model_hr_lv_allocation hr.group_hr_user 1 0 0 0
3 access_lv_allocation_manager access_lv_allocation_manager model_hr_lv_allocation hr.group_hr_manager 1 1 1 1
4 access_lv_allocation_wizard_user access_lv_allocation_wizard_user model_generate_lv_allocation_requests hr.group_hr_user 1 0 0 0
5 access_lv_allocation_wizard_manager access_lv_allocation_wizard_manager model_generate_lv_allocation_requests hr.group_hr_manager 1 1 1 1

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_event_type_luncheonvoucher_tree" model="ir.ui.view">
<field name="name">event.type.luncheonvoucher</field>
<field name="model">calendar.event.type</field>
<field name="inherit_id" ref="calendar.view_calendar_event_type_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="remove_luncheon_voucher" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_employee_form_lv" model="ir.ui.view">
<field name="name">hr.employee.form.lv</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form" />
<field name="arch" type="xml">
<header position="inside">
<button type="object" name="refresh_lv_values" string="Refresh Luncheon Vouchers" class="btn-secundary" />
</header>
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object" name="action_lv_allocations" class="oe_stat_button" icon="fa-ticket">
<field name="total_acquired_lv" widget="statinfo" string="Acquired" />
</button>
<button type="object" name="action_lv_allocations" class="oe_stat_button" icon="fa-ticket">
<field name="distributed_lv" widget="statinfo" string="Distributed" />
</button>
<button type="object" name="action_lv_allocations" class="oe_stat_button" icon="fa-ticket">
<field name="dued_lv" widget="statinfo" string="Dued" />
</button>
</xpath>
<xpath expr="//page[@name='hr_settings']/group" position="inside">
<group name="luncheon_vouchers" string="Luncheon Vouchers">
<field name="default_monthly_lv" />
<field name="lv_allocations_ids" />
</group>
</xpath>
</field>
</record>
<record id="view_employee_tree_lv" model="ir.ui.view">
<field name="name">view_employee_tree_lv</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_tree" />
<field name="arch" type="xml">
<xpath expr="//tree" position="inside">
<header>
<button type="object" name="action_lv_allocations_requests_wizard" string="Generate Luncheon Vouchers Allocations" class="btn-primary" />
</header>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_lv_allocation_search" model="ir.ui.view">
<field name="name">hr.lv.allocation.search</field>
<field name="model">hr.lv.allocation</field>
<field name="arch" type="xml">
<search string="Luncheon vouchers allocations">
<field name="state" />
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]" />
<filter name="confirmed" string="Confirmed" domain="[('state', '=', 'confirmed')]" />
<filter name="distributed" string="Distributed" domain="[('state', '=', 'distributed')]" />
</search>
</field>
</record>
<record id="hr_lv_allocation_tree" model="ir.ui.view">
<field name="name">hr.lv.allocation.tree</field>
<field name="model">hr.lv.allocation</field>
<field name="arch" type="xml">
<tree string="Luncheon vouchers allocations">
<header>
<button type="object" name="confirm_allocation" string="Confirm" class="btn-primary" />
<button type="object" name="distribute_allocation" string="Distribute Vouchers" class="btn-primary" />
<button type="object" name="back_to_draft" string="Back to draft" class="btn-secundary" />
<button type="object" name="adjust_distribution" string="Adjust distribution" class="btn-secundary" />
</header>
<field name="distrib_campaign_name" />
<field name="employee_id" />
<field name="state" />
<field name="date_from" widget="date" />
<field name="date_to" widget="date" />
<field name="number_acquired_lv" />
<field name="number_dued_lv" />
<field name="number_distributed_lv" />
</tree>
</field>
</record>
<record id="hr_lv_allocation_form" model="ir.ui.view">
<field name="name">hr.lv.allocation.form</field>
<field name="model">hr.lv.allocation</field>
<field name="arch" type="xml">
<form string="">
<header>
<button type="object" name="confirm_allocation" string="Confirm" class="btn-primary" attrs="{'invisible': [('state', '!=', 'draft')]}" />
<button type="object" name="distribute_allocation" string="Distribute Vouchers" class="btn-primary" attrs="{'invisible': [('state', '!=', 'confirmed')]}" />
<button type="object" name="back_to_draft" string="Back to draft" class="btn-secundary" attrs="{'invisible': [('state', '=', 'draft')]}" />
<button type="object" name="adjust_distribution" string="Adjust distribution" class="btn-secundary" attrs="{'invisible': [('state', '!=', 'draft')]}" />
</header>
<sheet>
<h1>
<field name="name" />
</h1>
<group string="Request context">
<field name="distrib_campaign_name" />
<field name="employee_id" />
<field name="state" />
<field name="date_from" widget="date" />
<field name="date_to" widget="date" />
</group>
<group string="Luncheon vouchers calculation">
<field name="number_acquired_lv" />
<field name="number_dued_lv" />
<field name="number_distributed_lv" />
</group>
</sheet>
</form>
</field>
</record>
<record id="act_lv_allocations" model="ir.actions.act_window">
<field name="name">Luncheon vouchers allocations</field>
<field name="res_model">hr.lv.allocation</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem id="menu_hr_lv_allocations" action="act_lv_allocations" parent="hr.menu_hr_employee_payroll" sequence="1" name="Luncheon vouchers" />
</odoo>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_lv_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.lv</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="99" />
<field name="inherit_id" ref="hr.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[@name='employee_rights_setting_container']/.." position="inside">
<h2>Employee Luncheon Vouchers</h2>
<div class="row mt16 o_settings_container" name="employee_lv_container">
<div class="col-12 col-lg-6 o_setting_box" id="employee_lv_halfday_cancel" title="Luncheon Vouchers Half-day Cancel">
<div class="o_setting_left_pane">
<field name="hr_half_day_cancels_voucher" />
</div>
<div class="o_setting_right_pane">
<label for="hr_half_day_cancels_voucher" />
<div class="text-muted" name="hr_presence_options_advanced">
Voucher is acquired only if the employee worked during all his attendance.
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import generate_lv_allocations_wizard

View File

@@ -0,0 +1,29 @@
from odoo import _, api, fields, models
class GenerateLVAllocationRequests(models.TransientModel):
_name = "generate.lv.allocation.requests"
_description = "Generate Luncheon Vouchers Allocations Requests"
distrib_campaign_name = fields.Char('Distribution campaign', required=True)
date_from = fields.Datetime(
string=_("Start Date"),
required=True
)
date_to = fields.Datetime(
string=_("End Date"),
required=True
)
def generate_lv_allocations(self):
values = {}
values["distrib_campaign_name"] = self.distrib_campaign_name
values["date_from"] = self.date_from
values["date_to"] = self.date_to
employees = self.env["hr.employee"].search(
[
("id", "in", self.env.context.get("active_ids")),
]
)
employees.generate_mass_lv_allocation(values)
# Open lv allocation tree view
return self.env["ir.actions.act_window"]._for_xml_id("hr-luncheon-voucher.act_lv_allocations")

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="lv_allocations_requests_wizard" model="ir.ui.view">
<field name="name">lv.allocations.requests.wizard</field>
<field name="model">generate.lv.allocation.requests</field>
<field name="arch" type="xml">
<form string="Create Luncheon Vouchers allocations requests">
<group name="dates" string="Period to consider">
<group>
<field name="distrib_campaign_name" />
<field name="date_from" widget="date" />
<field name="date_to" widget="date" />
</group>
</group>
<footer>
<button string="Create allocations requests" name="generate_lv_allocations" type="object" class="btn-primary" />
<button string="Cancel" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="lv_allocations_requests_wizard_action" model="ir.actions.act_window">
<field name="name">Create Luncheon Vouchers allocations requests</field>
<field name="res_model">generate.lv.allocation.requests</field>
<field name="view_mode">form</field>
<field name="view_id" ref="lv_allocations_requests_wizard" />
<field name="target">new</field>
</record>
</data>
</odoo>