[IMP] sale,hr_expense: simplify invoice policy, 2 options instead of 3
The invoice_policy 'time & material' has been replaced by an
expense_policy!='no', with invoice_policy='delivery'.
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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')]",\
|
||||
|
||||
+8
-17
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
</record>
|
||||
|
||||
<record id="product.service_cost_01" model="product.product">
|
||||
<field name="invoice_policy">cost</field>
|
||||
<field name="expense_policy">cost</field>
|
||||
</record>
|
||||
|
||||
<record id="product.service_order_01" model="product.product">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import models
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import analytic
|
||||
import product
|
||||
@@ -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)
|
||||
@@ -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')
|
||||
@@ -1,19 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
|
||||
<record id="hr_expense.car_travel" model="product.product">
|
||||
<field name="invoice_policy">cost</field>
|
||||
<field name="invoice_policy">delivery</field>
|
||||
<field name="expense_policy">sales_price</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense.air_ticket" model="product.product">
|
||||
<field name="invoice_policy">cost</field>
|
||||
<field name="invoice_policy">delivery</field>
|
||||
<field name="expense_policy">cost</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense.hotel_rent" model="product.product">
|
||||
<field name="invoice_policy">cost</field>
|
||||
<field name="invoice_policy">delivery</field>
|
||||
<field name="expense_policy">cost</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="invoice_policy" position="after">
|
||||
<field name="expense_policy" widget="radio"
|
||||
attrs="{'invisible': [('invoice_policy','!=','cost')]}"/>
|
||||
attrs="{'invisible': [('can_be_expensed','=',False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user