← Back to team overview

openerp-community-reviewer team mailing list archive

lp:~camptocamp/margin-analysis/port-add-product_multi_company_7.0-bis-jge into lp:margin-analysis

 

Joël Grand-Guillaume @ camptocamp has proposed merging lp:~camptocamp/margin-analysis/port-add-product_multi_company_7.0-bis-jge into lp:margin-analysis.

Requested reviews:
  Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903): code review
  Pedro Manuel Baeza (pedro.baeza): code review and test
  Yannick Vaucher @ Camptocamp (yvaucher-c2c)
  Guewen Baconnier @ Camptocamp (gbaconnier-c2c): code review

For more details, see:
https://code.launchpad.net/~camptocamp/margin-analysis/port-add-product_multi_company_7.0-bis-jge/+merge/197679

Hi,


I propose here a brand new version of product_multi_company to manage prices (standard_price and list_price) by company. 

It uses another table to store and historize the prices instead of using ir.property. You can also easily override the field to historize in other module to add your own.

I provided here a whole set of tests and demo data to ensure no regression and show how to setup OpenERP in a complex multi-company, multi-currency context. With this module, you can have a same shared product between company, with each of them their own average price computed. You can even have the standard_price recorded in USD for company 1 and CHF for company 2 for example.

See description of the module for more details.

Note that this module replace the old one. I prefered to update this module rather than making another one (with another name). So, If you think it's better to call it differently, please just say it !

Thanks for the review,

Joël



-- 
https://code.launchpad.net/~camptocamp/margin-analysis/port-add-product_multi_company_7.0-bis-jge/+merge/197679
Your team Margin Analysis Core Editors is subscribed to branch lp:margin-analysis.
=== added directory 'product_price_history'
=== added file 'product_price_history/__init__.py'
--- product_price_history/__init__.py	1970-01-01 00:00:00 +0000
+++ product_price_history/__init__.py	2013-12-04 10:41:12 +0000
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright 2013 Camptocamp SA
+#    Author: Joel Grand-Guillaume
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+from . import product_price_history
+from . import wizard
\ No newline at end of file

