diff --git a/addons/mrp_account/models/product.py b/addons/mrp_account/models/product.py
index da15e757cb3..61f067b020a 100644
--- a/addons/mrp_account/models/product.py
+++ b/addons/mrp_account/models/product.py
@@ -27,28 +27,18 @@ class ProductProduct(models.Model):
def button_bom_cost(self):
self.ensure_one()
- price = self._get_price_from_bom()
- if self.valuation == 'real_time':
- action_rec = self.env.ref('stock_account.action_view_change_standard_price')
- action = action_rec.read([])[0]
- action['context'] = {'default_new_price': price}
- return action
- else:
- self.standard_price = price
+ self._set_price_from_bom()
def action_bom_cost(self):
- real_time_products = self.filtered(lambda p: p.valuation == 'real_time')
- if real_time_products:
- raise UserError(_('The inventory valuation of some products %s is automated. You can only update their cost from the product form.') % (real_time_products.mapped('display_name')))
-
boms_to_recompute = self.env['mrp.bom'].search(['|', ('product_id', 'in', self.ids), '&', ('product_id', '=', False), ('product_tmpl_id', 'in', self.mapped('product_tmpl_id').ids)])
for product in self:
- product.standard_price = product._get_price_from_bom(boms_to_recompute)
+ product._set_price_from_bom(boms_to_recompute)
- def _get_price_from_bom(self, boms_to_recompute=False):
+ def _set_price_from_bom(self, boms_to_recompute=False):
self.ensure_one()
bom = self.env['mrp.bom']._bom_find(product=self)
- return self._compute_bom_price(bom, boms_to_recompute=boms_to_recompute)
+ if bom:
+ self.standard_price = self._compute_bom_price(bom, boms_to_recompute=boms_to_recompute)
def _compute_bom_price(self, bom, boms_to_recompute=False):
self.ensure_one()
diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py
index 1273e3ecdba..b015080a883 100644
--- a/addons/mrp_account/tests/test_bom_price.py
+++ b/addons/mrp_account/tests/test_bom_price.py
@@ -182,9 +182,3 @@ class TestBom(common.TransactionCase):
self.Product.browse([self.dining_table.id, self.table_head.id]).action_bom_cost()
# Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 10.42) = 854.17
self.assertEqual(float_compare(self.dining_table.standard_price, 854.17, precision_digits=2), 0, "After computing price from BoM price should be 786.46")
-
- def test_01_compute_price_inventory_valuation(self):
- """Test update cost from bom in list view when inventory valuation is real time."""
- self.glass.categ_id.property_valuation = 'real_time'
- with self.assertRaises(UserError):
- self.dining_table.with_context(button=False).action_bom_cost()
diff --git a/addons/mrp_account/views/product_views.xml b/addons/mrp_account/views/product_views.xml
index 5ad16258b6b..2263a5f0cf0 100644
--- a/addons/mrp_account/views/product_views.xml
+++ b/addons/mrp_account/views/product_views.xml
@@ -7,7 +7,9 @@
-
+
+
+
-
+
+
+
-
+
-
+
+
+
+
+
+
+
diff --git a/addons/product/views/product_views.xml b/addons/product/views/product_views.xml
index 5b244213532..deff7532cce 100644
--- a/addons/product/views/product_views.xml
+++ b/addons/product/views/product_views.xml
@@ -305,7 +305,7 @@
-
+
diff --git a/addons/product_margin/views/product_product_views.xml b/addons/product_margin/views/product_product_views.xml
index 481ca67b80e..e3788e19899 100644
--- a/addons/product_margin/views/product_product_views.xml
+++ b/addons/product_margin/views/product_product_views.xml
@@ -40,7 +40,7 @@
-
+
diff --git a/addons/sale_mrp/tests/test_sale_mrp_procurement.py b/addons/sale_mrp/tests/test_sale_mrp_procurement.py
index 8c411bfb55f..1f321e634d4 100644
--- a/addons/sale_mrp/tests/test_sale_mrp_procurement.py
+++ b/addons/sale_mrp/tests/test_sale_mrp_procurement.py
@@ -39,9 +39,7 @@ class TestSaleMrpProcurement(TransactionCase):
product.route_ids.add(warehouse0.mto_pull_id.route_id)
product_template_slidermobile0 = product.save()
- std_price_wiz = Form(self.env['stock.change.standard.price'].with_context(active_id=product_template_slidermobile0.id, active_model='product.template'))
- std_price_wiz.new_price = 189
- std_price_wiz.save()
+ product_template_slidermobile0.standard_price = 189
product_component = Form(self.env['product.product'])
product_component.name = 'Battery'
diff --git a/addons/sale_stock/tests/test_anglo_saxon_valuation.py b/addons/sale_stock/tests/test_anglo_saxon_valuation.py
index 3202f37d01c..1fe34175b8a 100644
--- a/addons/sale_stock/tests/test_anglo_saxon_valuation.py
+++ b/addons/sale_stock/tests/test_anglo_saxon_valuation.py
@@ -155,7 +155,7 @@ class TestAngloSaxonValuation(SavepointCase):
"""
self.product.categ_id.property_cost_method = 'standard'
self.product.invoice_policy = 'order'
- self.product._change_standard_price(10.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 10.0
# Put two items in stock.
self._inv_adj_two_units()
@@ -164,7 +164,7 @@ class TestAngloSaxonValuation(SavepointCase):
sale_order = self._so_and_confirm_two_units()
# standard price to 14
- self.product._change_standard_price(14.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 14.0
# Invoice the sale order.
invoice = sale_order._create_invoices()
@@ -192,7 +192,7 @@ class TestAngloSaxonValuation(SavepointCase):
The amounts used in Stock OUT and COGS should be 10 then 14."""
self.product.categ_id.property_cost_method = 'standard'
self.product.invoice_policy = 'order'
- self.product._change_standard_price(10.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 10.0
# Put two items in stock.
sale_order = self._so_and_confirm_two_units()
@@ -231,14 +231,14 @@ class TestAngloSaxonValuation(SavepointCase):
self.assertEqual(income_aml.credit, 12)
# change the standard price to 14
- self.product._change_standard_price(14.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 14.0
# deliver the backorder
sale_order.picking_ids[0].move_lines.quantity_done = 1
sale_order.picking_ids[0].button_validate()
# change the standard price to 16
- self.product._change_standard_price(16.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 16.0
# invoice 1
invoice2 = sale_order._create_invoices()
@@ -279,7 +279,7 @@ class TestAngloSaxonValuation(SavepointCase):
wiz.process()
# change the standard price to 14
- self.product._change_standard_price(14.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 14.0
# deliver the backorder
sale_order.picking_ids.filtered('backorder_id').move_lines.quantity_done = 1
@@ -370,14 +370,14 @@ class TestAngloSaxonValuation(SavepointCase):
self.assertEqual(income_aml.credit, 12)
# change the standard price to 14
- self.product._change_standard_price(14.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 14.0
# deliver the backorder
sale_order.picking_ids[0].move_lines.quantity_done = 1
sale_order.picking_ids[0].button_validate()
# change the standard price to 16
- self.product._change_standard_price(16.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 16.0
# invoice 1
invoice2 = sale_order._create_invoices()
@@ -418,7 +418,7 @@ class TestAngloSaxonValuation(SavepointCase):
wiz.process()
# change the standard price to 14
- self.product._change_standard_price(14.0, counterpart_account_id=self.counterpart_account.id)
+ self.product.standard_price = 14.0
# deliver the backorder
sale_order.picking_ids.filtered('backorder_id').move_lines.quantity_done = 1
diff --git a/addons/stock_account/__manifest__.py b/addons/stock_account/__manifest__.py
index 80fbc9ea82e..2662210d0c1 100644
--- a/addons/stock_account/__manifest__.py
+++ b/addons/stock_account/__manifest__.py
@@ -25,7 +25,6 @@ Dashboard / Reports for Warehouse Management includes:
'data': [
'security/stock_account_security.xml',
'security/ir.model.access.csv',
- 'wizard/stock_change_standard_price_views.xml',
'data/stock_account_data.xml',
'views/stock_account_views.xml',
'views/res_config_settings_views.xml',
diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py
index 8a35c7c63ee..ace7a8229d4 100644
--- a/addons/stock_account/models/product.py
+++ b/addons/stock_account/models/product.py
@@ -95,6 +95,16 @@ class ProductProduct(models.Model):
value_svl = fields.Float(compute='_compute_value_svl')
quantity_svl = fields.Float(compute='_compute_value_svl')
stock_valuation_layer_ids = fields.One2many('stock.valuation.layer', 'product_id')
+ valuation = fields.Selection(related="categ_id.property_valuation", readonly=True)
+
+ def write(self, vals):
+ if 'standard_price' in vals and not self.env.context.get('disable_auto_svl'):
+ for product_product in self:
+ if product_product.cost_method != 'fifo':
+ counterpart_account_id = product_product.property_account_expense_id.id or product_product.categ_id.property_account_expense_categ_id.id
+ product_product._change_standard_price(vals['standard_price'], counterpart_account_id)
+
+ return super(ProductProduct, self).write(vals)
@api.depends('stock_valuation_layer_ids')
@api.depends_context('to_date', 'company')
@@ -172,6 +182,10 @@ class ProductProduct(models.Model):
:param new_price: new standard price
"""
# Handle stock valuation layers.
+
+ if self.valuation == 'real_time' and not self.env['account.move'].check_access_rights('create', raise_exception=False):
+ raise UserError(_("You cannot update the cost of a product in automated valuation as it leads to the creation of a journal entry, for which you don't have the access rights."))
+
svl_vals_list = []
company_id = self.env.company
for product in self:
@@ -223,6 +237,7 @@ class ProductProduct(models.Model):
'company_id': company_id.id,
'ref': product.default_code,
'stock_valuation_layer_ids': [(6, None, [stock_valuation_layer.id])],
+ 'type': 'entry',
'line_ids': [(0, 0, {
'name': _('%s changed cost from %s to %s - %s') % (self.env.user.name, product.standard_price, new_price, product.display_name),
'account_id': debit_account_id,
@@ -238,11 +253,9 @@ class ProductProduct(models.Model):
})],
}
am_vals_list.append(move_vals)
- account_moves = self.env['account.move'].create(am_vals_list)
- account_moves.post()
- # Actually update the standard price.
- self.with_company(company_id).sudo().write({'standard_price': new_price})
+ account_moves = self.env['account.move'].sudo().create(am_vals_list)
+ account_moves.post()
def _run_fifo(self, quantity, company):
self.ensure_one()
@@ -279,7 +292,7 @@ class ProductProduct(models.Model):
# Update the standard price with the price of the last used candidate, if any.
if new_standard_price and self.cost_method == 'fifo':
- self.sudo().with_company(company.id).standard_price = new_standard_price
+ self.sudo().with_company(company.id).with_context(disable_auto_svl=True).standard_price = new_standard_price
# If there's still quantity to value but we're out of candidates, we fall in the
# negative stock use case. We chose to value the out move at the price of the
diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py
index 16f9fafcef4..a30c0e8607e 100644
--- a/addons/stock_account/models/stock_move.py
+++ b/addons/stock_account/models/stock_move.py
@@ -325,7 +325,7 @@ class StockMove(models.Model):
tmpl_dict[move.product_id.id] += qty_done
# Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products
- move.product_id.with_company(move.company_id.id).sudo().write({'standard_price': new_std_price})
+ move.product_id.with_company(move.company_id.id).with_context(disable_auto_svl=True).sudo().write({'standard_price': new_std_price})
std_price_update[move.company_id.id, move.product_id.id] = new_std_price
def _get_accounting_data_for_valuation(self):
diff --git a/addons/stock_account/tests/test_stockvaluation.py b/addons/stock_account/tests/test_stockvaluation.py
index f1b2492f46b..b8fa5379a54 100644
--- a/addons/stock_account/tests/test_stockvaluation.py
+++ b/addons/stock_account/tests/test_stockvaluation.py
@@ -108,7 +108,7 @@ class TestStockValuation(SavepointCase):
price change updates the stock value based on current stock level.
"""
# Enter 10 products while price is 5.0
- self.product1._change_standard_price(5.0, self.expense_account.id)
+ self.product1.standard_price = 5.0
move1 = self.env['stock.move'].create({
'name': 'IN 10 units @ 10.00 per unit',
'location_id': self.supplier_location.id,
@@ -123,7 +123,7 @@ class TestStockValuation(SavepointCase):
move1._action_done()
# Set price to 6.0
- self.product1._change_standard_price(6.0, self.expense_account.id)
+ self.product1.standard_price = 6.0
stock_aml, price_change_aml = self._get_stock_valuation_move_lines()
self.assertEqual(stock_aml.debit, 50)
self.assertEqual(price_change_aml.debit, 10)
@@ -2737,7 +2737,7 @@ class TestStockValuation(SavepointCase):
self.product1.categ_id.property_cost_method = 'standard'
self.product1.categ_id.property_valuation = 'manual_periodic'
- self.product1.with_user(self.inventory_user)._change_standard_price(10)
+ self.product1.standard_price = 10.0
move1 = self.env['stock.move'].with_user(self.inventory_user).create({
'name': 'IN 10 units',
@@ -2752,12 +2752,12 @@ class TestStockValuation(SavepointCase):
move1.move_line_ids.qty_done = 10.0
move1._action_done()
- def test_standard_perpetual_1(self):
+ def test_standard_perpetual_2(self):
"""Validate a receipt as a regular stock user."""
self.product1.categ_id.property_cost_method = 'standard'
self.product1.categ_id.property_valuation = 'real_time'
- self.product1.with_user(self.inventory_user)._change_standard_price(10)
+ self.product1.standard_price = 10.0
move1 = self.env['stock.move'].with_user(self.inventory_user).create({
'name': 'IN 10 units',
@@ -3100,7 +3100,7 @@ class TestStockValuation(SavepointCase):
date8 = now - timedelta(days=1)
# set the standard price to 10
- self.product1._change_standard_price(10, self.expense_account.id)
+ self.product1.standard_price = 10.0
# receive 10
move1 = self.env['stock.move'].create({
@@ -3160,7 +3160,7 @@ class TestStockValuation(SavepointCase):
self.assertEqual(self.product1.value_svl, 150)
# set the standard price to 5
- self.product1._change_standard_price(5, self.expense_account.id)
+ self.product1.standard_price = 5
self.product1.stock_valuation_layer_ids.sorted()[-1]._write({'create_date': date5})
self.assertEqual(self.product1.quantity_svl, 15)
@@ -3186,7 +3186,7 @@ class TestStockValuation(SavepointCase):
self.assertEqual(self.product1.value_svl, -25)
# set the standard price to 7.5
- self.product1._change_standard_price(7.5, self.expense_account.id)
+ self.product1.standard_price = 7.5
self.product1.stock_valuation_layer_ids.sorted()[-1]._write({'create_date': date7})
# receive 100
diff --git a/addons/stock_account/tests/test_stockvaluationlayer.py b/addons/stock_account/tests/test_stockvaluationlayer.py
index 73303192887..2d66ed01f32 100644
--- a/addons/stock_account/tests/test_stockvaluationlayer.py
+++ b/addons/stock_account/tests/test_stockvaluationlayer.py
@@ -211,7 +211,7 @@ class TestStockValuationStandard(TestStockValuationCommon):
move3 = self._make_out_move(self.product1, 15)
# change cost from 10 to 15
- self.product1._change_standard_price(15.0)
+ self.product1.standard_price = 15.0
self.assertEqual(self.product1.value_svl, 75)
self.assertEqual(self.product1.quantity_svl, 5)
diff --git a/addons/stock_account/views/product_views.xml b/addons/stock_account/views/product_views.xml
index 297f0678318..90c120809ed 100644
--- a/addons/stock_account/views/product_views.xml
+++ b/addons/stock_account/views/product_views.xml
@@ -35,78 +35,5 @@
-
-
- product.template.stock.property.form.inherit
- product.template
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- product.product.normal.form.view.inherit
- product.product
-
-
-
-
-
-
-
- {'invisible': [('cost_method', '=', 'fifo')]}
-
-
-
-
-
-
- product.product.product.view.form.easy.inherit
- product.product
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/stock_account/wizard/__init__.py b/addons/stock_account/wizard/__init__.py
index 0d966f31f09..31822ebf05e 100644
--- a/addons/stock_account/wizard/__init__.py
+++ b/addons/stock_account/wizard/__init__.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import stock_change_standard_price
from . import stock_quantity_history
from . import stock_picking_return
diff --git a/addons/stock_account/wizard/stock_change_standard_price.py b/addons/stock_account/wizard/stock_change_standard_price.py
deleted file mode 100644
index c3701234512..00000000000
--- a/addons/stock_account/wizard/stock_change_standard_price.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models
-
-
-class StockChangeStandardPrice(models.TransientModel):
- _name = "stock.change.standard.price"
- _description = "Change Standard Price"
-
- new_price = fields.Float(
- 'Price', digits='Product Price', required=True,
- help="If cost price is increased, stock variation account will be debited "
- "and stock output account will be credited with the value = (difference of amount * quantity available).\n"
- "If cost price is decreased, stock variation account will be creadited and stock input account will be debited.")
- counterpart_account_id = fields.Many2one(
- 'account.account', string="Counter-Part Account",
- domain=[('deprecated', '=', False)])
- counterpart_account_id_required = fields.Boolean(string="Counter-Part Account Required")
-
- @api.model
- def default_get(self, fields):
- res = super(StockChangeStandardPrice, self).default_get(fields)
-
- if 'active_model' in self._context:
- product_or_template = self.env[self._context['active_model']].browse(self._context['active_id'])
- if 'new_price' in fields and 'new_price' not in res:
- res['new_price'] = product_or_template.standard_price
- if 'counterpart_account_id' in fields and 'counterpart_account_id' not in res:
- res['counterpart_account_id'] = product_or_template.property_account_expense_id.id or product_or_template.categ_id.property_account_expense_categ_id.id
- res['counterpart_account_id_required'] = bool(product_or_template.valuation == 'real_time')
- return res
-
- def change_price(self):
- """ Changes the Standard Price of Product and creates an account move accordingly. """
- self.ensure_one()
- if self._context['active_model'] == 'product.template':
- products = self.env['product.template'].browse(self._context['active_id']).product_variant_ids
- else:
- products = self.env['product.product'].browse(self._context['active_id'])
-
- products._change_standard_price(self.new_price, counterpart_account_id=self.counterpart_account_id.id)
- return {'type': 'ir.actions.act_window_close'}
diff --git a/addons/stock_account/wizard/stock_change_standard_price_views.xml b/addons/stock_account/wizard/stock_change_standard_price_views.xml
deleted file mode 100644
index 200575b64c9..00000000000
--- a/addons/stock_account/wizard/stock_change_standard_price_views.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
- Change Standard Price
- stock.change.standard.price
-
-
-
-
-
-
- Change Standard Price
- ir.actions.act_window
- stock.change.standard.price
- form
-
- new
-
-
-
-
diff --git a/addons/stock_dropshipping/tests/test_crossdock.py b/addons/stock_dropshipping/tests/test_crossdock.py
index 671d3098c6a..f50d3dee19c 100644
--- a/addons/stock_dropshipping/tests/test_crossdock.py
+++ b/addons/stock_dropshipping/tests/test_crossdock.py
@@ -34,9 +34,7 @@ class TestCrossdock(common.TransactionCase):
p_f.route_ids.add(wh_pps.crossdock_route_id)
cross_shop_product = p_f.save()
- std_price_wiz = Form(self.env['stock.change.standard.price'].with_context(active_id=p_f.id, active_model='product.template'))
- std_price_wiz.new_price = 70.0
- std_price_wiz.save()
+ p_f.standard_price = 70.0
# Create a sales order with a line of 100 PCE incoming shipment with route_id crossdock shipping
so_form = Form(self.env['sale.order'])
diff --git a/addons/stock_dropshipping/tests/test_lifo_price.py b/addons/stock_dropshipping/tests/test_lifo_price.py
index 5dd28152a46..ece6d2b7c03 100644
--- a/addons/stock_dropshipping/tests/test_lifo_price.py
+++ b/addons/stock_dropshipping/tests/test_lifo_price.py
@@ -38,9 +38,7 @@ class TestLifoPrice(StockAccountTestCommon):
product_form.categ_id.property_stock_account_output_categ_id = self.o_income
product_lifo_icecream = product_form.save()
- std_price_wiz = Form(self.env['stock.change.standard.price'].with_context(active_id=product_lifo_icecream.id, active_model='product.product'))
- std_price_wiz.new_price = 70.0
- std_price_wiz.save()
+ product_lifo_icecream.standard_price = 70.0
# I create a draft Purchase Order for first in move for 10 pieces at 60 euro
order_form = Form(self.env['purchase.order'])
diff --git a/addons/stock_dropshipping/tests/test_procurement_exception.py b/addons/stock_dropshipping/tests/test_procurement_exception.py
index 8d1a234d4c2..2d5c7e44db9 100644
--- a/addons/stock_dropshipping/tests/test_procurement_exception.py
+++ b/addons/stock_dropshipping/tests/test_procurement_exception.py
@@ -21,9 +21,8 @@ class TestProcurementException(common.TransactionCase):
product_form.categ_id = self.env.ref('product.product_category_1')
product_with_no_seller = product_form.save()
- std_price_wiz = Form(self.env['stock.change.standard.price'].with_context(active_id=product_with_no_seller.id, active_model='product.product'))
- std_price_wiz.new_price = 70.0
- std_price_wiz.save()
+ product_with_no_seller.standard_price = 70.0
+
# I create a sales order with this product with route dropship.
so_form = Form(self.env['sale.order'])
so_form.partner_id = res_partner_2
diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py
index 44c6063efe6..5d895cd0d97 100644
--- a/addons/stock_landed_costs/models/stock_landed_cost.py
+++ b/addons/stock_landed_costs/models/stock_landed_cost.py
@@ -144,7 +144,7 @@ class LandedCost(models.Model):
# Update the AVCO
product = line.move_id.product_id
if product.cost_method == 'average' and not float_is_zero(product.quantity_svl, precision_rounding=product.uom_id.rounding):
- product.with_company(self.company_id).sudo().standard_price += cost_to_add / product.quantity_svl
+ product.with_company(self.company_id).sudo().with_context(disable_auto_svl=True).standard_price += cost_to_add / product.quantity_svl
# `remaining_qty` is negative if the move is out and delivered proudcts that were not
# in stock.
qty_out = 0