2013-12-04 10:59:20 +00:00
2015-09-16 09:07:14 +00:00
from datetime import datetime
2013-12-04 10:59:20 +00:00
from openerp import tools
from openerp . osv import fields , osv
2013-12-05 18:10:52 +00:00
from openerp . tools . translate import _
2013-12-04 10:59:20 +00:00
class wizard_valuation_history ( osv . osv_memory ) :
_name = ' wizard.valuation.history '
_description = ' Wizard that opens the stock valuation history table '
_columns = {
2014-02-14 14:57:12 +00:00
' choose_date ' : fields . boolean ( ' Choose a Particular Date ' ) ,
2013-12-04 10:59:20 +00:00
' date ' : fields . datetime ( ' Date ' , required = True ) ,
}
_defaults = {
2014-02-12 12:46:46 +00:00
' choose_date ' : False ,
2013-12-04 10:59:20 +00:00
' date ' : fields . datetime . now ,
}
def open_table ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
data = self . read ( cr , uid , ids , context = context ) [ 0 ]
ctx = context . copy ( )
ctx [ ' history_date ' ] = data [ ' date ' ]
2013-12-06 16:59:56 +00:00
ctx [ ' search_default_group_by_product ' ] = True
ctx [ ' search_default_group_by_location ' ] = True
2013-12-04 10:59:20 +00:00
return {
' domain ' : " [( ' date ' , ' <= ' , ' " + data [ ' date ' ] + " ' )] " ,
2013-12-05 18:10:52 +00:00
' name ' : _ ( ' Stock Value At Date ' ) ,
2013-12-04 10:59:20 +00:00
' view_type ' : ' form ' ,
2014-04-16 08:48:49 +00:00
' view_mode ' : ' tree,graph ' ,
2013-12-04 10:59:20 +00:00
' res_model ' : ' stock.history ' ,
' type ' : ' ir.actions.act_window ' ,
' context ' : ctx ,
}
class stock_history ( osv . osv ) :
_name = ' stock.history '
_auto = False
2013-12-06 16:33:07 +00:00
_order = ' date asc '
2013-12-04 10:59:20 +00:00
2014-04-15 09:19:23 +00:00
def read_group ( self , cr , uid , domain , fields , groupby , offset = 0 , limit = None , context = None , orderby = False , lazy = True ) :
res = super ( stock_history , self ) . read_group ( cr , uid , domain , fields , groupby , offset = offset , limit = limit , context = context , orderby = orderby , lazy = lazy )
2014-04-28 15:33:55 +00:00
if context is None :
context = { }
2015-09-16 09:07:14 +00:00
date = context . get ( ' history_date ' , datetime . now ( ) )
2013-12-04 10:59:20 +00:00
if ' inventory_value ' in fields :
2015-06-15 14:21:52 +00:00
group_lines = { }
for line in res :
2016-01-25 17:25:40 +00:00
domain = line . get ( ' __domain ' , domain )
2015-06-15 14:21:52 +00:00
group_lines . setdefault ( str ( domain ) , self . search ( cr , uid , domain , context = context ) )
line_ids = set ( )
for ids in group_lines . values ( ) :
for product_id in ids :
line_ids . add ( product_id )
line_ids = list ( line_ids )
2015-07-01 13:55:28 +00:00
lines_rec = { }
if line_ids :
cr . execute ( ' SELECT id, product_id, price_unit_on_quant, company_id, quantity FROM stock_history WHERE id in %s ' , ( tuple ( line_ids ) , ) )
lines_rec = cr . dictfetchall ( )
2015-06-15 14:21:52 +00:00
lines_dict = dict ( ( line [ ' id ' ] , line ) for line in lines_rec )
product_ids = list ( set ( line_rec [ ' product_id ' ] for line_rec in lines_rec ) )
products_rec = self . pool [ ' product.product ' ] . read ( cr , uid , product_ids , [ ' cost_method ' , ' product_tmpl_id ' ] , context = context )
products_dict = dict ( ( product [ ' id ' ] , product ) for product in products_rec )
cost_method_product_tmpl_ids = list ( set ( product [ ' product_tmpl_id ' ] [ 0 ] for product in products_rec if product [ ' cost_method ' ] != ' real ' ) )
2015-07-01 08:05:49 +00:00
histories = [ ]
if cost_method_product_tmpl_ids :
cr . execute ( ' SELECT DISTINCT ON (product_template_id, company_id) product_template_id, company_id, cost FROM product_price_history WHERE product_template_id in %s AND datetime <= %s ORDER BY product_template_id, company_id, datetime DESC ' , ( tuple ( cost_method_product_tmpl_ids ) , date ) )
histories = cr . dictfetchall ( )
2015-06-15 14:21:52 +00:00
histories_dict = { }
for history in histories :
histories_dict [ ( history [ ' product_template_id ' ] , history [ ' company_id ' ] ) ] = history [ ' cost ' ]
2013-12-04 10:59:20 +00:00
for line in res :
2015-01-13 13:07:06 +00:00
inv_value = 0.0
2016-01-25 17:25:40 +00:00
lines = group_lines . get ( str ( line . get ( ' __domain ' , domain ) ) )
2015-06-15 14:21:52 +00:00
for line_id in lines :
line_rec = lines_dict [ line_id ]
product = products_dict [ line_rec [ ' product_id ' ] ]
if product [ ' cost_method ' ] == ' real ' :
price = line_rec [ ' price_unit_on_quant ' ]
2015-01-13 13:07:06 +00:00
else :
2015-06-15 14:21:52 +00:00
price = histories_dict . get ( ( product [ ' product_tmpl_id ' ] [ 0 ] , line_rec [ ' company_id ' ] ) , 0.0 )
inv_value + = price * line_rec [ ' quantity ' ]
2015-01-13 13:07:06 +00:00
line [ ' inventory_value ' ] = inv_value
2013-12-04 10:59:20 +00:00
return res
def _get_inventory_value ( self , cr , uid , ids , name , attr , context = None ) :
2014-04-28 15:33:55 +00:00
if context is None :
context = { }
date = context . get ( ' history_date ' )
2014-05-05 12:07:43 +00:00
product_tmpl_obj = self . pool . get ( " product.template " )
2013-12-04 10:59:20 +00:00
res = { }
for line in self . browse ( cr , uid , ids , context = context ) :
if line . product_id . cost_method == ' real ' :
2013-12-06 16:33:07 +00:00
res [ line . id ] = line . quantity * line . price_unit_on_quant
2013-12-04 10:59:20 +00:00
else :
2014-05-05 12:07:43 +00:00
res [ line . id ] = line . quantity * product_tmpl_obj . get_history_price ( cr , uid , line . product_id . product_tmpl_id . id , line . company_id . id , date = date , context = context )
2013-12-04 10:59:20 +00:00
return res
_columns = {
' move_id ' : fields . many2one ( ' stock.move ' , ' Stock Move ' , required = True ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True ) ,
2014-04-16 15:18:25 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2013-12-04 10:59:20 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True ) ,
' product_categ_id ' : fields . many2one ( ' product.category ' , ' Product Category ' , required = True ) ,
2014-08-07 15:08:16 +00:00
' quantity ' : fields . float ( ' Product Quantity ' ) ,
2013-12-04 10:59:20 +00:00
' date ' : fields . datetime ( ' Operation Date ' ) ,
2016-03-11 16:58:36 +00:00
' price_unit_on_quant ' : fields . float ( ' Value ' , group_operator = ' avg ' ) ,
2013-12-04 10:59:20 +00:00
' inventory_value ' : fields . function ( _get_inventory_value , string = " Inventory Value " , type = ' float ' , readonly = True ) ,
2014-04-16 15:18:25 +00:00
' source ' : fields . char ( ' Source ' )
2013-12-04 10:59:20 +00:00
}
def init ( self , cr ) :
tools . drop_view_if_exists ( cr , ' stock_history ' )
cr . execute ( """
CREATE OR REPLACE VIEW stock_history AS (
2014-04-17 09:55:47 +00:00
SELECT MIN ( id ) as id ,
move_id ,
location_id ,
company_id ,
product_id ,
product_categ_id ,
SUM ( quantity ) as quantity ,
date ,
2016-05-31 15:55:27 +00:00
COALESCE ( SUM ( price_unit_on_quant * quantity ) / NULLIF ( SUM ( quantity ) , 0 ) , 0 ) as price_unit_on_quant ,
2014-04-17 09:55:47 +00:00
source
FROM
( ( SELECT
2015-10-22 12:56:13 +00:00
stock_move . id AS id ,
2013-12-04 10:59:20 +00:00
stock_move . id AS move_id ,
2014-04-17 09:55:47 +00:00
dest_location . id AS location_id ,
dest_location . company_id AS company_id ,
2013-12-04 10:59:20 +00:00
stock_move . product_id AS product_id ,
product_template . categ_id AS product_categ_id ,
2013-12-06 16:33:07 +00:00
quant . qty AS quantity ,
2013-12-04 10:59:20 +00:00
stock_move . date AS date ,
2014-04-16 15:18:25 +00:00
quant . cost as price_unit_on_quant ,
stock_move . origin AS source
2013-12-04 10:59:20 +00:00
FROM
2015-10-22 13:37:06 +00:00
stock_move
JOIN
stock_quant_move_rel on stock_quant_move_rel . move_id = stock_move . id
JOIN
stock_quant as quant on stock_quant_move_rel . quant_id = quant . id
JOIN
2014-04-17 09:55:47 +00:00
stock_location dest_location ON stock_move . location_dest_id = dest_location . id
2015-10-22 13:37:06 +00:00
JOIN
2014-04-17 09:55:47 +00:00
stock_location source_location ON stock_move . location_id = source_location . id
2015-10-22 13:37:06 +00:00
JOIN
2013-12-06 16:33:07 +00:00
product_product ON product_product . id = stock_move . product_id
2015-10-22 13:37:06 +00:00
JOIN
2013-12-06 16:33:07 +00:00
product_template ON product_template . id = product_product . product_tmpl_id
2015-10-22 13:37:06 +00:00
WHERE quant . qty > 0 AND stock_move . state = ' done ' AND dest_location . usage in ( ' internal ' , ' transit ' )
AND (
2016-04-21 09:24:49 +00:00
not ( source_location . company_id is null and dest_location . company_id is null ) or
2015-07-08 15:41:19 +00:00
source_location . company_id != dest_location . company_id or
source_location . usage not in ( ' internal ' , ' transit ' ) )
2015-10-22 12:56:13 +00:00
) UNION ALL
2013-12-06 16:33:07 +00:00
( SELECT
2015-10-22 12:56:13 +00:00
( - 1 ) * stock_move . id AS id ,
2013-12-06 16:33:07 +00:00
stock_move . id AS move_id ,
2014-04-17 09:55:47 +00:00
source_location . id AS location_id ,
source_location . company_id AS company_id ,
2013-12-06 16:33:07 +00:00
stock_move . product_id AS product_id ,
product_template . categ_id AS product_categ_id ,
- quant . qty AS quantity ,
stock_move . date AS date ,
2014-04-16 15:18:25 +00:00
quant . cost as price_unit_on_quant ,
stock_move . origin AS source
2013-12-06 16:33:07 +00:00
FROM
2015-10-22 13:37:06 +00:00
stock_move
JOIN
stock_quant_move_rel on stock_quant_move_rel . move_id = stock_move . id
JOIN
stock_quant as quant on stock_quant_move_rel . quant_id = quant . id
JOIN
2014-04-17 09:55:47 +00:00
stock_location source_location ON stock_move . location_id = source_location . id
2015-10-22 13:37:06 +00:00
JOIN
2014-04-17 09:55:47 +00:00
stock_location dest_location ON stock_move . location_dest_id = dest_location . id
2015-10-22 13:37:06 +00:00
JOIN
2013-12-04 10:59:20 +00:00
product_product ON product_product . id = stock_move . product_id
2015-10-22 13:37:06 +00:00
JOIN
2013-12-04 10:59:20 +00:00
product_template ON product_template . id = product_product . product_tmpl_id
2015-10-22 13:37:06 +00:00
WHERE quant . qty > 0 AND stock_move . state = ' done ' AND source_location . usage in ( ' internal ' , ' transit ' )
AND (
2016-04-21 09:24:49 +00:00
not ( dest_location . company_id is null and source_location . company_id is null ) or
2015-07-08 15:41:19 +00:00
dest_location . company_id != source_location . company_id or
dest_location . usage not in ( ' internal ' , ' transit ' ) )
2014-04-17 09:55:47 +00:00
) )
AS foo
[FIX] stock_account: valuation history with moves having multiple quant prices
The revision 29bd62252158803cdad351a2966af6eeac4cca74
aimed to improve the performance by changing
the view index, to use a BIGINT instead of a text.
It was assumed that the index used
(`stock_move.id`) could not appear
multiple times with the JOINs and
group bys defined.
This was in fact possible, if a
stock move is associated to
several quants with different costs,
because of the JOIN on the many2many table
`stock_quant_move_rel`
linking quants to moves,
and the GROUP BY
`price_unit_on_quant`
that caused the different moves/quants association
not to be merged within one unique line
if the costs on the quants are different.
Instead of the group by, we now aggregate the quants
costs, using the weighted average, so the index
used can be unique, as expected.
From now, the inventory value per line in
this report view can therefore
be different than what can be found on the quants,
but this report view is based on the stock moves
rather than the stock quants, and this is therefore accepted.
In other words, the inventory value is computed for the stock move
rather than for the stock quant.
opw-658903
2016-02-16 15:23:05 +00:00
GROUP BY move_id , location_id , company_id , product_id , product_categ_id , date , source
2013-12-04 10:59:20 +00:00
) """ )