=== added file 'product_price_history/__openerp__.py'
--- product_price_history/__openerp__.py	1970-01-01 00:00:00 +0000
+++ product_price_history/__openerp__.py	2013-12-04 10:41:12 +0000
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright 2013 Camptocamp SA
+#    Author: Joel Grand-Guillaume
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+{
+    "name" : "Product Price History",
+    "version" : "1.2",
+    "author" : "Camptocamp",
+    "category" : "Generic Modules/Inventory Control",
+    "depends" : ["product",
+                 "purchase",
+                 ],
+    "description": """
+Product Price History
+=====================
+
+This module allows you to:
+
+* Record various prices of a same product for different companies. This
+  way, every company can have its own costs (average or standard) and
+  sale prices.
+* Historize the prices in a way that you'll then be able to retrieve the
+  cost (or sale) price at a given date.
+
+Note that to benefit those values in stock report (or any other view that is based on SQL),
+you'll have to adapt it to include this new historized table. Especially true for stock
+valuation.
+
+This module also contains demo data and various tests to ensure it works well. It shows
+how to configure OpenERP properly when you have various company, each of them having 
+their product setup in average price and using different currencies. The goal is to share
+the products between all companies, keeping the right price for each of them.
+
+Technically, this module updates the definition of field standard_price, list_price 
+of the product and will make them stored in an external table. We override the read, 
+write and create methods to achieve that and don't used ir.property for performance
+and historization purpose.
+
+You may want to also use the module analytic_multicurrency from `bzr branch lp:account-analytic/7.0`
+in order to have a proper computation in analytic line as well (standard_price will be converted
+in company currency with this module when computing cost of analytic line).
+""",
+    'demo': [
+        'demo/product_price_history_purchase_demo.yml',
+    ],
+    'data': [
+        'product_price_history_view.xml',
+        'wizard/historic_prices_view.xml',
+        'security/ir.model.access.csv',
+        'security/product_price_history_security.xml',
+    ],
+    'test': [
+        'test/price_controlling_multicompany.yml',
+        'test/avg_price_computation_mutlicompanies_multicurrencies.yml',
+        'test/price_historization.yml',
+    ],
+    'installable': True,
+    'active': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added directory 'product_price_history/demo'
=== added file 'product_price_history/demo/product_price_history_purchase_demo.yml'
--- product_price_history/demo/product_price_history_purchase_demo.yml	1970-01-01 00:00:00 +0000
+++ product_price_history/demo/product_price_history_purchase_demo.yml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,189 @@
+-
+  Create a Partner for second company and second company user
+-
+  !record {model: res.partner, id: res_partner_company_01}:
+    name: Second Company Partner test
+-
+  !record {model: res.partner, id: res_partner_user_second_company_01}:
+    name: Second Company User Partner test
+-
+  Create a second company hanlded in CHF
+-
+  !record {model: res.company, id: res_company_01}:
+    name: Second Company test
+    partner_id: res_partner_company_01
+    currency_id: base.CHF
+-
+  Create a user for second company
+-
+ !record {model: res.users, id: res_users_second_company_01}:
+    login: user second company
+    password: user second company
+    partner_id: res_partner_user_second_company_01
+    company_id: res_company_01
+    company_ids: [res_company_01,base.main_company]
+    groups_id:
+      - base.group_user
+      - base.group_sale_manager
+      - purchase.group_purchase_manager
+      - stock.group_stock_user
+-
+  Create a second set of price type in USD for the second company
+-
+ !record {model: product.price.type, id: list_price_second_cmp}:
+    name: Public Price USD
+    field: list_price
+    currency_id: base.USD
+    company_id: res_company_01
+-
+ !record {model: product.price.type, id: standard_price_second_cmp}:
+    name: Cost Price USD
+    field: standard_price
+    currency_id: base.USD
+    company_id: res_company_01
+-
+  Ensure the price type of first company is in EUR
+-
+ !record {model: product.price.type, id: product.list_price}:
+    name: Public Price EUR
+    field: list_price
+    currency_id: base.EUR
+    company_id: base.main_company
+-
+ !record {model: product.price.type, id: product.standard_price}:
+    name: Cost Price EUR
+    field: standard_price
+    currency_id: base.EUR
+    company_id: base.main_company
+-
+  Set the admin user to first company
+-
+ !record {model: res.users, id: base.user_root}:
+    company_id: base.main_company 
+-
+  Set the currency rate of CHF, EUR and USD (reference EUR)
+-
+ !record {model: res.currency.rate, id: base.rateCHF}:
+    rate: 1.3086
+    currency_id: base.CHF
+    name: !eval time.strftime('%Y-%m-%d')
+-
+ !record {model: res.currency.rate, id: base.rateUSD}:
+    rate: 1.2086
+    currency_id: base.USD
+    name: !eval time.strftime('%Y-%m-%d')
+-
+ !record {model: res.currency.rate, id: base.rateEUR}:
+    rate: 1.0
+    currency_id: base.EUR
+    name: !eval time.strftime('%Y-%m-%d')
+-
+  Ensure the first Company and pricelists are in EUR (rate 1.0)
+-
+ !record {model: res.company, id: base.main_company}:
+    currency_id: base.EUR
+-
+ !record {model: product.pricelist, id: purchase.list0}:
+    name: Purchase Pricelist EUR
+    currency_id: base.EUR 
+    company_id: base.main_company
+-
+ !record {model: product.pricelist, id: product.list0}:
+    name: Public Pricelist EUR
+    currency_id: base.EUR 
+    company_id: base.main_company
+-
+  Create a sale pricelist in CHF for second company (also in CHF)
+-
+ !record {model: product.pricelist, id: product_pricelist_salechf}:
+    name: Public Pricelist CHF
+    type: sale
+    company_id: res_company_01
+    currency_id: base.CHF
+-
+ !record {model: product.pricelist.version, id: product_pricelist_version_salechf}:
+    pricelist_id: product_pricelist_salechf
+    name: Default Public Pricelist Version
+-
+ !record {model: product.pricelist.item, id: product_pricelist_item_salechf}:
+    price_version_id: product_pricelist_version_salechf
+    base: !eval ref('list_price_second_cmp')
+    name: Default Public Pricelist Line
+-
+  Create a purchase pricelist in CHF for second company (also in CHF)
+-
+ !record {model: product.pricelist, id: product_pricelist_purchchf}:
+    name: Purchase Pricelist CHF
+    type: purchase
+    company_id: res_company_01
+    currency_id: base.CHF
+-
+ !record {model: product.pricelist.version, id: product_pricelist_version_purchchf}:
+    pricelist_id: product_pricelist_purchchf
+    name: Default Purchase Pricelist Version
+-
+ !record {model: product.pricelist.item, id: product_pricelist_item_puchchf}:
+    price_version_id: product_pricelist_version_purchchf
+    base: !eval ref('standard_price_second_cmp')
+    name: Default Purchase Pricelist Line
+-
+  Create a stock location for first and second company
+-
+ !record {model: stock.location, id: location_stock_01}:
+    name: Stock first company EUR
+    usage: internal
+    company_id: base.main_company
+-
+ !record {model: stock.location, id: location_stock_02}:
+    name: Stock second company CHF
+    usage: internal
+    company_id: res_company_01
+-
+  Create a warehouse for first and second company
+-
+ !record {model: stock.warehouse, id: wh_stock_01}:
+    name: Warehouse for first company EUR
+    lot_output_id: location_stock_01
+    lot_stock_id: location_stock_01
+    lot_input_id: location_stock_01
+    company_id: base.main_company
+-
+ !record {model: stock.warehouse, id: wh_stock_02}:
+    name: Warehouse for second company CHF
+    lot_output_id: location_stock_02
+    lot_stock_id: location_stock_02
+    lot_input_id: location_stock_02
+    company_id: res_company_01
+-
+  Create a Supplier for PO
+-
+  !record {model: res.partner, id: res_partner_supplier_01}:
+    name: Supplier 1
+    supplier: 1
+-
+  Create a wine product J owned by both company
+-
+  !record {model: product.product, id: product_product_j_avg_01}:
+    categ_id: product.product_category_1
+    cost_method: average
+    name: Wine J
+    type: product
+    uom_id: product.product_uom_unit
+    uom_po_id: product.product_uom_unit
+    company_id: False
+-
+  Create a wine product K owned by both company
+-
+  !record {model: product.product, id: product_product_k_avg_01}:
+    categ_id: product.product_category_1
+    cost_method: average
+    name: Wine K
+    type: product
+    uom_id: product.product_uom_unit
+    uom_po_id: product.product_uom_unit
+    company_id: False
+-
+  Setup the multi company rules for product, share them between company
+-
+  !record {model: ir.rule, id: product.product_comp_rule}:
+    domain_force: "[(1,'=',1)]"

=== added directory 'product_price_history/i18n'
=== added file 'product_price_history/i18n/product_price_history.pot'
--- product_price_history/i18n/product_price_history.pot	1970-01-01 00:00:00 +0000
+++ product_price_history/i18n/product_price_history.pot	2013-12-04 10:41:12 +0000
@@ -0,0 +1,210 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* product_price_history
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-11-05 11:39+0000\n"
+"PO-Revision-Date: 2013-11-05 11:39+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: product_price_history
+#: model:stock.location,name:product_price_history.location_stock_02
+msgid "Stock second company CHF"
+msgstr ""
+
+#. module: product_price_history
+#: view:product.price.history:0
+msgid "Group By..."
+msgstr ""
+
+#. module: product_price_history
+#: model:product.pricelist,name:product_price_history.product_pricelist_purchchf
+msgid "Purchase Pricelist CHF"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model.fields,field_description:product_price_history.field_product_price_history_product_id
+#: view:product.price.history:0
+#: field:product.price.history,product_id:0
+msgid "Product"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.price.type,name:product_price_history.standard_price_second_cmp
+msgid "Cost Price USD"
+msgstr ""
+
+#. module: product_price_history
+#: view:historic.prices:0
+msgid "Compute prices"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model.fields,field_description:product_price_history.field_product_price_history_company_id
+#: model:ir.model.fields,field_description:product_price_history.field_product_price_type_company_id
+#: view:product.price.history:0
+#: field:product.price.history,company_id:0
+#: field:product.price.type,company_id:0
+msgid "Company"
+msgstr ""
+
+#. module: product_price_history
+#: code:addons/product_price_history/wizard/historic_prices.py:63
+#, python-format
+msgid "Historical Prices"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model.fields,field_description:product_price_history.field_product_price_history_name
+#: field:product.price.history,name:0
+msgid "Field name"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.pricelist.version,name:product_price_history.product_pricelist_version_salechf
+msgid "Default Public Pricelist Version"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.actions.act_window,name:product_price_history.action_product_historic_prices_view
+#: model:ir.ui.menu,name:product_price_history.menu_action_product_historic_prices_tree
+msgid "Product Historical Prices"
+msgstr ""
+
+#. module: product_price_history
+#: field:historic.prices,to_date:0
+#: model:ir.model.fields,field_description:product_price_history.field_historic_prices_to_date
+#: model:ir.model.fields,field_description:product_price_history.field_product_price_history_datetime
+#: view:product.price.history:0
+#: field:product.price.history,datetime:0
+msgid "Date"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.actions.act_window,name:product_price_history.act_product_prices_history_open
+#: model:ir.actions.act_window,name:product_price_history.action_price_history
+#: model:ir.ui.menu,name:product_price_history.menu_product_price_history_action_form
+msgid "Prices History"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.pricelist.version,name:product_price_history.product_pricelist_version_purchchf
+msgid "Default Purchase Pricelist Version"
+msgstr ""
+
+#. module: product_price_history
+#: view:product.price.history:0
+msgid "Search Prices History"
+msgstr ""
+
+#. module: product_price_history
+#: view:product.price.history:0
+msgid "Name"
+msgstr ""
+
+#. module: product_price_history
+#: view:historic.prices:0
+msgid "Historical prices"
+msgstr ""
+
+#. module: product_price_history
+#: model:res.company,overdue_msg:product_price_history.res_company_01
+msgid "Dear Sir/Madam,\n"
+"\n"
+"Our records indicate that some payments on your account are still due. Please find details below.\n"
+"If the amount has already been paid, please disregard this notice. Otherwise, please forward us the total amount stated below.\n"
+"If you have any queries regarding your account, Please contact us.\n"
+"\n"
+"Thank you in advance for your cooperation.\n"
+"Best Regards,"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model,name:product_price_history.model_product_price_history
+msgid "product.price.history"
+msgstr ""
+
+#. module: product_price_history
+#: view:product.price.history:0
+msgid "Price field"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model.fields,field_description:product_price_history.field_product_price_history_amount
+#: field:product.price.history,amount:0
+msgid "Amount"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.price.type,name:product_price_history.list_price_second_cmp
+msgid "Public Price USD"
+msgstr ""
+
+#. module: product_price_history
+#: model:stock.location,name:product_price_history.location_stock_01
+msgid "Stock first company EUR"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.pricelist,name:product_price_history.product_pricelist_salechf
+msgid "Public Pricelist CHF"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.template,name:product_price_history.product_product_k_avg_01_product_template
+msgid "Wine K"
+msgstr ""
+
+#. module: product_price_history
+#: model:product.template,name:product_price_history.product_product_j_avg_01_product_template
+msgid "Wine J"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model,name:product_price_history.model_product_template
+msgid "Product Template"
+msgstr ""
+
+#. module: product_price_history
+#: view:product.price.history:0
+msgid "Historic Prices"
+msgstr ""
+
+#. module: product_price_history
+#: help:historic.prices,to_date:0
+msgid "Date at which the analysis need to be done. Note that the date is understood as this day at midnight, so you may want to specify the day after ! No date is the last value."
+msgstr ""
+
+#. module: product_price_history
+#: view:product.product:0
+msgid "Product Historic Prices"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model,name:product_price_history.model_historic_prices
+msgid "Product historical prices"
+msgstr ""
+
+#. module: product_price_history
+#: view:historic.prices:0
+msgid "Cancel"
+msgstr ""
+
+#. module: product_price_history
+#: view:historic.prices:0
+msgid "or"
+msgstr ""
+
+#. module: product_price_history
+#: model:ir.model,name:product_price_history.model_product_price_type
+msgid "Price Type"
+msgstr ""
+

=== added file 'product_price_history/product_price_history.py'
--- product_price_history/product_price_history.py	1970-01-01 00:00:00 +0000
+++ product_price_history/product_price_history.py	2013-12-04 10:41:12 +0000
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright 2013 Camptocamp SA
+#    Author: Joel Grand-Guillaume
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import logging
+import time
+from openerp.osv import orm, fields
+import openerp.addons.decimal_precision as dp
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
+
+# All field name of product that will be historize
+PRODUCT_FIELD_HISTORIZE = ['standard_price', 'list_price']
+
+_logger = logging.getLogger(__name__)
+
+class product_price_history(orm.Model):
+    # TODO : Create good index for select
+
+    _name = 'product.price.history'
+    _order = 'datetime, company_id asc'
+
+    _columns = {
+        'name': fields.char('Field name', size=32, required=True),
+        'company_id': fields.many2one('res.company', 'Company',
+                                      required=True),
+        'product_id': fields.many2one('product.template', 'Product',
+                                      required=True),
+        'datetime': fields.datetime('Date'),
+        'amount': fields.float('Amount',
+                               digits_compute=dp.get_precision('Product Price')),
+        }
+
+    def _get_default_company(self, cr, uid, context=None):
+        company = self.pool.get('res.company')
+        return company._company_default_get(cr, uid,
+                                            'product.template',
+                                            context=context)
+
+    def _get_default_date(self, cr, uid, context=None):
+        if context is None:
+            context = {}
+        if context.get('to_date'):
+            result = context.get('to_date')
+        else:
+            result = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+        return result
+
+    _defaults = {
+        'company_id': _get_default_company,
+        'datetime': _get_default_date,
+        }
+
+    def _get_historic_price(self, cr, uid, ids, company_id,
+                            datetime=False, field_name=None,
+                            context=None):
+        """ Use SQL for performance. Return a dict like:
+            {product_id:{'standard_price': Value, 'list_price': Value}}
+            If no value found, return 0.0 for each field and products.
+        """
+        res = {}
+        if not ids:
+            return res
+        if field_name is None:
+            field_name = PRODUCT_FIELD_HISTORIZE
+        if not datetime:
+            datetime = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+        sql_wh_clause = """SELECT DISTINCT ON (product_id, name)
+            datetime, product_id, name, amount
+            FROM product_price_history
+            WHERE product_id IN %s
+            AND datetime <= %s
+            AND company_id = %s
+            AND name IN %s
+            ORDER BY product_id, name, datetime DESC"""
+        cr.execute(sql_wh_clause, (tuple(ids), datetime,
+                   company_id, tuple(field_name)))
+        for id in ids:
+            res[id] = dict.fromkeys(field_name, 0.0)
+        result = cr.dictfetchall()
+        for line in result:
+            data = {line['name']: line['amount']}
+            res[line['product_id']].update(data)
+        _logger.debug("Result of price history is : %s, company_id: %s", res, company_id)
+        return res
+
+
+class product_template(orm.Model):
+
+    _inherit = "product.template"
+
+    def _log_all_price_changes(self, cr, uid, product, values, context=None):
+        """
+        For each field to historize, call the _log_price_change method
+        @param: values dict of vals used by write and create od product
+        @param: int product ID
+        """
+        for field_name in PRODUCT_FIELD_HISTORIZE:
+            if values.get(field_name): 
+                amount = values[field_name]
+                self._log_price_change(cr, uid, product, field_name, 
+                                       amount, context=context)
+        return True
+
+    def _log_price_change(self, cr, uid, product, field_name, amount, context=None):
+        """
+        On change of price create a price_history
+        :param int product value of new product or product_id
+        """
+        price_history = self.pool.get('product.price.history')
+        data = {
+            'product_id': product,
+            'amount': amount,
+            'name': field_name,
+            'company_id': self._get_transaction_company_id(cr, uid,
+                context=context)
+            }
+        return price_history.create(cr, uid, data, context=context)
+
+    def _get_transaction_company_id(self, cr, uid, context=None):
+        """As it may happend that OpenERP force the uid to 1 to bypass
+        rule (in function field), we may sometimes read the price of the company 
+        of user id 1 instead of the good one. Because we found the real uid and company_id
+        in the context in that case, I return this one. It also allow other module to 
+        give the proper company_id in the context (like it's done in product_standard_margin
+        for example. If company_id not in context, take the one from uid."""
+        res = uid
+        if context == None:
+            context = {}
+        if context.get('company_id'):
+            res = context.get('company_id')
+        else:
+            user_obj = self.pool.get('res.users')
+            res = user_obj.browse(cr, uid, uid, 
+                context=context).company_id.id
+        return res
+
+    def create(self, cr, uid, values, context=None):
+        """Add the historization at product creation."""
+        res = super(product_template, self).create(cr, uid, values,
+                                                   context=context)
+        self._log_all_price_changes(cr, uid, res, values, context=context)
+        return res
+
+    def read(self, cr, uid, ids, fields=None, context=None,
+             load='_classic_read'):
+        """Override the read to take price values from the related
+        price history table."""
+        if context is None:
+            context = {}
+        if fields:
+            fields.append('id')
+        results = super(product_template, self).read(
+            cr, uid, ids, fields=fields, context=context, load=load)
+        # Note if fields is empty => read all, so look at history table
+        if not fields or any([f in PRODUCT_FIELD_HISTORIZE for f in fields]):
+            date_crit = False
+            price_history = self.pool.get('product.price.history')
+            company_id = self._get_transaction_company_id(cr, uid, context=context)
+            if context.get('to_date'):
+                date_crit = context['to_date']
+            # if fields is empty we read all price fields
+            if not fields:
+                price_fields = PRODUCT_FIELD_HISTORIZE
+            # Otherwise we filter on price fields asked in read
+            else:
+                price_fields = [f for f in PRODUCT_FIELD_HISTORIZE if f in fields]
+            prod_prices = price_history._get_historic_price(cr, uid, ids,
+                                                            company_id,
+                                                            datetime=date_crit,
+                                                            field_name=price_fields,
+                                                            context=context)
+            for result in results:
+                dict_value = prod_prices[result['id']]
+                result.update(dict_value)
+        return results
+
+    def write(self, cr, uid, ids, values, context=None):
+        """Create an entry in the history table for every modified price
+        of every products with current datetime (or given one in context)"""
+        if any([f in PRODUCT_FIELD_HISTORIZE for f in values]):
+            for product in self.browse(cr, uid, ids, context=context):
+                self._log_all_price_changes(cr, uid, product.id, values,
+                                       context=context)
+        return super(product_template, self).write(cr, uid, ids, values,
+                                                   context=context)
+
+    def unlink(self, cr, uid, ids, context=None):
+        price_history = self.pool.get('product.price.history')
+        history_ids = price_history.search(cr, uid,
+                                           [('product_id', 'in', ids)],
+                                           context=context)
+        price_history.unlink(cr, uid, history_ids, context=context)
+        res = super(product_template, self).unlink(cr, uid, ids,
+                                                   context=context)
+        return res
+
+
+class price_type(orm.Model):
+    """
+        The price type is used to points which field in the product form
+        is a price and in which currency is this price expressed.
+        Here, we add the company field to allow having various price type for
+        various company, may be even in different currency.
+    """
+
+    _inherit = "product.price.type"
+
+    _columns = {
+        'company_id': fields.many2one('res.company', 'Company',
+                                      required=True),
+    }
+
+    def _get_default_company(self, cr, uid, context=None):
+        company = self.pool.get('res.company')
+        return company._company_default_get(cr, uid,
+                                            'product.price.type',
+                                            context=context)
+    _defaults = {
+        'company_id': _get_default_company,
+        }

=== added file 'product_price_history/product_price_history_view.xml'
--- product_price_history/product_price_history_view.xml	1970-01-01 00:00:00 +0000
+++ product_price_history/product_price_history_view.xml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+
+    <record id="product_price_type_view" model="ir.ui.view">
+        <field name="name">product.price.type.form</field>
+        <field name="model">product.price.type</field>
+        <field name="inherit_id" ref="product.product_price_type_view"/>
+        <field name="arch" type="xml">
+            <field name="currency_id" groups="base.group_multi_currency" position="after">
+              <field name="company_id" groups="base.group_multi_company"/>
+            </field>
+        </field>
+    </record>
+
+    <record id="view_product_price_history" model="ir.ui.view">
+      <field name="name">product.product.price.history.tree</field>
+      <field name="model">product.product</field>
+      <field name="arch" type="xml">
+        <tree string="Product Historic Prices" create="false">
+          <field name="default_code"/>
+          <field name="name"/>
+          <field name="categ_id" invisible="1"/>
+          <field name="variants" groups="product.group_product_variant"/>
+          <field name="type"/>
+          <field name="state" groups="base.group_extended"/>
+          <field name="list_price"/>
+          <field name="standard_price"/>
+          <field name="company_id" groups="base.group_multi_company" invisible="1"/>
+        </tree>
+      </field>
+    </record>
+
+    <record id="view_product_price_history_from_product" model="ir.ui.view">
+      <field name="name">product.price.history.tree</field>
+      <field name="model">product.price.history</field>
+      <field name="arch" type="xml">
+        <tree string="Historic Prices" create="false">
+          <field name="datetime"/>
+          <field name="name"/>
+          <field name="company_id" groups="base.group_multi_company" invisible="1"/>
+          <field name="product_id"/>
+          <field name="amount"/>
+        </tree>
+      </field>
+    </record>
+
+    <record id="view_product_price_history_filter" model="ir.ui.view">
+        <field name="name">product.price.history.filter</field>
+        <field name="model">product.price.history</field>
+        <field name="arch" type="xml">
+            <search string="Search Prices History">
+                <field name="name" string="Price field"/>
+                <field name="product_id"/>
+                <field name="company_id" groups="base.group_multi_company"/>
+                <group expand="0" string="Group By...">
+                    <filter string="Date" icon="terp-go-month" domain="[]" context="{'group_by':'datetime'}"/>
+                    <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
+                    <filter string="Name" domain="[]" context="{'group_by':'name'}"/>
+                    <filter string="Company" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
+                </group>
+            </search>
+
+        </field>
+    </record>
+
+    <record id="action_price_history" model="ir.actions.act_window">
+        <field name="name">Prices History</field>
+        <field name="type">ir.actions.act_window</field>
+        <field name="res_model">product.price.history</field>
+        <field name="view_type">form</field>
+        <field name="view_id" ref="view_product_price_history_from_product"/>
+        <field name="search_view_id" ref="view_product_price_history_filter"/>
+    </record>
+
+    <act_window
+            context="{'search_default_product_id': [active_id], 'default_product_id': active_id}"
+            id="act_product_prices_history_open"
+            name="Prices History"
+            res_model="product.price.history"
+            src_model="product.product"/>
+
+    <menuitem action="action_price_history"
+            groups="base.group_no_one"
+            id="menu_product_price_history_action_form"
+            parent="product.prod_config_main" sequence="2"/>
+
+
+  </data>
+</openerp>

=== added directory 'product_price_history/security'
=== added file 'product_price_history/security/ir.model.access.csv'
--- product_price_history/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ product_price_history/security/ir.model.access.csv	2013-12-04 10:41:12 +0000
@@ -0,0 +1,7 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"access_product_price_history_group_user","product_price_history user","model_product_price_history","base.group_user",1,0,0,0
+"access_product_price_history_group_sale_manager","product_price_history sale manager","model_product_price_history","base.group_sale_manager",1,1,1,1
+"access_product_price_history_stock_manager","product_price_history stock manager","model_product_price_history","stock.group_stock_manager",1,1,1,1
+"access_product_price_history_stock_user","product_price_history stock user","model_product_price_history","stock.group_stock_user",1,1,0,0
+"access_product_price_history_purchase_user","product_price_history purchase user","model_product_price_history","purchase.group_purchase_user",1,0,0,0
+"access_product_price_history_purchase_manager","product_price_history purchase manager","model_product_price_history","purchase.group_purchase_manager",1,1,1,1

=== added file 'product_price_history/security/product_price_history_security.xml'
--- product_price_history/security/product_price_history_security.xml	1970-01-01 00:00:00 +0000
+++ product_price_history/security/product_price_history_security.xml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data noupdate="1">
+
+    <record id="product_price_type_comp_rule" model="ir.rule">
+        <field name="name" >Product Price type multi-company</field>
+        <field name="model_id" ref="model_product_price_type"/>
+        <field name="global" eval="True"/>
+        <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
+    </record>
+    
+</data>
+</openerp>

=== added directory 'product_price_history/test'
=== added file 'product_price_history/test/avg_price_computation_mutlicompanies_multicurrencies.yml'
--- product_price_history/test/avg_price_computation_mutlicompanies_multicurrencies.yml	1970-01-01 00:00:00 +0000
+++ product_price_history/test/avg_price_computation_mutlicompanies_multicurrencies.yml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,186 @@
+-
+  Test the following with user admin from first company (EUR)
+-
+  !context
+    uid: 'base.user_root'
+-
+  Create a purchase order for first company EUR
+-
+  !record {model: purchase.order, id: purchase_order_lcost_01}:
+    partner_id: res_partner_supplier_01
+    invoice_method: order
+    location_id: location_stock_01
+    pricelist_id: purchase.list0
+    company_id: base.main_company
+    order_line:
+      - product_id: product_product_j_avg_01
+        price_unit: 100
+        product_qty: 15.0
+-
+ I confirm the order where invoice control is 'Bases on order'.
+-
+  !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_01}
+-
+  Reception is ready to process, make it and check moves value
+-
+  !python {model: stock.partial.picking}: |
+    pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_01")).picking_ids
+    partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
+    self.do_partial(cr, uid, [partial_id])
+    picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0]
+    for move in picking.move_lines:
+      if move.product_id.name == 'Wine J':
+        assert move.price_unit == 100.0,"Technical field price_unit of Wine J stock move should record the purchase price"
+-
+  I check that purchase order is shipped.
+-
+  !python {model: purchase.order}: |
+     assert self.browse(cr, uid, ref("purchase_order_lcost_01")).shipped == True,"Purchase order should be delivered"
+-
+  I check that avg price of products is computed correctly
+-
+  !python {model: product.product}: |
+     xchg_rate_chf = 1.0
+     # computed as : (100 * 15 / 15) * Exchnge rate of 1.3086
+     value_a = round(100.0 * xchg_rate_chf, 2)
+     assert self.browse(cr, uid, ref("product_product_j_avg_01")).standard_price == value_a,"Avg price for product Wine J for first company is wrongly computed"
+-
+  Create a second purchase order for first company EUR
+-
+  !record {model: purchase.order, id: purchase_order_lcost_01bis}:
+    partner_id: res_partner_supplier_01
+    invoice_method: order
+    location_id: location_stock_01
+    pricelist_id: purchase.list0
+    company_id: base.main_company
+    order_line:
+      - product_id: product_product_j_avg_01
+        price_unit: 200
+        product_qty: 15.0
+-
+ I confirm the order where invoice control is 'Bases on order'.
+-
+  !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_01bis}
+-
+  Reception is ready for process, make it and check moves value
+-
+  !python {model: stock.partial.picking}: |
+    pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_01bis")).picking_ids
+    partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
+    self.do_partial(cr, uid, [partial_id])
+    picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0]
+    for move in picking.move_lines:
+      if move.product_id.name == 'Wine J':
+        assert move.price_unit == 200.0,"Technical field price_unit of Wine J stock move should record the purchase price"
+-
+  I check that purchase order is shipped.
+-
+  !python {model: purchase.order}: |
+     assert self.browse(cr, uid, ref("purchase_order_lcost_01bis")).shipped == True,"Purchase order should be delivered"
+-
+  I check that avg price of products is computed correctly
+-
+  !python {model: product.product}: |
+     xchg_rate_chf = 1.0
+     # Value in stock in EUR
+     value_a = round(100.0 * xchg_rate_chf, 2)
+     # computed as : (value_a * 15 + (200 * xchg_rate_chf) * 15) / 30
+     value_abis = round((value_a * 15 + (200 * xchg_rate_chf) * 15) / 30, 2)
+     assert self.browse(cr, uid, ref("product_product_j_avg_01")).standard_price == value_abis,"Avg price for product Wine J for first company is wrongly computed"
+-
+  Test the following with user admin from second company (CHF)
+-
+  !context
+    uid: 'res_users_second_company_01'
+-
+  Create a purchase order for second company CHF
+-
+  !record {model: purchase.order, id: purchase_order_lcost_02}:
+    partner_id: res_partner_supplier_01
+    invoice_method: manual
+    location_id: location_stock_02
+    pricelist_id: product_pricelist_purchchf
+    company_id: res_company_01
+    order_line:
+      - product_id: product_product_j_avg_01
+        price_unit: 50
+        product_qty: 15.0
+-
+ I confirm the order where invoice control is 'Bases on order'.
+-
+  !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_02}
+-
+  Reception is ready for process, make it and check moves value
+-
+  !python {model: stock.partial.picking}: |
+    pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_02")).picking_ids
+    partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
+    self.do_partial(cr, uid, [partial_id])
+    picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0]
+    for move in picking.move_lines:
+      if move.product_id.name == 'Wine J':
+        assert move.price_unit == 50.0,"Technical field price_unit of Wine J stock move should record the purchase price"
+-
+  I check that purchase order is shipped.
+-
+  !python {model: purchase.order}: |
+     assert self.browse(cr, uid, ref("purchase_order_lcost_02")).shipped == True,"Purchase order should be delivered"
+-
+  I check that avg price of products is computed correctly
+-
+  !python {model: product.product}: |
+     # value in USD stored
+     value_a = round(50 * (1.2086 / 1.3086), 2)
+     assert self.browse(cr, uid, ref("product_product_j_avg_01")).standard_price == value_a,"Avg price for product Wine J for second company is wrongly computed"
+-
+  Create a second purchase order for second company CHF
+-
+  !record {model: purchase.order, id: purchase_order_lcost_02bis}:
+    partner_id: res_partner_supplier_01
+    invoice_method: manual
+    location_id: location_stock_02
+    pricelist_id: product_pricelist_purchchf
+    company_id: res_company_01
+    order_line:
+      - product_id: product_product_j_avg_01
+        price_unit: 100
+        product_qty: 15.0
+-
+ I confirm the order where invoice control is 'Bases on order'.
+-
+  !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_02bis}
+-
+  Reception is ready for process, make it and check moves value
+-
+  !python {model: stock.partial.picking}: |
+    pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_02bis")).picking_ids
+    partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
+    self.do_partial(cr, uid, [partial_id])
+    picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0]
+    for move in picking.move_lines:
+      if move.product_id.name == 'Wine J':
+        assert move.price_unit == 100.0,"Technical field price_unit of Wine J stock move should record the purchase price"
+-
+  I check that purchase order is shipped.
+-
+  !python {model: purchase.order}: |
+     assert self.browse(cr, uid, ref("purchase_order_lcost_02bis")).shipped == True,"Purchase order should be delivered"
+-
+  I check that avg price of products is computed correctly
+-
+  !python {model: product.product}: |
+     # Value in stock in USD for first entry (compute as to_currency / from_currency)
+     value_a = round(50 * (1.2086 / 1.3086), 2)
+     # Value in stock in USD for second entry (compute as to_currency / from_currency)
+     value_b = round(100 * (1.2086 / 1.3086), 2)
+     # computed as : (value_a * 15 + value_b * 15) / 30
+     value_abis = round((value_a * 15 + value_b * 15) / 30, 2)
+     assert self.browse(cr, uid, ref("product_product_j_avg_01")).standard_price == value_abis,"Avg price for product Wine J for second company is wrongly computed"
+-
+  I Check that I get all entries in the price history table
+-
+  !python {model: product.price.history}: |
+     num_j = self.search(cr, uid, [('product_id','=',ref("product_product_j_avg_01")),('name','=','standard_price')])
+     # 4 PO, 4 updates of standard_price
+     right_number_j = 4
+     assert len(num_j) == right_number_j,"The number of value in the price history table is correct for product J"

