diff --git a/hr_expense_usability/__init__.py b/hr_expense_usability/__init__.py index 87a7dee..23553c5 100644 --- a/hr_expense_usability/__init__.py +++ b/hr_expense_usability/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- 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 diff --git a/hr_expense_usability/__manifest__.py b/hr_expense_usability/__manifest__.py index 285a513..9008d2d 100644 --- a/hr_expense_usability/__manifest__.py +++ b/hr_expense_usability/__manifest__.py @@ -16,13 +16,16 @@ 'hr_expense_sequence', ], 'data': [ + 'hr_expense_data.xml', 'hr_employee_view.xml', 'hr_expense_view.xml', 'product_view.xml', - 'hr_expense_data.xml', + 'private_car_km_price_view.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'], 'installable': True, } diff --git a/hr_expense_usability/hr_employee_view.xml b/hr_expense_usability/hr_employee_view.xml index e50d3b3..4cf5e86 100644 --- a/hr_expense_usability/hr_employee_view.xml +++ b/hr_expense_usability/hr_employee_view.xml @@ -15,8 +15,7 @@ - + diff --git a/hr_expense_usability/hr_expense.py b/hr_expense_usability/hr_expense.py index b600641..30d16ba 100644 --- a/hr_expense_usability/hr_expense.py +++ b/hr_expense_usability/hr_expense.py @@ -33,24 +33,6 @@ import odoo.addons.decimal_precision as dp class ProductTemplate(models.Model): _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 @api.onchange('can_be_expensed') def onchange_can_be_expensed(self): @@ -58,31 +40,12 @@ class ProductTemplate(models.Model): unit_uom = self.env.ref('product.product_uom_unit') self.type = 'service' self.list_price = 0.0 - self.private_car_expense_ok = False self.sale_ok = False self.purchase_ok = False self.uom_id = unit_uom.id self.po_uom_id = unit_uom.id 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 # But the same constrain is also on hr.expense @api.constrains('supplier_taxes_id', 'can_be_expensed') @@ -107,21 +70,6 @@ class ProductTemplate(models.Model): class ProductProduct(models.Model): _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 @api.onchange('can_be_expensed') def onchange_can_be_expensed(self): @@ -129,7 +77,6 @@ class ProductProduct(models.Model): unit_uom = self.env.ref('product.product_uom_unit') self.type = 'service' self.list_price = 0.0 - self.private_car_expense_ok = False self.sale_ok = False self.purchase_ok = False self.uom_id = unit_uom.id @@ -137,13 +84,29 @@ class ProductProduct(models.Model): 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): _inherit = 'hr.employee' def compute_private_car_total_km_this_year(self): res = {} - private_car_products = self.env['product.product'].search( - [('private_car_expense_ok', '=', True)]) + private_car_product_id = self.env.ref( + 'hr_expense_usability.generic_private_car_expense').id today = fields.Date.context_today(self) today_dt = fields.Date.from_string(today) self._cr.execute( @@ -152,11 +115,11 @@ class HrEmployee(models.Model): FROM hr_expense el WHERE el.state NOT IN ('draft', 'cancel') AND el.employee_id IN %s - AND el.product_id IN %s + AND el.product_id=%s AND EXTRACT(year FROM el.date) = %s 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(): res[line['employee_id']] = line['sum'] for empl in self: @@ -165,9 +128,8 @@ class HrEmployee(models.Model): private_car_plate = fields.Char( 'Private Car Plate', size=32, copy=False, track_visibility='onchange', help="This field will be copied on the expenses of this employee.") - private_car_product_id = fields.Many2one( - 'product.product', string='Private Car Product', copy=False, - domain=[('private_car_expense_ok', '=', True)], + private_car_km_price_id = fields.Many2one( + '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.") private_car_total_km_this_year = fields.Float( @@ -213,8 +175,12 @@ class HrExpense(models.Model): private_car_plate = fields.Char( string='Private Car Plate', size=32, track_visibility='onchange', readonly=True, states={'draft': [('readonly', False)]}) - private_car_expense = fields.Boolean( - related='product_id.private_car_expense_ok', readonly=True, store=True) + private_car_km_price_id = fields.Many2one( + '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( string='Tax Amount', currency_field='currency_id', readonly=True, states={'draft': [('readonly', False)]}) @@ -277,27 +243,42 @@ class HrExpense(models.Model): @api.onchange('product_id') def _onchange_product_id(self): + private_car_product = self.env.ref( + 'hr_expense_usability.generic_private_car_expense') if ( - self.product_id and self.product_id == self.env.ref( - 'hr_expense_usability.generic_private_car_expense')): - if not self.employee_id.private_car_product_id: + self.product_id and + self.product_id == private_car_product and + self.employee_id): + if not self.employee_id.private_car_km_price_id: 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) if not self.employee_id.private_car_plate: raise UserError(_( "Missing Private Car Plate on the configuration of " "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_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() + @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') def _onchange_unit_amount(self): res = {} - if self.product_id.private_car_expense_ok: - original_unit_amount = self.product_id.price_compute( - 'standard_price')[self.product_id.id] + if self.private_car_expense: + original_unit_amount = self.private_car_km_price_id.unit_amount prec = self.env['decimal.precision'].precision_get( 'Expense Unit Price') if float_compare( @@ -338,21 +319,23 @@ class HrExpense(models.Model): 'hr_expense_usability.generic_private_car_expense') for exp in self: 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: raise ValidationError(_( "Missing 'Private Car Plate' on the " "expense '%s' of employee '%s'.") % (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: raise ValidationError(_( "The expense '%s' is a private car expense " diff --git a/hr_expense_usability/hr_expense_data.xml b/hr_expense_usability/hr_expense_data.xml index 040b47f..e7bd2b7 100644 --- a/hr_expense_usability/hr_expense_data.xml +++ b/hr_expense_usability/hr_expense_data.xml @@ -21,14 +21,8 @@ + - - - Expense Unit Price - 3 - diff --git a/hr_expense_usability/hr_expense_view.xml b/hr_expense_usability/hr_expense_view.xml index 64e65c2..146b160 100644 --- a/hr_expense_usability/hr_expense_view.xml +++ b/hr_expense_usability/hr_expense_view.xml @@ -16,6 +16,7 @@ + - - Private Car Expenses - product.product - tree,form,kanban - {'default_private_car_expense_ok': 1, 'search_default_private_car_expense_ok': 1} - - - - diff --git a/hr_expense_usability/security/expense_security.xml b/hr_expense_usability/security/expense_security.xml index 51d161a..eb1b162 100644 --- a/hr_expense_usability/security/expense_security.xml +++ b/hr_expense_usability/security/expense_security.xml @@ -15,5 +15,10 @@ + + Private Car Kilometer Prices Multi-company + + ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])] + diff --git a/hr_expense_usability/security/ir.model.access.csv b/hr_expense_usability/security/ir.model.access.csv new file mode 100644 index 0000000..6de7267 --- /dev/null +++ b/hr_expense_usability/security/ir.model.access.csv @@ -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