From b32b977f5544fb11436ea5ee1662d659e932bf1c Mon Sep 17 00:00:00 2001 From: Lorenzo Battistini Date: Fri, 10 May 2013 10:55:26 +0200 Subject: [PATCH 01/35] [add] skeleton --- stock_lot_valuation/AUTHORS.txt | 1 + stock_lot_valuation/__init__.py | 21 +++++++++++++++ stock_lot_valuation/__openerp__.py | 38 ++++++++++++++++++++++++++++ stock_lot_valuation/product.py | 29 +++++++++++++++++++++ stock_lot_valuation/product_view.xml | 14 ++++++++++ stock_lot_valuation/stock.py | 30 ++++++++++++++++++++++ 6 files changed, 133 insertions(+) create mode 100644 stock_lot_valuation/AUTHORS.txt create mode 100644 stock_lot_valuation/__init__.py create mode 100644 stock_lot_valuation/__openerp__.py create mode 100644 stock_lot_valuation/product.py create mode 100644 stock_lot_valuation/product_view.xml create mode 100644 stock_lot_valuation/stock.py diff --git a/stock_lot_valuation/AUTHORS.txt b/stock_lot_valuation/AUTHORS.txt new file mode 100644 index 000000000..7106ca0eb --- /dev/null +++ b/stock_lot_valuation/AUTHORS.txt @@ -0,0 +1 @@ +Lorenzo Battistini diff --git a/stock_lot_valuation/__init__.py b/stock_lot_valuation/__init__.py new file mode 100644 index 000000000..e6547b102 --- /dev/null +++ b/stock_lot_valuation/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013 Agile Business Group sagl () +# +# 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 . +# +############################################################################## + +import product diff --git a/stock_lot_valuation/__openerp__.py b/stock_lot_valuation/__openerp__.py new file mode 100644 index 000000000..4139db30d --- /dev/null +++ b/stock_lot_valuation/__openerp__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013 Agile Business Group sagl () +# +# 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 . +# +############################################################################## + +{ + 'name': "Lot Valuation", + 'version': '0.1', + 'category': 'Warehouse Management', + 'description': """ +Stock valuation (standard or average price, ...) based on lots. +This module extends standard stock valuation (based on products). Valuing lots allows to have different costs for different lots of the same product. +""", + 'author': 'Agile Business Group', + 'website': 'http://www.agilebg.com', + 'license': 'AGPL-3', + "depends" : ['stock'], + "init_xml" : [], + "update_xml" : [], + "demo_xml" : [], + "active": False, + "installable": True +} diff --git a/stock_lot_valuation/product.py b/stock_lot_valuation/product.py new file mode 100644 index 000000000..49018c873 --- /dev/null +++ b/stock_lot_valuation/product.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013 Agile Business Group sagl () +# +# 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 . +# +############################################################################## + +from openerp.osv import fields, orm +from openerp.tools.translate import _ + +class product_product(orm.Model): + _inherit = "product.product" + + _columns = { + 'lot_valuation':fields.boolean('Lot valuation', help="Use lot valuation instead of product valuation"), + } diff --git a/stock_lot_valuation/product_view.xml b/stock_lot_valuation/product_view.xml new file mode 100644 index 000000000..9326f93c6 --- /dev/null +++ b/stock_lot_valuation/product_view.xml @@ -0,0 +1,14 @@ + + + + + product_normal_form_view + product.product + + + + + + + + diff --git a/stock_lot_valuation/stock.py b/stock_lot_valuation/stock.py new file mode 100644 index 000000000..f0c9b3457 --- /dev/null +++ b/stock_lot_valuation/stock.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013 Agile Business Group sagl () +# +# 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 . +# +############################################################################## + +from openerp.osv import fields, orm +from openerp.tools.translate import _ + +class stock_move(orm.Model): + _inherit = "stock.move" + + def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None): + res = super(stock_move,self)._get_reference_accounting_values_for_valuation( + cr, uid, move, context=context) + return res From 80470b41c5510acc7798797fc58f39f0c69465f8 Mon Sep 17 00:00:00 2001 From: Lorenzo Battistini Date: Fri, 10 May 2013 11:16:54 +0200 Subject: [PATCH 02/35] [add] methods --- stock_lot_valuation/stock.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/stock_lot_valuation/stock.py b/stock_lot_valuation/stock.py index f0c9b3457..bc80286c0 100644 --- a/stock_lot_valuation/stock.py +++ b/stock_lot_valuation/stock.py @@ -21,10 +21,45 @@ from openerp.osv import fields, orm from openerp.tools.translate import _ +class stock_production_lot(osv.osv): + _inherit = "stock.production.lot" + + _columns = { + 'standard_price': fields.float('Cost', digits_compute=dp.get_precision('Lot Price'), help="Cost price (in company currency) of the lot used for standard stock valuation in accounting.", groups="base.group_user"), + 'cost_method': fields.selection([('standard','Standard Price'), ('average','Average Price')], 'Costing Method', required=True, + help="Standard Price: The cost price is manually updated at the end of a specific period (usually every year). \nAverage Price: The cost price is recomputed at each incoming shipment."), + } + + def price_get(self, cr, uid, ids, context=None): + if context is None: + context = {} + res = {} + product_uom_obj = self.pool.get('product.uom') + for lot in self.browse(cr, uid, ids, context=context): + res[lot.id] = lot['standard_price'] or 0.0 + if 'uom' in context: + uom = lot.product_id.uom_id or lot.product_id.uos_id + res[lot.id] = product_uom_obj._compute_price(cr, uid, + uom.id, res[lot.id], context['uom']) + # Convert from price_type currency to asked one + if 'currency_id' in context: + res[lot.id] = self.pool.get('res.currency').compute(cr, uid, + lot.company_id.currency_id.id, + context['currency_id'], res[lot.id],context=context) + return res + class stock_move(orm.Model): _inherit = "stock.move" def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None): res = super(stock_move,self)._get_reference_accounting_values_for_valuation( cr, uid, move, context=context) + if not move.product_id.cost_method == 'average' or not move.price_unit: + if move.prodlot_id: + if context is None: + context = {} + currency_ctx = dict(context, currency_id = move.company_id.currency_id.id) + amount_unit = move.prodlot_id.price_get(context=currency_ctx)[move.prodlot_id.id] + reference_amount = amount_unit * qty + res[0] = reference_amount return res From 471f5e08bf717219c3677d705f7af1a3548f3b6d Mon Sep 17 00:00:00 2001 From: Lorenzo Battistini Date: Fri, 10 May 2013 11:53:19 +0200 Subject: [PATCH 03/35] [add] do_change_standard_price --- stock_lot_valuation/stock.py | 115 ++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/stock_lot_valuation/stock.py b/stock_lot_valuation/stock.py index bc80286c0..5918da384 100644 --- a/stock_lot_valuation/stock.py +++ b/stock_lot_valuation/stock.py @@ -47,6 +47,119 @@ class stock_production_lot(osv.osv): lot.company_id.currency_id.id, context['currency_id'], res[lot.id],context=context) return res + + def do_change_standard_price(self, cr, uid, ids, datas, context=None): + """ Changes the Standard Price of Lot and creates an account move accordingly. + @param datas : dict. contain default datas like new_price, stock_output_account, stock_input_account, stock_journal + @param context: A standard dictionary + """ + location_obj = self.pool.get('stock.location') + move_obj = self.pool.get('account.move') + move_line_obj = self.pool.get('account.move.line') + if context is None: + context = {} + + new_price = datas.get('new_price', 0.0) + stock_output_acc = datas.get('stock_output_account', False) + stock_input_acc = datas.get('stock_input_account', False) + journal_id = datas.get('stock_journal', False) + lot_obj=self.browse(cr, uid, ids, context=context)[0] + account_valuation = lot_obj.product_it.categ_id.property_stock_valuation_account_id + account_valuation_id = account_valuation and account_valuation.id or False + if not account_valuation_id: raise osv.except_osv(_('Error!'), _('Specify valuation Account for Product Category: %s.') % (lot_obj.product_it.categ_id.name)) + move_ids = [] + loc_ids = location_obj.search(cr, uid,[('usage','=','internal')]) + for rec_id in ids: + for location in location_obj.browse(cr, uid, loc_ids, context=context): + c = context.copy() + c.update({ + 'location': location.id, + 'compute_child': False + }) + + lot = self.browse(cr, uid, rec_id, context=c) + qty = lot.stock_available + diff = lot.standard_price - new_price + if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!")) + if qty: + company_id = location.company_id and location.company_id.id or False + if not company_id: raise osv.except_osv(_('Error!'), _('Please specify company in Location.')) + # + # Accounting Entries + # + product = lot.product_id + if not journal_id: + journal_id = product.categ_id.property_stock_journal and product.categ_id.property_stock_journal.id or False + if not journal_id: + raise osv.except_osv(_('Error!'), + _('Please define journal '\ + 'on the product category: "%s" (id: %d).') % \ + (product.categ_id.name, + product.categ_id.id,)) + move_id = move_obj.create(cr, uid, { + 'journal_id': journal_id, + 'company_id': company_id + }) + + move_ids.append(move_id) + + + if diff > 0: + if not stock_input_acc: + stock_input_acc = product.\ + property_stock_account_input.id + if not stock_input_acc: + stock_input_acc = product.categ_id.\ + property_stock_account_input_categ.id + if not stock_input_acc: + raise osv.except_osv(_('Error!'), + _('Please define stock input account ' \ + 'for this product: "%s" (id: %d).') % \ + (product.name, + product.id,)) + amount_diff = qty * diff + move_line_obj.create(cr, uid, { + 'name': product.name, + 'account_id': stock_input_acc, + 'debit': amount_diff, + 'move_id': move_id, + }) + move_line_obj.create(cr, uid, { + 'name': product.categ_id.name, + 'account_id': account_valuation_id, + 'credit': amount_diff, + 'move_id': move_id + }) + elif diff < 0: + if not stock_output_acc: + stock_output_acc = product.\ + property_stock_account_output.id + if not stock_output_acc: + stock_output_acc = product.categ_id.\ + property_stock_account_output_categ.id + if not stock_output_acc: + raise osv.except_osv(_('Error!'), + _('Please define stock output account ' \ + 'for this product: "%s" (id: %d).') % \ + (product.name, + product.id,)) + amount_diff = qty * -diff + move_line_obj.create(cr, uid, { + 'name': product.name, + 'account_id': stock_output_acc, + 'credit': amount_diff, + 'move_id': move_id + }) + move_line_obj.create(cr, uid, { + 'name': product.categ_id.name, + 'account_id': account_valuation_id, + 'debit': amount_diff, + 'move_id': move_id + }) + + self.write(cr, uid, rec_id, {'standard_price': new_price}) + + return move_ids class stock_move(orm.Model): _inherit = "stock.move" @@ -55,7 +168,7 @@ class stock_move(orm.Model): res = super(stock_move,self)._get_reference_accounting_values_for_valuation( cr, uid, move, context=context) if not move.product_id.cost_method == 'average' or not move.price_unit: - if move.prodlot_id: + if move.product_id.lot_valuation and move.prodlot_id: if context is None: context = {} currency_ctx = dict(context, currency_id = move.company_id.currency_id.id) From 0950ccfd40c75f642bc8a8f5b9f3866df30221c8 Mon Sep 17 00:00:00 2001 From: Lorenzo Battistini Date: Fri, 10 May 2013 12:11:59 +0200 Subject: [PATCH 04/35] [add] wizard --- stock_lot_valuation/stock_view.xml | 18 ++++ .../wizard/stock_change_standard_price.py | 96 +++++++++++++++++++ .../stock_change_standard_price_view.xml | 34 +++++++ 3 files changed, 148 insertions(+) create mode 100644 stock_lot_valuation/stock_view.xml create mode 100644 stock_lot_valuation/wizard/stock_change_standard_price.py create mode 100644 stock_lot_valuation/wizard/stock_change_standard_price_view.xml diff --git a/stock_lot_valuation/stock_view.xml b/stock_lot_valuation/stock_view.xml new file mode 100644 index 000000000..3f07e974c --- /dev/null +++ b/stock_lot_valuation/stock_view.xml @@ -0,0 +1,18 @@ + + + + + view_production_lot_form + stock.production.lot + + + + +