=== added file 'product_price_history/test/price_controlling_multicompany.yml'
--- product_price_history/test/price_controlling_multicompany.yml	1970-01-01 00:00:00 +0000
+++ product_price_history/test/price_controlling_multicompany.yml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,55 @@
+-
+  Test the following with user admin from first company (EUR)
+-
+  !context
+    uid: 'base.user_root'
+-
+  Create a wine A product owned by both company and set price for first company (EUR)
+-
+  !record {model: product.product, id: product_product_a_avg_01}:
+    categ_id: product.product_category_1
+    name: Wine A
+    cost_method: standard
+    uom_id: product.product_uom_unit
+    uom_po_id: product.product_uom_unit
+    company_id: 0
+    standard_price: 50.0
+    list_price: 75.0
+-
+  Test the prices are those set for the first company (EUR)
+-
+  !python {model: product.product}: |
+    product = self.browse(cr, uid, ref('product_product_a_avg_01'))
+    assert product.standard_price == 50.0, "The standard_price has not been recorded correctly for first company"
+    assert product.list_price == 75.0, "The list_price has not been recorded correctly for first company"
+-
+  Test the following with user second_user from second company (CHF)
+-
+  !context
+    uid: 'res_users_second_company_01'
+-
+  Modify product A owned by both company and set price in USD for second company (CHF)
+-
+  !python {model: product.product}: |
+    self.write(cr, uid, ref('product_product_a_avg_01'), {'standard_price':70,'list_price':90})
+-
+  Test the USD prices are those set for the second company (CHF)
+-
+  !python {model: product.product}: |
+    product = self.browse(cr, uid, ref('product_product_a_avg_01'))
+    assert product.standard_price == 70.0, "The standard_price has not been recorded correctly for first company"
+    assert product.list_price == 90.0, "The list_price has not been recorded correctly for first company"
+-
+  Test the following with user admin from first company (EUR)
+-
+  !context
+    uid: 'base.user_root'
+-
+  Test the prices are still the same for first company (EUR)
+-
+  !python {model: product.product}: |
+    product = self.browse(cr, uid, ref('product_product_a_avg_01'))
+    assert product.standard_price == 50.0, "The standard_price has not been recorded correctly for first company"
+    assert product.list_price == 75.0, "The list_price has not been recorded correctly for first company"
+
+

