From 572053d9f59b037070ae833b1beaef82edbfb1fa Mon Sep 17 00:00:00 2001 From: Kinjal Mehta Date: Tue, 24 May 2016 15:46:29 +0530 Subject: [PATCH] [MOV] sale_timesheet: reorganize files and split models and views into their own files --- addons/sale_timesheet/__openerp__.py | 16 +-- .../{demo => data}/sale_service_demo.xml | 8 +- .../data/sale_timesheet_demo.xml | 14 --- addons/sale_timesheet/models/__init__.py | 9 +- addons/sale_timesheet/models/account.py | 70 ++++++++++++ addons/sale_timesheet/models/hr.py | 10 ++ .../{sale_service.py => procurement.py} | 52 --------- addons/sale_timesheet/models/product.py | 31 ++++++ addons/sale_timesheet/models/project.py | 45 ++++++++ addons/sale_timesheet/models/res_company.py | 16 +++ .../{sale_timesheet.py => sale_order.py} | 100 ------------------ addons/sale_timesheet/views/hr_views.xml | 17 +++ .../views/procurement_views.xml | 15 +++ addons/sale_timesheet/views/product_views.xml | 19 ++++ ...ervice_view.xml => project_task_views.xml} | 11 -- ...imesheet_view.xml => sale_order_views.xml} | 28 ----- 16 files changed, 246 insertions(+), 215 deletions(-) rename addons/sale_timesheet/{demo => data}/sale_service_demo.xml (78%) delete mode 100644 addons/sale_timesheet/data/sale_timesheet_demo.xml create mode 100644 addons/sale_timesheet/models/account.py create mode 100644 addons/sale_timesheet/models/hr.py rename addons/sale_timesheet/models/{sale_service.py => procurement.py} (70%) create mode 100644 addons/sale_timesheet/models/product.py create mode 100644 addons/sale_timesheet/models/project.py create mode 100644 addons/sale_timesheet/models/res_company.py rename addons/sale_timesheet/models/{sale_timesheet.py => sale_order.py} (62%) create mode 100644 addons/sale_timesheet/views/hr_views.xml create mode 100644 addons/sale_timesheet/views/procurement_views.xml create mode 100644 addons/sale_timesheet/views/product_views.xml rename addons/sale_timesheet/views/{sale_service_view.xml => project_task_views.xml} (72%) rename addons/sale_timesheet/views/{sale_timesheet_view.xml => sale_order_views.xml} (60%) diff --git a/addons/sale_timesheet/__openerp__.py b/addons/sale_timesheet/__openerp__.py index e26f1bbc754..543dad375e7 100644 --- a/addons/sale_timesheet/__openerp__.py +++ b/addons/sale_timesheet/__openerp__.py @@ -3,7 +3,6 @@ { 'name': 'Sales Timesheet', - 'version': '1.0', 'category': 'Hidden', 'summary': 'Sell based on timesheets', 'description': """ @@ -16,13 +15,16 @@ have real delivered quantities in sales orders. """, 'website': 'https://www.odoo.com/page/warehouse', 'depends': ['sale', 'hr_timesheet'], - 'data': ['views/sale_timesheet_view.xml', - 'data/sale_timesheet_data.xml', - 'views/sale_service_view.xml', + 'data': [ + 'data/sale_timesheet_data.xml', + 'views/sale_order_views.xml', + 'views/product_views.xml', + 'views/hr_views.xml', + 'views/project_task_views.xml', + 'views/procurement_views.xml', ], - 'demo': ['data/sale_timesheet_demo.xml', - 'demo/sale_service_demo.xml', + 'demo': [ + 'data/sale_service_demo.xml', ], - 'installable': True, 'auto_install': True, } diff --git a/addons/sale_timesheet/demo/sale_service_demo.xml b/addons/sale_timesheet/data/sale_service_demo.xml similarity index 78% rename from addons/sale_timesheet/demo/sale_service_demo.xml rename to addons/sale_timesheet/data/sale_service_demo.xml index ffaa23d68d0..92d989fb2eb 100644 --- a/addons/sale_timesheet/demo/sale_service_demo.xml +++ b/addons/sale_timesheet/data/sale_service_demo.xml @@ -1,7 +1,13 @@ - + + timesheet + + + Support Contract (on timesheet) + timesheet + diff --git a/addons/sale_timesheet/data/sale_timesheet_demo.xml b/addons/sale_timesheet/data/sale_timesheet_demo.xml deleted file mode 100644 index 4544c8f50de..00000000000 --- a/addons/sale_timesheet/data/sale_timesheet_demo.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - timesheet - - - Support Contract (on timesheet) - timesheet - - - - diff --git a/addons/sale_timesheet/models/__init__.py b/addons/sale_timesheet/models/__init__.py index 2ffaf67ee14..587b31b8202 100644 --- a/addons/sale_timesheet/models/__init__.py +++ b/addons/sale_timesheet/models/__init__.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import sale_timesheet -import sale_service +import account +import hr +import procurement +import product +import project +import res_company +import sale_order diff --git a/addons/sale_timesheet/models/account.py b/addons/sale_timesheet/models/account.py new file mode 100644 index 00000000000..0250d4252b4 --- /dev/null +++ b/addons/sale_timesheet/models/account.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp import models, api + + +class AccountAnalyticLine(models.Model): + _inherit = 'account.analytic.line' + + def _get_sale_order_line(self, vals=None): + result = dict(vals or {}) + if self.project_id: + if result.get('so_line'): + sol = self.env['sale.order.line'].browse([result['so_line']]) + else: + sol = self.so_line + if not sol: + sol = self.env['sale.order.line'].search([ + ('order_id.project_id', '=', self.account_id.id), + ('state', '=', 'sale'), + ('product_id.track_service', '=', 'timesheet'), + ('product_id.type', '=', 'service')], + limit=1) + if sol: + result.update({ + 'so_line': sol.id, + 'product_id': sol.product_id.id, + }) + result = self._get_timesheet_cost(result) + + result = super(AccountAnalyticLine, self)._get_sale_order_line(vals=result) + return result + + def _get_timesheet_cost(self, vals=None): + result = dict(vals or {}) + if result.get('project_id') or self.project_id: + if result.get('amount'): + return result + unit_amount = result.get('unit_amount', 0.0) or self.unit_amount + user_id = result.get('user_id') or self.user_id.id or self._default_user() + user = self.env['res.users'].browse([user_id]) + emp = self.env['hr.employee'].search([('user_id', '=', user_id)], limit=1) + cost = emp and emp.timesheet_cost or 0.0 + uom = (emp or user).company_id.project_time_mode_id + # Nominal employee cost = 1 * company project UoM (project_time_mode_id) + result.update( + amount=(-unit_amount * cost), + product_uom_id=uom.id + ) + return result + + @api.model + def _update_values(self, values): + if values.get('task_id', False): + task = self.env['project.task'].browse(values['task_id']) + values['so_line'] = task.sale_line_id and task.sale_line_id.id or values.get('so_line', False) + + @api.multi + def write(self, values): + self._update_values(values) + for line in self: + values = line._get_timesheet_cost(vals=values) + super(AccountAnalyticLine, line).write(values) + return True + + @api.model + def create(self, values): + self._update_values(values) + values = self._get_timesheet_cost(vals=values) + return super(AccountAnalyticLine, self).create(values) diff --git a/addons/sale_timesheet/models/hr.py b/addons/sale_timesheet/models/hr.py new file mode 100644 index 00000000000..5ee69e82d16 --- /dev/null +++ b/addons/sale_timesheet/models/hr.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp import models, fields + + +class HrEmployee(models.Model): + _inherit = 'hr.employee' + # FIXME: this field should be in module hr_timesheet, not sale_timesheet + timesheet_cost = fields.Float(string='Timesheet Cost', default=0.0) diff --git a/addons/sale_timesheet/models/sale_service.py b/addons/sale_timesheet/models/procurement.py similarity index 70% rename from addons/sale_timesheet/models/sale_service.py rename to addons/sale_timesheet/models/procurement.py index 0bd0b1b00c2..ba07285cb34 100644 --- a/addons/sale_timesheet/models/sale_service.py +++ b/addons/sale_timesheet/models/procurement.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from openerp import api, models from openerp.osv import fields, osv -from openerp.exceptions import UserError from openerp.tools.translate import _ @@ -85,53 +83,3 @@ class procurement_order(osv.osv): self.message_post(cr, uid, [procurement.id], body=body, context=context) if procurement.sale_line_id and procurement.sale_line_id.order_id: procurement.sale_line_id.order_id.message_post(body=body) - - -class project_task(osv.osv): - _name = "project.task" - _inherit = "project.task" - _columns = { - 'procurement_id': fields.many2one('procurement.order', 'Procurement', ondelete='set null'), - 'sale_line_id': fields.related('procurement_id', 'sale_line_id', type='many2one', relation='sale.order.line', store=True, string='Sales Order Line'), - } - - def unlink(self, cr, uid, ids, context=None): - if context is None: - context = {} - for task in self.browse(cr, uid, ids, context=context): - if task.sale_line_id: - raise UserError(_('You cannot delete a task related to a Sale Order. You can only archive this task.')) - res = super(project_task, self).unlink(cr, uid, ids, context) - return res - - def action_view_so(self, cr, uid, ids, context=None): - task = self.browse(cr, uid, ids, context=context)[0] - return { - "type": "ir.actions.act_window", - "res_model": "sale.order", - "views": [[False, "form"]], - "res_id": task.sale_line_id.order_id.id, - "context": {"create": False, "show_sale": True}, - } - - def onchange_parent_id(self, cr, uid, ids, parent_id, context=None): - if not parent_id: - return {'value' : {'procurement_id': False, 'sale_line_id': False }} - parent_task = self.browse(cr, uid, parent_id, context=context) - return { - 'value' : { - 'procurement_id' : parent_task.procurement_id.id, - 'sale_line_id' : parent_task.sale_line_id.id, - } - } - - -class ProductProduct(models.Model): - _inherit = 'product.product' - - @api.multi - def _need_procurement(self): - for product in self: - if product.type == 'service' and product.track_service == 'task': - return True - return super(ProductProduct, self)._need_procurement() diff --git a/addons/sale_timesheet/models/product.py b/addons/sale_timesheet/models/product.py new file mode 100644 index 00000000000..bc67ec9b68c --- /dev/null +++ b/addons/sale_timesheet/models/product.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + track_service = fields.Selection(selection_add=[('timesheet', 'Timesheets on project'), ('task', 'Create a task and track hours')]) + project_id = fields.Many2one('project.project', string='Project', + help='Create a task under this project on sale order validation. This setting must be set for each company.', + company_dependent=True) + + @api.onchange('type', 'invoice_policy') + def onchange_type_timesheet(self): + if self.type == 'service': + self.track_service = 'timesheet' + else: + self.track_service = 'manual' + return {} + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + @api.multi + def _need_procurement(self): + for product in self: + if product.type == 'service' and product.track_service == 'task': + return True + return super(ProductProduct, self)._need_procurement() diff --git a/addons/sale_timesheet/models/project.py b/addons/sale_timesheet/models/project.py new file mode 100644 index 00000000000..8b6637ece2c --- /dev/null +++ b/addons/sale_timesheet/models/project.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp.osv import fields, osv +from openerp.exceptions import UserError +from openerp.tools.translate import _ + + +class project_task(osv.osv): + _name = "project.task" + _inherit = "project.task" + _columns = { + 'procurement_id': fields.many2one('procurement.order', 'Procurement', ondelete='set null'), + 'sale_line_id': fields.related('procurement_id', 'sale_line_id', type='many2one', relation='sale.order.line', store=True, string='Sales Order Line'), + } + + def unlink(self, cr, uid, ids, context=None): + if context is None: + context = {} + for task in self.browse(cr, uid, ids, context=context): + if task.sale_line_id: + raise UserError(_('You cannot delete a task related to a Sale Order. You can only archive this task.')) + res = super(project_task, self).unlink(cr, uid, ids, context) + return res + + def action_view_so(self, cr, uid, ids, context=None): + task = self.browse(cr, uid, ids, context=context)[0] + return { + "type": "ir.actions.act_window", + "res_model": "sale.order", + "views": [[False, "form"]], + "res_id": task.sale_line_id.order_id.id, + "context": {"create": False, "show_sale": True}, + } + + def onchange_parent_id(self, cr, uid, ids, parent_id, context=None): + if not parent_id: + return {'value' : {'procurement_id': False, 'sale_line_id': False }} + parent_task = self.browse(cr, uid, parent_id, context=context) + return { + 'value' : { + 'procurement_id' : parent_task.procurement_id.id, + 'sale_line_id' : parent_task.sale_line_id.id, + } + } diff --git a/addons/sale_timesheet/models/res_company.py b/addons/sale_timesheet/models/res_company.py new file mode 100644 index 00000000000..f8b279a8225 --- /dev/null +++ b/addons/sale_timesheet/models/res_company.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp import models, api, fields + + +class ResCompany(models.Model): + _inherit = 'res.company' + + @api.model + def _get_uom_hours(self): + try: + return self.env.ref("product.product_uom_hour") + except ValueError: + return False + project_time_mode_id = fields.Many2one('product.uom', string='Timesheet UoM', default=_get_uom_hours) diff --git a/addons/sale_timesheet/models/sale_timesheet.py b/addons/sale_timesheet/models/sale_order.py similarity index 62% rename from addons/sale_timesheet/models/sale_timesheet.py rename to addons/sale_timesheet/models/sale_order.py index ea9fc992505..51f288ca399 100644 --- a/addons/sale_timesheet/models/sale_timesheet.py +++ b/addons/sale_timesheet/models/sale_order.py @@ -7,106 +7,6 @@ from openerp.tools.translate import _ from openerp.exceptions import ValidationError -class ResCompany(models.Model): - _inherit = 'res.company' - - @api.model - def _get_uom_hours(self): - try: - return self.env.ref("product.product_uom_hour") - except ValueError: - return False - project_time_mode_id = fields.Many2one('product.uom', string='Timesheet UoM', default=_get_uom_hours) - - -class HrEmployee(models.Model): - _inherit = 'hr.employee' - # FIXME: this field should be in module hr_timesheet, not sale_timesheet - timesheet_cost = fields.Float(string='Timesheet Cost', default=0.0) - - -class ProductTemplate(models.Model): - _inherit = 'product.template' - track_service = fields.Selection(selection_add=[('timesheet', 'Timesheets on project'), ('task', 'Create a task and track hours')]) - project_id = fields.Many2one('project.project', string='Project', - help='Create a task under this project on sale order validation. This setting must be set for each company.', - company_dependent=True) - - @api.onchange('type', 'invoice_policy') - def onchange_type_timesheet(self): - if self.type == 'service': - self.track_service = 'timesheet' - else: - self.track_service = 'manual' - return {} - - -class AccountAnalyticLine(models.Model): - _inherit = 'account.analytic.line' - - def _get_sale_order_line(self, vals=None): - result = dict(vals or {}) - if self.project_id: - if result.get('so_line'): - sol = self.env['sale.order.line'].browse([result['so_line']]) - else: - sol = self.so_line - if not sol: - sol = self.env['sale.order.line'].search([ - ('order_id.project_id', '=', self.account_id.id), - ('state', '=', 'sale'), - ('product_id.track_service', '=', 'timesheet'), - ('product_id.type', '=', 'service')], - limit=1) - if sol: - result.update({ - 'so_line': sol.id, - 'product_id': sol.product_id.id, - }) - result = self._get_timesheet_cost(result) - - result = super(AccountAnalyticLine, self)._get_sale_order_line(vals=result) - return result - - def _get_timesheet_cost(self, vals=None): - result = dict(vals or {}) - if result.get('project_id') or self.project_id: - if result.get('amount'): - return result - unit_amount = result.get('unit_amount', 0.0) or self.unit_amount - user_id = result.get('user_id') or self.user_id.id or self._default_user() - user = self.env['res.users'].browse([user_id]) - emp = self.env['hr.employee'].search([('user_id', '=', user_id)], limit=1) - cost = emp and emp.timesheet_cost or 0.0 - uom = (emp or user).company_id.project_time_mode_id - # Nominal employee cost = 1 * company project UoM (project_time_mode_id) - result.update( - amount=(-unit_amount * cost), - product_uom_id=uom.id - ) - return result - - @api.model - def _update_values(self, values): - if values.get('task_id', False): - task = self.env['project.task'].browse(values['task_id']) - values['so_line'] = task.sale_line_id and task.sale_line_id.id or values.get('so_line', False) - - @api.multi - def write(self, values): - self._update_values(values) - for line in self: - values = line._get_timesheet_cost(vals=values) - super(AccountAnalyticLine, line).write(values) - return True - - @api.model - def create(self, values): - self._update_values(values) - values = self._get_timesheet_cost(vals=values) - return super(AccountAnalyticLine, self).create(values) - - class SaleOrder(models.Model): _inherit = 'sale.order' diff --git a/addons/sale_timesheet/views/hr_views.xml b/addons/sale_timesheet/views/hr_views.xml new file mode 100644 index 00000000000..bbba04e7282 --- /dev/null +++ b/addons/sale_timesheet/views/hr_views.xml @@ -0,0 +1,17 @@ + + + + + hr.timesheet.employee.extd_form + hr.employee + + + + + + + + + + + diff --git a/addons/sale_timesheet/views/procurement_views.xml b/addons/sale_timesheet/views/procurement_views.xml new file mode 100644 index 00000000000..63843516c0b --- /dev/null +++ b/addons/sale_timesheet/views/procurement_views.xml @@ -0,0 +1,15 @@ + + + + + procurement.procurement.form.view.inherit + procurement.order + + + + + + + + + diff --git a/addons/sale_timesheet/views/product_views.xml b/addons/sale_timesheet/views/product_views.xml new file mode 100644 index 00000000000..00d08ada78f --- /dev/null +++ b/addons/sale_timesheet/views/product_views.xml @@ -0,0 +1,19 @@ + + + + + product.template.timesheet.form + product.template + + + + False + {'invisible': [('type','!=','service')]} + + + + + + + + diff --git a/addons/sale_timesheet/views/sale_service_view.xml b/addons/sale_timesheet/views/project_task_views.xml similarity index 72% rename from addons/sale_timesheet/views/sale_service_view.xml rename to addons/sale_timesheet/views/project_task_views.xml index 68683c27021..9b954edcf4a 100644 --- a/addons/sale_timesheet/views/sale_service_view.xml +++ b/addons/sale_timesheet/views/project_task_views.xml @@ -1,17 +1,6 @@ - - procurement.procurement.form.view.inherit - procurement.order - - - - - - - - sale.service.form.view.inherit project.task diff --git a/addons/sale_timesheet/views/sale_timesheet_view.xml b/addons/sale_timesheet/views/sale_order_views.xml similarity index 60% rename from addons/sale_timesheet/views/sale_timesheet_view.xml rename to addons/sale_timesheet/views/sale_order_views.xml index 1f5a846b272..220ae93752b 100644 --- a/addons/sale_timesheet/views/sale_timesheet_view.xml +++ b/addons/sale_timesheet/views/sale_order_views.xml @@ -1,34 +1,6 @@ - - product.template.timesheet.form - product.template - - - - False - {'invisible': [('type','!=','service')]} - - - - - - - - - hr.timesheet.employee.extd_form - hr.employee - - - - - - - - - - sale.order.form.sale.timesheet sale.order