openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #01884
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