=== added file 'product_price_history/test/price_historization.yml'
--- product_price_history/test/price_historization.yml	1970-01-01 00:00:00 +0000
+++ product_price_history/test/price_historization.yml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,55 @@
+-
+  Test the following with user admin from first company (EUR)
+-
+  !context
+    uid: 'base.user_root'
+-
+  Modify product K at first day of year and set price in EUR for first company (EUR)
+-
+  !python {model: product.product}: |
+    import time
+    ctx = context
+    ctx.update({'to_date':time.strftime('%Y-01-01 %H:%M:%S')})
+    print ctx
+    self.write(cr, uid, ref('product_product_k_avg_01'), {'standard_price':70,'list_price':90}, context=ctx)
+-
+  Test the EUR prices are those just set on the first day of year
+-
+  !python {model: product.product}: |
+    import time
+    ctx = context
+    ctx.update({'to_date':time.strftime('%Y-01-01 %H:%M:%S')})
+    product = self.browse(cr, uid, ref('product_product_k_avg_01'), context=ctx)
+    assert product.standard_price == 70.0, "The standard_price has not been recorded correctly for first company"
+    assert product.list_price == 90.0, "The list_price has not been recorded correctly for first company"
+-
+  Modify product K at 3rd day of year and set price in EUR for first company (EUR)
+-
+  !python {model: product.product}: |
+    import time
+    ctx = context
+    ctx.update({'to_date':time.strftime('%Y-01-03 %H:%M:%S')})
+    print ctx
+    self.write(cr, uid, ref('product_product_k_avg_01'), {'standard_price':80,'list_price':100}, context=ctx)
+-
+  Test the EUR prices 2nd day of year
+-
+  !python {model: product.product}: |
+    import time
+    ctx = context
+    ctx.update({'to_date':time.strftime('%Y-01-02 %H:%M:%S')})
+    print ctx
+    product = self.browse(cr, uid, ref('product_product_k_avg_01'),context=ctx)
+    assert product.standard_price == 70.0, "The standard_price has not been retrieved correctly for first company"
+    assert product.list_price == 90.0, "The list_price has not been retrieved correctly for first company"
+-
+  Test the EUR prices 3rd day of year
+-
+  !python {model: product.product}: |
+    import time
+    ctx = context
+    ctx.update({'to_date':time.strftime('%Y-01-03 %H:%M:%S')})
+    print ctx
+    product = self.browse(cr, uid, ref('product_product_k_avg_01'),context=ctx)
+    assert product.standard_price == 80.0, "The standard_price has not been retrieved correctly for first company"
+    assert product.list_price == 100.0, "The list_price has not been retrieved correctly for first company"

