diff --git a/addons/hr_expense/models/product_template.py b/addons/hr_expense/models/product_template.py index 8b0d3057197..d6252889dfb 100644 --- a/addons/hr_expense/models/product_template.py +++ b/addons/hr_expense/models/product_template.py @@ -7,4 +7,5 @@ from openerp import fields, models class ProductTemplate(models.Model): _inherit = "product.template" - can_be_expensed = fields.Boolean(help="Specify whether the product can be selected in an HR expense.") + can_be_expensed = fields.Boolean(help="Specify whether the product can be selected in an HR expense.", string="Can be Expensed") + diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py index cf30384d41c..e675ea343aa 100644 --- a/addons/sale/res_config.py +++ b/addons/sale/res_config.py @@ -71,8 +71,7 @@ class sale_configuration(osv.TransientModel): 'Advanced pricing based on formula: You can have all the rights on pricelist'), 'default_invoice_policy': fields.selection([ ('order', 'Invoice ordered quantities'), - ('delivery', 'Invoice delivered quantities'), - ('cost', 'Invoice based on costs (time and material, expenses)') + ('delivery', 'Invoice delivered quantities') ], 'Default Invoicing', default_model='product.template'), 'deposit_product_id_setting': fields.many2one('product.product', 'Deposit Product',\ domain="[('type', '=', 'service')]",\ diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 7d5d4d484ec..13af8a676bb 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -449,11 +449,6 @@ class SaleOrder(models.Model): if self.env.context.get('send_email'): self.force_quotation_send() order.order_line._action_procurement_create() - if not order.project_id: - for line in order.order_line: - if line.product_id.invoice_policy == 'cost': - order._create_analytic_account() - break if self.env['ir.values'].get_default('sale.config.settings', 'auto_done_setting'): self.action_done() return True @@ -572,7 +567,7 @@ class SaleOrderLine(models.Model): @api.depends('product_id.invoice_policy', 'order_id.state') def _compute_qty_delivered_updateable(self): for line in self: - line.qty_delivered_updateable = line.product_id.invoice_policy in ('order', 'delivery') and line.order_id.state == 'sale' and line.product_id.track_service == 'manual' + line.qty_delivered_updateable = line.order_id.state == 'sale' and line.product_id.track_service == 'manual' @api.depends('qty_invoiced', 'qty_delivered', 'product_uom_qty', 'order_id.state') def _get_to_invoice_qty(self): @@ -671,10 +666,6 @@ class SaleOrderLine(models.Model): new_procs.run() return new_procs - @api.model - def _get_analytic_invoice_policy(self): - return ['cost'] - @api.model def _get_analytic_track_service(self): return [] @@ -691,8 +682,7 @@ class SaleOrderLine(models.Model): line = super(SaleOrderLine, self).create(values) if line.state == 'sale': if (not line.order_id.project_id and - (line.product_id.track_service in self._get_analytic_track_service() or - line.product_id.invoice_policy in self._get_analytic_invoice_policy())): + (line.product_id.track_service in self._get_analytic_track_service())): line.order_id._create_analytic_account() line._action_procurement_create() @@ -1004,7 +994,10 @@ class ProductTemplate(models.Model): default='manual') sale_line_warn = fields.Selection(WARNING_MESSAGE, 'Sales Order Line', help=WARNING_HELP, required=True, default="no-message") sale_line_warn_msg = fields.Text('Message for Sales Order Line') - + expense_policy = fields.Selection( + [('no', 'No'), ('cost', 'At cost'), ('sales_price', 'At sale price')], + string='Re-Invoice Expenses', + default='no') @api.multi @api.depends('product_variant_ids.sales_count') def _sales_count(self): @@ -1033,8 +1026,6 @@ class ProductTemplate(models.Model): invoice_policy = fields.Selection( [('order', 'Ordered quantities'), ('delivery', 'Delivered quantities'), - ('cost', 'Reinvoice Costs')], - string='Invoicing Policy', help='Ordered Quantity: Invoice based on the quantity the customer ordered.\n' - 'Delivered Quantity: Invoiced based on the quantity the vendor delivered.\n' - 'Reinvoice Costs: Invoice with some additional charges (product transfer, labour charges,...)', + ], string='Invoicing Policy', help='Ordered Quantity: Invoice based on the quantity the customer ordered.\n' + 'Delivered Quantity: Invoiced based on the quantity the vendor delivered (time or deliveries).', default='order') diff --git a/addons/sale/sale_analytic.py b/addons/sale/sale_analytic.py index 9261c5c4208..0eed9f4cf71 100644 --- a/addons/sale/sale_analytic.py +++ b/addons/sale/sale_analytic.py @@ -40,6 +40,13 @@ class AccountAnalyticLine(models.Model): so_line = fields.Many2one('sale.order.line', string='Sale Order Line') def _get_invoice_price(self, order): + if self.product_id.expense_policy == 'sales_price': + return self.product_id.with_context( + partner=order.partner_id.id, + date_order=order.date_order, + pricelist=order.pricelist_id.id, + uom=self.product_uom_id.id + ).price if self.unit_amount == 0.0: return 0.0 price_unit = abs(self.amount / self.unit_amount) @@ -47,20 +54,12 @@ class AccountAnalyticLine(models.Model): price_unit = self.currency_id.compute(price_unit, order.currency_id) return price_unit - def _get_sale_order_line_vals(self): - order = self.env['sale.order'].search([('project_id', '=', self.account_id.id)], limit=1) - if not order: - return False - if order.state != 'sale': - raise UserError(_('The Sale Order %s linked to the Analytic Account must be validated before registering expenses.') % order.name) - + def _get_sale_order_line_vals(self, order, price): last_so_line = self.env['sale.order.line'].search([('order_id', '=', order.id)], order='sequence desc', limit=1) last_sequence = last_so_line.sequence + 1 if last_so_line else 100 fpos = order.fiscal_position_id or order.partner_id.property_account_position_id taxes = fpos.map_tax(self.product_id.taxes_id) - price = self._get_invoice_price(order) - return { 'order_id': order.id, 'name': self.name, @@ -77,30 +76,24 @@ class AccountAnalyticLine(models.Model): def _get_sale_order_line(self, vals=None): result = dict(vals or {}) so_line = result.get('so_line', False) or self.so_line - if not so_line and self.account_id and self.product_id and self.product_id.invoice_policy in ('cost', 'order'): + if not so_line and self.account_id and self.product_id and (self.product_id.expense_policy!='no'): + order = self.env['sale.order'].search([('project_id', '=', self.account_id.id), ('state','=','sale')], limit=1) + if not order: + return result + price = self._get_invoice_price(order) so_lines = self.env['sale.order.line'].search([ - ('order_id.project_id', '=', self.account_id.id), - ('state', '=', 'sale'), + ('order_id', '=', order.id), + ('price_unit', '=', price), ('product_id', '=', self.product_id.id)]) - # Use the existing SO line only if the unit prices are the same, otherwise we create - # a new line - for line in so_lines: - if line.product_id.invoice_policy != 'cost' or (line.product_id.invoice_policy == 'cost' and line.price_unit == self._get_invoice_price(line.order_id)): - result.update({'so_line': line.id}) - so_line = line - break + if so_lines: + result.update({'so_line': so_lines[0].id}) else: - # This will trigger the creation of a new SO line - so_line = False - - if not so_line and self.account_id and self.product_id and self.product_id.invoice_policy == 'cost': - order_line_vals = self._get_sale_order_line_vals() - if order_line_vals: - so_line = self.env['sale.order.line'].create(order_line_vals) - so_line._compute_tax_id() - result.update({'so_line': so_line.id}) - + order_line_vals = self._get_sale_order_line_vals(order, price) + if order_line_vals: + so_line = self.env['sale.order.line'].create(order_line_vals) + so_line._compute_tax_id() + result.update({'so_line': so_line.id}) return result @api.multi diff --git a/addons/sale/sale_product_demo.xml b/addons/sale/sale_product_demo.xml index c7287431017..a4b8c6a5b47 100644 --- a/addons/sale/sale_product_demo.xml +++ b/addons/sale/sale_product_demo.xml @@ -146,7 +146,7 @@ - cost + cost diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py index af5ffecca9b..f920aaa10ca 100644 --- a/addons/sale/tests/test_sale_order.py +++ b/addons/sale/tests/test_sale_order.py @@ -40,7 +40,7 @@ class TestSaleOrder(TestSale): # deliver lines except 'time and material' then invoice again for line in so.order_line: - line.qty_delivered = 2 if line.product_id.invoice_policy in ['order', 'delivery'] else 0 + line.qty_delivered = 2 if line.product_id.expense_policy=='no' else 0 self.assertTrue(so.invoice_status == 'to invoice', 'Sale: SO status after delivery should be "to invoice"') inv_id = so.action_invoice_create() inv = inv_obj.browse(inv_id) diff --git a/addons/sale_expense/__init__.py b/addons/sale_expense/__init__.py index 48f417bbd93..67dee8c60db 100644 --- a/addons/sale_expense/__init__.py +++ b/addons/sale_expense/__init__.py @@ -1,3 +1,2 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import models diff --git a/addons/sale_expense/models/__init__.py b/addons/sale_expense/models/__init__.py deleted file mode 100644 index 2bf570fe2be..00000000000 --- a/addons/sale_expense/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. -import analytic -import product diff --git a/addons/sale_expense/models/analytic.py b/addons/sale_expense/models/analytic.py deleted file mode 100644 index 3baeeaaa4f5..00000000000 --- a/addons/sale_expense/models/analytic.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from openerp import models - - -class AccountAnalyticLine(models.Model): - _inherit = "account.analytic.line" - - def _get_invoice_price(self, order): - if self.product_id.invoice_policy == 'cost' and self.product_id.expense_policy == 'sales_price': - return self.product_id.with_context( - partner=order.partner_id.id, - date_order=order.date_order, - pricelist=order.pricelist_id.id, - uom=self.product_uom_id.id - ).price - else: - return super(AccountAnalyticLine, self)._get_invoice_price(order) diff --git a/addons/sale_expense/models/product.py b/addons/sale_expense/models/product.py deleted file mode 100644 index 0d3276b453f..00000000000 --- a/addons/sale_expense/models/product.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from openerp import fields, models - - -class ProductProduct(models.Model): - _inherit = "product.template" - - expense_policy = fields.Selection( - [('cost', 'At Cost'), ('sales_price', 'At Sales Price')], - string='Expense Invoice Policy', - help="If you invoice at cost, the expense will be invoiced on the sale order at the cost of the analytic line;" - "if you invoice at sales price, the price of the product will be used instead.", - default='cost') diff --git a/addons/sale_expense/sale_expense_demo.xml b/addons/sale_expense/sale_expense_demo.xml index ee398004c98..4aa55923c0d 100644 --- a/addons/sale_expense/sale_expense_demo.xml +++ b/addons/sale_expense/sale_expense_demo.xml @@ -1,19 +1,19 @@ - + - cost + delivery sales_price - cost + delivery cost - cost + delivery cost diff --git a/addons/sale_expense/views/product_view.xml b/addons/sale_expense/views/product_view.xml index 5f2dbd81c0f..ddedc50e4dc 100644 --- a/addons/sale_expense/views/product_view.xml +++ b/addons/sale_expense/views/product_view.xml @@ -7,9 +7,8 @@ + attrs="{'invisible': [('can_be_expensed','=',False)]}"/> - \ No newline at end of file diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index c8bdb407a72..70b6a7c8c11 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -285,7 +285,7 @@ class StockMove(models.Model): # Update delivered quantities on sale order lines todo = self.env['sale.order.line'] for move in self: - if (move.procurement_id.sale_line_id) and (move.product_id.invoice_policy in ('order', 'delivery')): + if (move.procurement_id.sale_line_id) and (move.product_id.expense_policy=='no'): todo |= move.procurement_id.sale_line_id for line in todo: line.qty_delivered = line._get_delivered_qty() diff --git a/addons/sale_timesheet/models/sale_timesheet.py b/addons/sale_timesheet/models/sale_timesheet.py index 4dfb0dda486..adf5a96deeb 100644 --- a/addons/sale_timesheet/models/sale_timesheet.py +++ b/addons/sale_timesheet/models/sale_timesheet.py @@ -33,7 +33,7 @@ class ProductTemplate(models.Model): @api.onchange('type', 'invoice_policy') def onchange_type_timesheet(self): - if self.type == 'service' and self.invoice_policy != 'cost': + if self.type == 'service': self.track_service = 'timesheet' else: self.track_service = 'manual'