New implementation of KM cost to property support the decimal precision at 3 digits (without changing the decimal precision of all prices in Odoo)
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from . import hr_expense
|
from . import hr_expense
|
||||||
from .post_install import create_private_car_expense_products
|
from .post_install import create_private_car_km_prices
|
||||||
|
from .pre_install import create_decimal_precision
|
||||||
|
|||||||
@@ -16,13 +16,16 @@
|
|||||||
'hr_expense_sequence',
|
'hr_expense_sequence',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
|
'hr_expense_data.xml',
|
||||||
'hr_employee_view.xml',
|
'hr_employee_view.xml',
|
||||||
'hr_expense_view.xml',
|
'hr_expense_view.xml',
|
||||||
'product_view.xml',
|
'product_view.xml',
|
||||||
'hr_expense_data.xml',
|
'private_car_km_price_view.xml',
|
||||||
'security/expense_security.xml',
|
'security/expense_security.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
],
|
],
|
||||||
'post_init_hook': 'create_private_car_expense_products',
|
'pre_init_hook': 'create_decimal_precision',
|
||||||
|
'post_init_hook': 'create_private_car_km_prices',
|
||||||
'demo': ['private_car_demo.xml'],
|
'demo': ['private_car_demo.xml'],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
<page name="hr_settings" position="inside">
|
<page name="hr_settings" position="inside">
|
||||||
<group name="private_car_expenses" string="Private Car Expenses">
|
<group name="private_car_expenses" string="Private Car Expenses">
|
||||||
<field name="private_car_plate"/>
|
<field name="private_car_plate"/>
|
||||||
<field name="private_car_product_id"
|
<field name="private_car_km_price_id"/>
|
||||||
context="{'default_private_car_expense_ok': True}"/>
|
|
||||||
<field name="private_car_total_km_this_year"/>
|
<field name="private_car_total_km_this_year"/>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
|
|||||||
@@ -33,24 +33,6 @@ import odoo.addons.decimal_precision as dp
|
|||||||
class ProductTemplate(models.Model):
|
class ProductTemplate(models.Model):
|
||||||
_inherit = 'product.template'
|
_inherit = 'product.template'
|
||||||
|
|
||||||
private_car_expense_ok = fields.Boolean(
|
|
||||||
string='Private Car Expense', track_visibility='onchange')
|
|
||||||
|
|
||||||
# same code in class product.product and product.template
|
|
||||||
@api.onchange('private_car_expense_ok')
|
|
||||||
def onchange_private_car_expense_ok(self):
|
|
||||||
if self.private_car_expense_ok:
|
|
||||||
km_uom = self.env.ref('product.product_uom_km')
|
|
||||||
self.type = 'service'
|
|
||||||
self.list_price = 0.0
|
|
||||||
self.can_be_expensed = False
|
|
||||||
self.sale_ok = False
|
|
||||||
self.purchase_ok = False
|
|
||||||
self.uom_id = km_uom.id
|
|
||||||
self.po_uom_id = km_uom.id
|
|
||||||
self.taxes_id = False
|
|
||||||
self.supplier_taxes_id = False
|
|
||||||
|
|
||||||
# same code in class product.product and product.template
|
# same code in class product.product and product.template
|
||||||
@api.onchange('can_be_expensed')
|
@api.onchange('can_be_expensed')
|
||||||
def onchange_can_be_expensed(self):
|
def onchange_can_be_expensed(self):
|
||||||
@@ -58,31 +40,12 @@ class ProductTemplate(models.Model):
|
|||||||
unit_uom = self.env.ref('product.product_uom_unit')
|
unit_uom = self.env.ref('product.product_uom_unit')
|
||||||
self.type = 'service'
|
self.type = 'service'
|
||||||
self.list_price = 0.0
|
self.list_price = 0.0
|
||||||
self.private_car_expense_ok = False
|
|
||||||
self.sale_ok = False
|
self.sale_ok = False
|
||||||
self.purchase_ok = False
|
self.purchase_ok = False
|
||||||
self.uom_id = unit_uom.id
|
self.uom_id = unit_uom.id
|
||||||
self.po_uom_id = unit_uom.id
|
self.po_uom_id = unit_uom.id
|
||||||
self.taxes_id = False
|
self.taxes_id = False
|
||||||
|
|
||||||
@api.constrains(
|
|
||||||
'private_car_expense_ok', 'can_be_expensed', 'uom_id',
|
|
||||||
'standard_price')
|
|
||||||
def _check_private_car_expense(self):
|
|
||||||
for product in self:
|
|
||||||
if product.private_car_expense_ok:
|
|
||||||
if product.can_be_expensed:
|
|
||||||
raise ValidationError(_(
|
|
||||||
"The product '%s' cannot have both the properties "
|
|
||||||
"'Can be Expensed' and 'Private Car Expense'.")
|
|
||||||
% product.display_name)
|
|
||||||
km_uom = self.env.ref('product.product_uom_km')
|
|
||||||
if product.uom_id != km_uom:
|
|
||||||
raise ValidationError(_(
|
|
||||||
"The product '%s' is a Private Car Expense, so "
|
|
||||||
"it's unit of measure must be kilometers (KM).")
|
|
||||||
% product.display_name)
|
|
||||||
|
|
||||||
# It probably doesn't make sense to have a constraint on a property fields
|
# It probably doesn't make sense to have a constraint on a property fields
|
||||||
# But the same constrain is also on hr.expense
|
# But the same constrain is also on hr.expense
|
||||||
@api.constrains('supplier_taxes_id', 'can_be_expensed')
|
@api.constrains('supplier_taxes_id', 'can_be_expensed')
|
||||||
@@ -107,21 +70,6 @@ class ProductTemplate(models.Model):
|
|||||||
class ProductProduct(models.Model):
|
class ProductProduct(models.Model):
|
||||||
_inherit = 'product.product'
|
_inherit = 'product.product'
|
||||||
|
|
||||||
# same code in class product.product and product.template
|
|
||||||
@api.onchange('private_car_expense_ok')
|
|
||||||
def onchange_private_car_expense_ok(self):
|
|
||||||
if self.private_car_expense_ok:
|
|
||||||
km_uom = self.env.ref('product.product_uom_km')
|
|
||||||
self.type = 'service'
|
|
||||||
self.list_price = 0.0
|
|
||||||
self.can_be_expensed = False
|
|
||||||
self.sale_ok = False
|
|
||||||
self.purchase_ok = False
|
|
||||||
self.uom_id = km_uom.id
|
|
||||||
self.po_uom_id = km_uom.id
|
|
||||||
self.taxes_id = False
|
|
||||||
self.supplier_taxes_id = False
|
|
||||||
|
|
||||||
# same code in class product.product and product.template
|
# same code in class product.product and product.template
|
||||||
@api.onchange('can_be_expensed')
|
@api.onchange('can_be_expensed')
|
||||||
def onchange_can_be_expensed(self):
|
def onchange_can_be_expensed(self):
|
||||||
@@ -129,7 +77,6 @@ class ProductProduct(models.Model):
|
|||||||
unit_uom = self.env.ref('product.product_uom_unit')
|
unit_uom = self.env.ref('product.product_uom_unit')
|
||||||
self.type = 'service'
|
self.type = 'service'
|
||||||
self.list_price = 0.0
|
self.list_price = 0.0
|
||||||
self.private_car_expense_ok = False
|
|
||||||
self.sale_ok = False
|
self.sale_ok = False
|
||||||
self.purchase_ok = False
|
self.purchase_ok = False
|
||||||
self.uom_id = unit_uom.id
|
self.uom_id = unit_uom.id
|
||||||
@@ -137,13 +84,29 @@ class ProductProduct(models.Model):
|
|||||||
self.taxes_id = False
|
self.taxes_id = False
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateCarKmPrice(models.Model):
|
||||||
|
_name = 'private.car.km.price'
|
||||||
|
_description = 'Private Car Kilometer Price'
|
||||||
|
_order = 'name'
|
||||||
|
|
||||||
|
name = fields.Char(required=True)
|
||||||
|
unit_amount = fields.Float(
|
||||||
|
string='Price per KM', digits=dp.get_precision('Expense Unit Price'),
|
||||||
|
help='Price per kilometer in company currency.')
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
'res.company', string='Company',
|
||||||
|
default=lambda self: self.env['res.company']._company_default_get(
|
||||||
|
'private.car.km.price'))
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
|
||||||
|
|
||||||
class HrEmployee(models.Model):
|
class HrEmployee(models.Model):
|
||||||
_inherit = 'hr.employee'
|
_inherit = 'hr.employee'
|
||||||
|
|
||||||
def compute_private_car_total_km_this_year(self):
|
def compute_private_car_total_km_this_year(self):
|
||||||
res = {}
|
res = {}
|
||||||
private_car_products = self.env['product.product'].search(
|
private_car_product_id = self.env.ref(
|
||||||
[('private_car_expense_ok', '=', True)])
|
'hr_expense_usability.generic_private_car_expense').id
|
||||||
today = fields.Date.context_today(self)
|
today = fields.Date.context_today(self)
|
||||||
today_dt = fields.Date.from_string(today)
|
today_dt = fields.Date.from_string(today)
|
||||||
self._cr.execute(
|
self._cr.execute(
|
||||||
@@ -152,11 +115,11 @@ class HrEmployee(models.Model):
|
|||||||
FROM hr_expense el
|
FROM hr_expense el
|
||||||
WHERE el.state NOT IN ('draft', 'cancel')
|
WHERE el.state NOT IN ('draft', 'cancel')
|
||||||
AND el.employee_id IN %s
|
AND el.employee_id IN %s
|
||||||
AND el.product_id IN %s
|
AND el.product_id=%s
|
||||||
AND EXTRACT(year FROM el.date) = %s
|
AND EXTRACT(year FROM el.date) = %s
|
||||||
GROUP BY el.employee_id
|
GROUP BY el.employee_id
|
||||||
""",
|
""",
|
||||||
(tuple(self.ids), tuple(private_car_products.ids), today_dt.year))
|
(tuple(self.ids), private_car_product_id, today_dt.year))
|
||||||
for line in self._cr.dictfetchall():
|
for line in self._cr.dictfetchall():
|
||||||
res[line['employee_id']] = line['sum']
|
res[line['employee_id']] = line['sum']
|
||||||
for empl in self:
|
for empl in self:
|
||||||
@@ -165,9 +128,8 @@ class HrEmployee(models.Model):
|
|||||||
private_car_plate = fields.Char(
|
private_car_plate = fields.Char(
|
||||||
'Private Car Plate', size=32, copy=False, track_visibility='onchange',
|
'Private Car Plate', size=32, copy=False, track_visibility='onchange',
|
||||||
help="This field will be copied on the expenses of this employee.")
|
help="This field will be copied on the expenses of this employee.")
|
||||||
private_car_product_id = fields.Many2one(
|
private_car_km_price_id = fields.Many2one(
|
||||||
'product.product', string='Private Car Product', copy=False,
|
'private.car.km.price', string='Private Car Price', copy=False,
|
||||||
domain=[('private_car_expense_ok', '=', True)],
|
|
||||||
ondelete='restrict', track_visibility='onchange',
|
ondelete='restrict', track_visibility='onchange',
|
||||||
help="This field will be copied on the expenses of this employee.")
|
help="This field will be copied on the expenses of this employee.")
|
||||||
private_car_total_km_this_year = fields.Float(
|
private_car_total_km_this_year = fields.Float(
|
||||||
@@ -213,8 +175,12 @@ class HrExpense(models.Model):
|
|||||||
private_car_plate = fields.Char(
|
private_car_plate = fields.Char(
|
||||||
string='Private Car Plate', size=32, track_visibility='onchange',
|
string='Private Car Plate', size=32, track_visibility='onchange',
|
||||||
readonly=True, states={'draft': [('readonly', False)]})
|
readonly=True, states={'draft': [('readonly', False)]})
|
||||||
private_car_expense = fields.Boolean(
|
private_car_km_price_id = fields.Many2one(
|
||||||
related='product_id.private_car_expense_ok', readonly=True, store=True)
|
'private.car.km.price', string='Private Car Price', copy=False,
|
||||||
|
ondelete='restrict', track_visibility='onchange',
|
||||||
|
help="This field will be copied on the expenses of this employee.")
|
||||||
|
# only for field visibility
|
||||||
|
private_car_expense = fields.Boolean()
|
||||||
tax_amount = fields.Monetary(
|
tax_amount = fields.Monetary(
|
||||||
string='Tax Amount', currency_field='currency_id',
|
string='Tax Amount', currency_field='currency_id',
|
||||||
readonly=True, states={'draft': [('readonly', False)]})
|
readonly=True, states={'draft': [('readonly', False)]})
|
||||||
@@ -277,27 +243,42 @@ class HrExpense(models.Model):
|
|||||||
|
|
||||||
@api.onchange('product_id')
|
@api.onchange('product_id')
|
||||||
def _onchange_product_id(self):
|
def _onchange_product_id(self):
|
||||||
|
private_car_product = self.env.ref(
|
||||||
|
'hr_expense_usability.generic_private_car_expense')
|
||||||
if (
|
if (
|
||||||
self.product_id and self.product_id == self.env.ref(
|
self.product_id and
|
||||||
'hr_expense_usability.generic_private_car_expense')):
|
self.product_id == private_car_product and
|
||||||
if not self.employee_id.private_car_product_id:
|
self.employee_id):
|
||||||
|
if not self.employee_id.private_car_km_price_id:
|
||||||
raise UserError(_(
|
raise UserError(_(
|
||||||
"Missing Private Car Product on the configuration of "
|
"Missing Private Car Km Price on the configuration of "
|
||||||
"the employee '%s'.") % self.employee_id.display_name)
|
"the employee '%s'.") % self.employee_id.display_name)
|
||||||
if not self.employee_id.private_car_plate:
|
if not self.employee_id.private_car_plate:
|
||||||
raise UserError(_(
|
raise UserError(_(
|
||||||
"Missing Private Car Plate on the configuration of "
|
"Missing Private Car Plate on the configuration of "
|
||||||
"the employee '%s'.") % self.employee_id.display_name)
|
"the employee '%s'.") % self.employee_id.display_name)
|
||||||
self.product_id = self.employee_id.private_car_product_id
|
self.private_car_expense = True
|
||||||
|
self.currency_id = self.company_id.currency_id
|
||||||
self.private_car_plate = self.employee_id.private_car_plate
|
self.private_car_plate = self.employee_id.private_car_plate
|
||||||
|
self.private_car_km_price_id =\
|
||||||
|
self.employee_id.private_car_km_price_id
|
||||||
|
else:
|
||||||
|
self.private_car_expense = False
|
||||||
|
self.private_car_plate = False
|
||||||
|
self.private_car_km_price_id = False
|
||||||
return super(HrExpense, self)._onchange_product_id()
|
return super(HrExpense, self)._onchange_product_id()
|
||||||
|
|
||||||
|
@api.onchange('private_car_km_price_id')
|
||||||
|
def _onchange_private_car_km_price_id(self):
|
||||||
|
if self.private_car_km_price_id and self.employee_id:
|
||||||
|
self.unit_amount =\
|
||||||
|
self.employee_id.private_car_km_price_id.unit_amount
|
||||||
|
|
||||||
@api.onchange('unit_amount')
|
@api.onchange('unit_amount')
|
||||||
def _onchange_unit_amount(self):
|
def _onchange_unit_amount(self):
|
||||||
res = {}
|
res = {}
|
||||||
if self.product_id.private_car_expense_ok:
|
if self.private_car_expense:
|
||||||
original_unit_amount = self.product_id.price_compute(
|
original_unit_amount = self.private_car_km_price_id.unit_amount
|
||||||
'standard_price')[self.product_id.id]
|
|
||||||
prec = self.env['decimal.precision'].precision_get(
|
prec = self.env['decimal.precision'].precision_get(
|
||||||
'Expense Unit Price')
|
'Expense Unit Price')
|
||||||
if float_compare(
|
if float_compare(
|
||||||
@@ -338,21 +319,23 @@ class HrExpense(models.Model):
|
|||||||
'hr_expense_usability.generic_private_car_expense')
|
'hr_expense_usability.generic_private_car_expense')
|
||||||
for exp in self:
|
for exp in self:
|
||||||
if exp.product_id == generic_private_car_product:
|
if exp.product_id == generic_private_car_product:
|
||||||
raise ValidationError(_(
|
|
||||||
"You are trying to save the expense '%s' "
|
|
||||||
"with the generic product '%s': it is not possible, "
|
|
||||||
"this product should have been automatically replaced "
|
|
||||||
"by the specific private car product configured for "
|
|
||||||
"the employee '%s'.") % (
|
|
||||||
exp.name,
|
|
||||||
generic_private_car_product.name,
|
|
||||||
exp.employee_id.display_name))
|
|
||||||
if exp.product_id.private_car_expense_ok:
|
|
||||||
if not exp.private_car_plate:
|
if not exp.private_car_plate:
|
||||||
raise ValidationError(_(
|
raise ValidationError(_(
|
||||||
"Missing 'Private Car Plate' on the "
|
"Missing 'Private Car Plate' on the "
|
||||||
"expense '%s' of employee '%s'.")
|
"expense '%s' of employee '%s'.")
|
||||||
% (exp.name, exp.employee_id.display_name))
|
% (exp.name, exp.employee_id.display_name))
|
||||||
|
if not exp.private_car_km_price_id:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"Missing 'Private Car Km Price' on the "
|
||||||
|
"expense '%s'.") % exp.name)
|
||||||
|
if exp.currency_id != exp.company_id.currency_id:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"The expense '%s' is a private car expense, "
|
||||||
|
"so the currency of this expense (%s) should "
|
||||||
|
"be the currency of the company (%s).") % (
|
||||||
|
exp.name,
|
||||||
|
exp.currency_id.name,
|
||||||
|
exp.company_id.currency_id.name))
|
||||||
if exp.tax_ids:
|
if exp.tax_ids:
|
||||||
raise ValidationError(_(
|
raise ValidationError(_(
|
||||||
"The expense '%s' is a private car expense "
|
"The expense '%s' is a private car expense "
|
||||||
|
|||||||
@@ -21,14 +21,8 @@
|
|||||||
<field name="uom_po_id" ref="product.product_uom_km"/>
|
<field name="uom_po_id" ref="product.product_uom_km"/>
|
||||||
<field name="taxes_id" eval="False"/>
|
<field name="taxes_id" eval="False"/>
|
||||||
<field name="supplier_taxes_id" eval="False"/>
|
<field name="supplier_taxes_id" eval="False"/>
|
||||||
|
<field name="company_id" eval="False"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- KM cost is decimal precision = 3
|
|
||||||
but decimal precision 'Product Price' is 2 by default,
|
|
||||||
and companies may not want to change it -->
|
|
||||||
<record forcecreate="True" id="decimal_price" model="decimal.precision">
|
|
||||||
<field name="name">Expense Unit Price</field>
|
|
||||||
<field name="digits">3</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<field name="reference" position="after">
|
<field name="reference" position="after">
|
||||||
<field name="private_car_expense" invisible="1"/>
|
<field name="private_car_expense" invisible="1"/>
|
||||||
<field name="private_car_plate" attrs="{'invisible': [('private_car_expense', '!=', True)]}"/>
|
<field name="private_car_plate" attrs="{'invisible': [('private_car_expense', '!=', True)]}"/>
|
||||||
|
<field name="private_car_km_price_id" attrs="{'invisible': [('private_car_expense', '!=', True)]}" groups="hr_expense.group_hr_expense_manager,account.group_account_manager"/>
|
||||||
</field>
|
</field>
|
||||||
<label for="payment_mode" position="attributes">
|
<label for="payment_mode" position="attributes">
|
||||||
<attribute name="invisible">1</attribute>
|
<attribute name="invisible">1</attribute>
|
||||||
|
|||||||
@@ -1,89 +1,42 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# © 2016-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
# © 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import api, SUPERUSER_ID
|
from odoo import api, SUPERUSER_ID
|
||||||
|
|
||||||
PRODUCTS = {
|
KM_PRICES = {
|
||||||
'FR': [
|
'FR': [
|
||||||
{'default_code': '1-3CV_0-5000km',
|
{'name': u"[FR] 1-3 CV, < 5 000 km", 'unit_amount': 0.41},
|
||||||
'name': u"Frais kilométriques: 1-3 CV, < 5 000 km",
|
{'name': u"[FR] 4 CV, < 5 000 km", 'unit_amount': 0.493},
|
||||||
'cost': 0.41},
|
{'name': u"[FR] 5 CV, < 5 000 km", 'unit_amount': 0.543},
|
||||||
{'default_code': '4CV_0-5000km',
|
{'name': u"[FR] 6 CV, < 5 000 km", 'unit_amount': 0.568},
|
||||||
'name': u"Frais kilométriques: 4 CV, < 5 000 km",
|
{'name': u"[FR] 7+ CV, < 5 000 km", 'unit_amount': 0.595},
|
||||||
'cost': 0.493},
|
{'name': u"[FR] 1-3 CV, 5-20 000 km", 'unit_amount': 0.245},
|
||||||
{'default_code': '5CV_0-5000km',
|
{'name': u"[FR] 4 CV, 5-20 000 km", 'unit_amount': 0.277},
|
||||||
'name': u"Frais kilométriques: 5 CV, < 5 000 km",
|
{'name': u"[FR] 5 CV, 5-20 000 km", 'unit_amount': 0.305},
|
||||||
'cost': 0.543},
|
{'name': u"[FR] 6 CV, 5-20 000 km", 'unit_amount': 0.32},
|
||||||
{'default_code': '6CV_0-5000km',
|
{'name': u"[FR] 7+ CV, 5-20 000 km", 'unit_amount': 0.337},
|
||||||
'name': u"Frais kilométriques: 6 CV, < 5 000 km",
|
{'name': u"[FR] 1-3 CV, > 20 000 km", 'unit_amount': 0.245},
|
||||||
'cost': 0.568},
|
{'name': u"[FR] 4 CV, > 20 000 km", 'unit_amount': 0.277},
|
||||||
{'default_code': '7+CV_0-5000km',
|
{'name': u"[FR] 5 CV, > 20 000 km", 'unit_amount': 0.305},
|
||||||
'name': u"Frais kilométriques: 7+ CV, < 5 000 km",
|
{'name': u"[FR] 6 CV, > 20 000 km", 'unit_amount': 0.32},
|
||||||
'cost': 0.595},
|
{'name': u"[FR] 7+ CV, > 20 000 km", 'unit_amount': 0.337},
|
||||||
{'default_code': '1-3CV_5-20000km',
|
|
||||||
'name': u"Frais kilométriques: 1-3 CV, 5-20 000 km",
|
|
||||||
'cost': 0.245},
|
|
||||||
{'default_code': '4CV_5-20000km',
|
|
||||||
'name': u"Frais kilométriques: 4 CV, 5-20 000 km",
|
|
||||||
'cost': 0.277},
|
|
||||||
{'default_code': '5CV_5-20000km',
|
|
||||||
'name': u"Frais kilométriques: 5 CV, 5-20 000 km",
|
|
||||||
'cost': 0.305},
|
|
||||||
{'default_code': '6CV_5-20000km',
|
|
||||||
'name': u"Frais kilométriques: 6 CV, 5-20 000 km",
|
|
||||||
'cost': 0.32},
|
|
||||||
{'default_code': '7+CV_5-20000km',
|
|
||||||
'name': u"Frais kilométriques: 7+ CV, 5-20 000 km",
|
|
||||||
'cost': 0.337},
|
|
||||||
{'default_code': '1-3CV_+20000km',
|
|
||||||
'name': u"Frais kilométriques: 1-3 CV, > 20 000 km",
|
|
||||||
'cost': 0.245},
|
|
||||||
{'default_code': '4CV_+20000km',
|
|
||||||
'name': u"Frais kilométriques: 4 CV, > 20 000 km",
|
|
||||||
'cost': 0.277},
|
|
||||||
{'default_code': '5CV_+20000km',
|
|
||||||
'name': u"Frais kilométriques: 5 CV, > 20 000 km",
|
|
||||||
'cost': 0.305},
|
|
||||||
{'default_code': '6CV_+20000km',
|
|
||||||
'name': u"Frais kilométriques: 6 CV, > 20 000 km",
|
|
||||||
'cost': 0.32},
|
|
||||||
{'default_code': '7+CV_+20000km',
|
|
||||||
'name': u"Frais kilométriques: 7+ CV, > 20 000 km",
|
|
||||||
'cost': 0.337},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def create_private_car_expense_products(cr, registry):
|
def create_private_car_km_prices(cr, registry):
|
||||||
with api.Environment.manage():
|
with api.Environment.manage():
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
companies = env['res.company'].search([])
|
companies = env['res.company'].search([])
|
||||||
country_codes = []
|
|
||||||
for company in companies:
|
for company in companies:
|
||||||
company_country_code = company.country_id.code and\
|
company_country_code = company.country_id.code and\
|
||||||
company.country_id.code.upper() or False
|
company.country_id.code.upper() or False
|
||||||
if company_country_code not in country_codes:
|
if company_country_code in KM_PRICES:
|
||||||
country_codes.append(company_country_code)
|
for record in KM_PRICES[company_country_code]:
|
||||||
categ_id = env.ref('hr_expense.cat_expense').id
|
env['private.car.km.price'].create({
|
||||||
km_uom_id = env.ref('product.product_uom_km').id
|
'name': record['name'],
|
||||||
for country_code in country_codes:
|
'unit_amount': record['unit_amount'],
|
||||||
if country_code in PRODUCTS:
|
'company_id': company.id,
|
||||||
for product in PRODUCTS[country_code]:
|
|
||||||
env['product.product'].create({
|
|
||||||
'name': product['name'],
|
|
||||||
'default_code': product.get('default_code'),
|
|
||||||
'categ_id': categ_id,
|
|
||||||
'sale_ok': False,
|
|
||||||
'purchase_ok': False,
|
|
||||||
'can_be_expensed': False,
|
|
||||||
'private_car_expense_ok': True,
|
|
||||||
'type': 'service',
|
|
||||||
'list_price': False,
|
|
||||||
'standard_price': product['cost'],
|
|
||||||
'uom_id': km_uom_id,
|
|
||||||
'uom_po_id': km_uom_id,
|
|
||||||
'taxes_id': False,
|
|
||||||
'supplier_taxes_id': False,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|||||||
18
hr_expense_usability/pre_install.py
Normal file
18
hr_expense_usability/pre_install.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
|
||||||
|
|
||||||
|
# I can't create decimal precision via hr_expense_data.xml because it's
|
||||||
|
# too "late" in the module installation process: in this case,
|
||||||
|
# demo data and post_install.py data have a unit_amount truncated at 2 digits
|
||||||
|
# That's why I create the decimal precision via a pre_init_hook
|
||||||
|
def create_decimal_precision(cr):
|
||||||
|
with api.Environment.manage():
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
env['decimal.precision'].create({
|
||||||
|
'name': 'Expense Unit Price',
|
||||||
|
'digits': 3,
|
||||||
|
})
|
||||||
@@ -7,41 +7,29 @@
|
|||||||
|
|
||||||
<odoo noupdate="1">
|
<odoo noupdate="1">
|
||||||
|
|
||||||
<record id="demo_private_car_expense" model="product.product">
|
<record id="demo_private_car_km_price" model="private.car.km.price">
|
||||||
<field name="name">1-2CV 0-12000km Private Car Expenses</field>
|
<field name="name">France: 1-2CV 0-12000km</field>
|
||||||
<field name="default_code">1-2CV/0-12000km</field>
|
<field name="unit_amount">0.423</field>
|
||||||
<field name="categ_id" ref="hr_expense.cat_expense"/>
|
|
||||||
<field name="sale_ok" eval="False"/>
|
|
||||||
<field name="purchase_ok" eval="False"/>
|
|
||||||
<field name="can_be_expensed" eval="False"/>
|
|
||||||
<field name="private_car_expense_ok" eval="True"/>
|
|
||||||
<field name="type">service</field>
|
|
||||||
<field name="list_price">0</field>
|
|
||||||
<field name="standard_price">0.42</field>
|
|
||||||
<field name="uom_id" ref="product.product_uom_km"/>
|
|
||||||
<field name="uom_po_id" ref="product.product_uom_km"/>
|
|
||||||
<field name="taxes_id" eval="False"/>
|
|
||||||
<field name="supplier_taxes_id" eval="False"/>
|
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="hr.employee_root" model="hr.employee">
|
<record id="hr.employee_root" model="hr.employee">
|
||||||
<field name="private_car_plate">OD 4212 OO</field>
|
<field name="private_car_plate">OD 4212 OO</field>
|
||||||
<field name="private_car_product_id" ref="demo_private_car_expense"/>
|
<field name="private_car_km_price_id" ref="demo_private_car_km_price"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="hr.employee_mit" model="hr.employee">
|
<record id="hr.employee_mit" model="hr.employee">
|
||||||
<field name="private_car_plate">OE 1234 EO</field>
|
<field name="private_car_plate">OE 1234 EO</field>
|
||||||
<field name="private_car_product_id" ref="demo_private_car_expense"/>
|
<field name="private_car_km_price_id" ref="demo_private_car_km_price"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="hr.employee_al" model="hr.employee">
|
<record id="hr.employee_al" model="hr.employee">
|
||||||
<field name="private_car_plate">BE 6543 AL</field>
|
<field name="private_car_plate">BE 6543 AL</field>
|
||||||
<field name="private_car_product_id" ref="demo_private_car_expense"/>
|
<field name="private_car_km_price_id" ref="demo_private_car_km_price"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="hr.employee_qdp" model="hr.employee">
|
<record id="hr.employee_qdp" model="hr.employee">
|
||||||
<field name="private_car_plate">BE 1235 QD</field>
|
<field name="private_car_plate">BE 1235 QD</field>
|
||||||
<field name="private_car_product_id" ref="demo_private_car_expense"/>
|
<field name="private_car_km_price_id" ref="demo_private_car_km_price"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
55
hr_expense_usability/private_car_km_price_view.xml
Normal file
55
hr_expense_usability/private_car_km_price_view.xml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
© 2017 Akretion (http://www.akretion.com)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="private_car_km_price_form" model="ir.ui.view">
|
||||||
|
<field name="name">private_car_km_price.form</field>
|
||||||
|
<field name="model">private.car.km.price</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Private Car Km Price">
|
||||||
|
<div class="oe_button_box" name="button_box">
|
||||||
|
<button name="toggle_active" type="object"
|
||||||
|
class="oe_stat_button" icon="fa-archive">
|
||||||
|
<field name="active" widget="boolean_button"
|
||||||
|
options='{"terminology": "archive"}'/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<group name="main">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="unit_amount"/>
|
||||||
|
<field name="company_id" groups="base.group_multi_company"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="private_car_km_price_tree" model="ir.ui.view">
|
||||||
|
<field name="name">private_car_km_price.tree</field>
|
||||||
|
<field name="model">private.car.km.price</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Private Car Km Prices">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="unit_amount"/>
|
||||||
|
<field name="company_id" groups="base.group_multi_company"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="private_car_km_price_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Private Car Km Prices</field>
|
||||||
|
<field name="res_model">private.car.km.price</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="private_car_km_price_menu"
|
||||||
|
parent="hr_expense.menu_hr_expense_configuration"
|
||||||
|
action="private_car_km_price_action"
|
||||||
|
groups="account.group_account_manager,hr_expense.group_hr_expense_manager"
|
||||||
|
sequence="50"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -6,20 +6,6 @@
|
|||||||
-->
|
-->
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_product_hr_expense_form" model="ir.ui.view">
|
|
||||||
<field name="name">private.car.expense.product.template.form</field>
|
|
||||||
<field name="model">product.template</field>
|
|
||||||
<field name="inherit_id" ref="hr_expense.view_product_hr_expense_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<div name="options" position="inside">
|
|
||||||
<div name="private_car_expense">
|
|
||||||
<field name="private_car_expense_ok"/>
|
|
||||||
<label for="private_car_expense_ok"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="product_template_search_view" model="ir.ui.view">
|
<record id="product_template_search_view" model="ir.ui.view">
|
||||||
<field name="name">private.car.expense.product.template.search</field>
|
<field name="name">private.car.expense.product.template.search</field>
|
||||||
<field name="model">product.template</field>
|
<field name="model">product.template</field>
|
||||||
@@ -28,20 +14,8 @@
|
|||||||
<filter name="filter_to_purchase" position="after">
|
<filter name="filter_to_purchase" position="after">
|
||||||
<filter name="can_be_expensed" domain="[('can_be_expensed', '=', True)]"
|
<filter name="can_be_expensed" domain="[('can_be_expensed', '=', True)]"
|
||||||
string="Can be Expensed"/>
|
string="Can be Expensed"/>
|
||||||
<filter string="Private Car Expense" name="private_car_expense_ok"
|
|
||||||
domain="[('private_car_expense_ok', '=', 1)]"/>
|
|
||||||
</filter>
|
</filter>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="private_car_product_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Private Car Expenses</field>
|
|
||||||
<field name="res_model">product.product</field>
|
|
||||||
<field name="view_mode">tree,form,kanban</field>
|
|
||||||
<field name="context">{'default_private_car_expense_ok': 1, 'search_default_private_car_expense_ok': 1}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem id="private_car_product_menu" parent="hr_expense.menu_hr_expense_configuration"
|
|
||||||
action="private_car_product_action" groups="hr_expense.group_hr_expense_manager"/>
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -15,5 +15,10 @@
|
|||||||
<field name="groups" eval="[(4, ref('account.group_account_user')), (4, ref('hr_expense.group_hr_expense_manager')), (4, ref('hr_expense.group_hr_expense_user'))]"/>
|
<field name="groups" eval="[(4, ref('account.group_account_user')), (4, ref('hr_expense.group_hr_expense_manager')), (4, ref('hr_expense.group_hr_expense_user'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="private_car_km_price_multicompany" model="ir.rule">
|
||||||
|
<field name="name">Private Car Kilometer Prices Multi-company</field>
|
||||||
|
<field name="model_id" ref="model_private_car_km_price"/>
|
||||||
|
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
4
hr_expense_usability/security/ir.model.access.csv
Normal file
4
hr_expense_usability/security/ir.model.access.csv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_private_car_km_price_read,Read access on private.car.km.price to employees,model_private_car_km_price,base.group_user,1,0,0,0
|
||||||
|
access_private_car_km_price_full,Full access on private.car.km.price to HR Expense Manager,model_private_car_km_price,hr_expense.group_hr_expense_manager,1,1,1,1
|
||||||
|
access_private_car_km_price_full,Full access on private.car.km.price to Accounting Manager,model_private_car_km_price,account.group_account_manager,1,1,1,1
|
||||||
|
Reference in New Issue
Block a user