=== added directory 'product_price_history/wizard'
=== added file 'product_price_history/wizard/__init__.py'
--- product_price_history/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ product_price_history/wizard/__init__.py	2013-12-04 10:41:12 +0000
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright 2013 Camptocamp SA
+#    Author: Joel Grand-Guillaume
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+from . import historic_prices
+

=== added file 'product_price_history/wizard/historic_prices.py'
--- product_price_history/wizard/historic_prices.py	1970-01-01 00:00:00 +0000
+++ product_price_history/wizard/historic_prices.py	2013-12-04 10:41:12 +0000
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle, Joel Grand-Guillaume
+#    Copyright 2012 Camptocamp SA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
+import time
+
+
+class historic_prices(orm.TransientModel):
+    _name = 'historic.prices'
+    _description = 'Product historical prices'
+
+    _columns = {
+        'to_date': fields.date(
+            'Date',
+            help='Date at which the analysis need to be done. '
+            'Note that the date is understood as this day at midnight, so you may want to '
+            'specify the day after ! No date is the last value.'),
+        }
+
+    def action_open_window(self, cr, uid, ids, context=None):
+        """
+        Open the historical prices view
+        """
+        if context is None:
+            context = {}
+        user_obj = self.pool.get('res.users')
+        wiz = self.read(cr, uid, ids, [], context=context)[0]
+        ctx = context.copy()
+        if wiz.get('to_date'):
+            ctx.update(
+                to_date=wiz.get('to_date')
+                )
+        data_pool = self.pool.get('ir.model.data')
+        filter_ids = data_pool.get_object_reference(cr, uid, 'product',
+                                                    'product_search_form_view')
+        product_view_id = data_pool.get_object_reference(cr, uid,
+                                                         'product_price_history',
+                                                         'view_product_price_history')
+        if filter_ids:
+            filter_id = filter_ids[1]
+        else:
+            filter_id = 0
+        return {
+            'type': 'ir.actions.act_window',
+            'name': _('Historical Prices'),
+            'context': ctx,
+            'view_type': 'form',
+            'view_mode': 'tree',
+            'res_model': 'product.product',
+            'view_id': product_view_id[1],
+            'search_view_id': filter_id,
+            }

=== added file 'product_price_history/wizard/historic_prices_view.xml'
--- product_price_history/wizard/historic_prices_view.xml	1970-01-01 00:00:00 +0000
+++ product_price_history/wizard/historic_prices_view.xml	2013-12-04 10:41:12 +0000
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+    <record id="view_historical_prices" model="ir.ui.view">
+      <field name="name">historic.prices.form</field>
+      <field name="model">historic.prices</field>
+      <field name="arch" type="xml">
+        <form string="Historical prices" version="7.0">
+          <group>
+            <field name="to_date"/>
+          </group>
+          <footer>
+            <button name="action_open_window" string="Compute prices" type="object" icon="gtk-execute" class="oe_highlight"/>
+            or
+            <button string="Cancel" class="oe_link" special="cancel" />
+          </footer>
+        </form>
+      </field>
+    </record>
+
+    <record id="action_product_historic_prices_view" model="ir.actions.act_window">
+      <field name="name">Product Historical Prices</field>
+      <field name="res_model">historic.prices</field>
+      <field name="view_type">form</field>
+      <field name="view_mode">tree,form</field>
+      <field name="view_id" ref="view_historical_prices"/>
+      <field name="target">new</field>
+    </record>
+
+    <menuitem action="action_product_historic_prices_view"
+        id="menu_action_product_historic_prices_tree"
+        parent="stock.next_id_61" />
+
+
+  </data>
+</openerp>


References