From d0dbdf80824592548592ef6f051b457e4dd8363d Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Tue, 8 Apr 2014 17:09:35 +0530 Subject: [PATCH 01/44] [ADD] stock_landed_costs : Added the stock_landed_costs module. bzr revid: mdi@tinyerp.com-20140408113935-67fykki3056w8hr8 --- addons/stock_landed_costs/__init__.py | 25 ++++ addons/stock_landed_costs/__openerp__.py | 50 ++++++++ addons/stock_landed_costs/product.py | 45 +++++++ addons/stock_landed_costs/product_view.xml | 26 ++++ .../stock_landed_costs/stock_landed_costs.py | 112 ++++++++++++++++++ .../stock_landed_costs_view.xml | 110 +++++++++++++++++ 6 files changed, 368 insertions(+) create mode 100644 addons/stock_landed_costs/__init__.py create mode 100644 addons/stock_landed_costs/__openerp__.py create mode 100644 addons/stock_landed_costs/product.py create mode 100644 addons/stock_landed_costs/product_view.xml create mode 100644 addons/stock_landed_costs/stock_landed_costs.py create mode 100644 addons/stock_landed_costs/stock_landed_costs_view.xml diff --git a/addons/stock_landed_costs/__init__.py b/addons/stock_landed_costs/__init__.py new file mode 100644 index 00000000000..8d9926454f9 --- /dev/null +++ b/addons/stock_landed_costs/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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 +import stock_landed_costs + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_landed_costs/__openerp__.py b/addons/stock_landed_costs/__openerp__.py new file mode 100644 index 00000000000..b8d11ae77cd --- /dev/null +++ b/addons/stock_landed_costs/__openerp__.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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': 'WMS Landed Costs', + 'version': '1.1', + 'author': 'OpenERP SA', + 'summary': 'Landed Costs', + 'description': """ +#TODO +====================== +#TODO + """, + 'website': 'http://www.openerp.com', + 'images': [], + 'depends': ['stock_account'], + 'category': 'Hidden', + 'sequence': 16, + 'demo': [ + ], + 'data': [ + 'product_view.xml', + 'stock_landed_costs_view.xml', + ], + 'test': [ + ], + 'installable': True, + 'application': True, + 'auto_install': True, +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_landed_costs/product.py b/addons/stock_landed_costs/product.py new file mode 100644 index 00000000000..93e2ec3547c --- /dev/null +++ b/addons/stock_landed_costs/product.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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, osv + +SPLIT_METHOD = [ + ('equal', 'Equal'), + ('by_quantity', 'By Quantity'), + ('by_current_cost_price', 'By Current Cost Price'), + ('by_weight', 'By Weight'), + ('by_volume', 'By Volume'), +] + +class product_product(osv.osv): + _inherit = "product.product" + + _columns = { + 'landed_cost_ok': fields.boolean('Can constitute a landed cost'), + 'split_method': fields.selection(SPLIT_METHOD, 'Split Method'), + } + + _defaults = { + 'landed_cost_ok': False, + 'split_method': 'equal', + } + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_landed_costs/product_view.xml b/addons/stock_landed_costs/product_view.xml new file mode 100644 index 00000000000..7e5d2826504 --- /dev/null +++ b/addons/stock_landed_costs/product_view.xml @@ -0,0 +1,26 @@ + + + + + + product.product.landed.cost.form + product.product + + +
+ +
+ + + + + + + + +
+
+ +
+
diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py new file mode 100644 index 00000000000..981ce6191c7 --- /dev/null +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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 time + +from openerp.osv import fields, osv +import openerp.addons.decimal_precision as dp +import product + +class stock_landed_cost(osv.osv): + _name = 'stock.landed.cost' + _description = 'Stock Landed Cost' + + def _total_amount(self, cr, uid, ids, name, args, context=None): + result = {} + for cost in self.browse(cr, uid, ids, context=context): + total = 0.0 + for line in cost.cost_lines: + total += line.price_subtotal + result[cost.id] = total + return result + + def _get_cost_line(self, cr, uid, ids, context=None): + result = {} + for line in self.pool.get('stock.landed.cost.lines').browse(cr, uid, ids, context=context): + result[line.cost_id.id] = True + return result.keys() + + _columns = { + 'name': fields.char('Name', size=256, required=True), + 'date': fields.datetime('Date', required=True), + 'picking_ids': fields.many2many('stock.picking', string='Pickings'), + 'cost_lines': fields.one2many('stock.landed.cost.lines', 'cost_id', 'Cost Lines'), + 'description': fields.text('Item Description'), + 'amount_total': fields.function(_total_amount, type='float', string='Total', digits_compute=dp.get_precision('Account'), + store={ + 'stock.landed.cost': (lambda self, cr, uid, ids, c={}: ids, ['cost_lines'], 20), + 'stock.landed.cost.lines': (_get_cost_line, ['price_unit', 'quantity', 'cost_id'], 20), + } + ), + 'state':fields.selection([('draft', 'Draft'), ('open', 'Open'), ('cancel', 'Cancelled')], 'State', readonly=True), + } + + _defaults = { + 'state': 'draft', + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + + def button_validate(self, cr ,uid, ids, context=None): + return True + + def button_cancel(self, cr ,uid, ids, context=None): + return True + +class stock_landed_cost_lines(osv.osv): + _name = 'stock.landed.cost.lines' + _description = 'Stock Landed Cost Lines' + + def _amount_subtotal(self, cr, uid, ids, name, args, context=None): + result = {} + for line in self.browse(cr, uid, ids, context=context): + result[line.id] = (line.quantity * line.price_unit) + return result + + def onchange_product_id(self, cr, uid, ids, product_id=False, quantity=0.0, uom_id=False, price_unit=0.0, account_id=False, context=None): + result = {} + if not product_id: + return {'value': {'quantity': 0.0, 'uom_id': False, 'price_unit': 0.0}} + + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + result['name'] = product.name + result['split_method'] = product.split_method + result['price_unit'] = product.standard_price + result['uom_id'] = product.uom_id.id + return {'value': result} + + _columns = { + 'name': fields.char('Description', size=256), + 'cost_id': fields.many2one('stock.landed.cost', 'Landed Cost', required=True, ondelete='cascade'), + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'uom_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True), + 'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True), + 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')), + 'split_method': fields.selection(product.SPLIT_METHOD, string='Split Method'), + 'account_id': fields.many2one('account.account', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]), + 'price_subtotal': fields.function(_amount_subtotal, string='Amount', type='float', digits_compute= dp.get_precision('Account'), store=True), + } + + _defaults = { + 'quantity': 1.0, + 'split_method': 'equal', + } + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml new file mode 100644 index 00000000000..d5e4349593d --- /dev/null +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -0,0 +1,110 @@ + + + + + + + stock.landed.cost.form + stock.landed.cost + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + + + stock.landed.cost.tree + stock.landed.cost + + + + + + + + + + + Landed Costs + stock.landed.cost + form + tree,form + {} + +

+ Click to create a new landed cost. +

+
+
+ + + + + +
+
From ea7fb6d0d20f832e3df7aec17f36e097fb6e37e2 Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Tue, 8 Apr 2014 17:30:33 +0530 Subject: [PATCH 02/44] [ADD] stock_landed_costs : Added the search view. bzr revid: mdi@tinyerp.com-20140408120033-189parnuviod7z9x --- .../stock_landed_costs_view.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index d5e4349593d..7426a4a233f 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -88,6 +88,25 @@ + + + stock.landed.cost.search + stock.landed.cost + + + + + + + + + + + + + + + Landed Costs From f146fe4d6a91d32049da2288d7c59dea1e33a037 Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Wed, 9 Apr 2014 10:11:25 +0530 Subject: [PATCH 03/44] [ADD] stock : Added the option to install module from warehouse configuration wizard. bzr revid: mdi@tinyerp.com-20140409044125-mfyqq66f0qvpoui3 --- addons/stock/res_config.py | 2 ++ addons/stock/res_config_view.xml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/addons/stock/res_config.py b/addons/stock/res_config.py index 162de1ff5b7..a1156fdf264 100644 --- a/addons/stock/res_config.py +++ b/addons/stock/res_config.py @@ -78,6 +78,8 @@ This installs the module product_expiry."""), help="""This way you can receive products attributed to a certain owner. """), 'module_stock_account': fields.boolean("Generate accounting entries per stock movement", help="""Allows to configure inventory valuations on products and product categories."""), + 'module_stock_landed_costs': fields.boolean("Allows to calculate landed costs on products", + help="""Allows to calculate landed costs on products."""), 'group_stock_multiple_locations': fields.boolean("Manage multiple locations and warehouses", implied_group='stock.group_locations', help="""This allows to configure and use multiple stock locations and warehouses, diff --git a/addons/stock/res_config_view.xml b/addons/stock/res_config_view.xml index b783caff41e..89b645a2a8b 100644 --- a/addons/stock/res_config_view.xml +++ b/addons/stock/res_config_view.xml @@ -56,6 +56,10 @@ From 7e700e45546690125c718046263b421fbcc3e829 Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Thu, 24 Apr 2014 15:54:31 +0530 Subject: [PATCH 34/44] [FIX] stock_landed_costs : Fixed the cost price of related quant. bzr revid: mdi@tinyerp.com-20140424102431-llaqwn80mfp1j3h6 --- addons/stock_landed_costs/stock_landed_costs.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index c49b5555e73..d60d61e7d37 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -153,16 +153,20 @@ class stock_landed_cost(osv.osv): def button_validate(self, cr ,uid, ids, context=None): quant_obj = self.pool.get('stock.quant') for cost in self.browse(cr, uid, ids, context=context): + quant_dict = {} for line in cost.valuation_adjustment_lines: per_unit = line.final_cost / line.quantity diff = per_unit - line.former_cost_per_unit quants = [quant for quant in line.move_id.quant_ids if line.move_id] for quant in quants: - if quant.cost < 0: - new_cost = quant.cost - diff + if quant.id not in quant_dict: + quant_dict[quant.id] = quant.cost + diff else: - new_cost = quant.cost + diff - quant_obj.write(cr, uid, quant.id, {'cost': new_cost}, context=context) + quant_dict[quant.id] += diff + + for key, value in quant_dict.items(): + quant_obj.write(cr, uid, quant.id, {'cost': value}, context=context) + self._create_accounting_entries(cr, uid, cost, cost.valuation_adjustment_lines, context=context) self.write(cr, uid, cost.id, {'state': 'open'}, context=context) return True From a4eef5f17eccb42049d8a853c13e020c3a0116e0 Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Thu, 24 Apr 2014 16:10:22 +0530 Subject: [PATCH 35/44] [IMP] stock_landed_costs : Total field should be above the compute button. bzr revid: mdi@tinyerp.com-20140424104022-h88ujot6kmklbo6e --- addons/stock_landed_costs/stock_landed_costs_view.xml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index cde0b1f5aec..5343351225b 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -57,6 +57,9 @@ + + + @@ -95,13 +98,6 @@ - - - -
-
-
From d0fc2200910b823d1a74f78dc1a291ff2d78966e Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Thu, 24 Apr 2014 17:10:17 +0530 Subject: [PATCH 36/44] [ADD] stock_landed_costs : Added the access rights. bzr revid: mdi@tinyerp.com-20140424114017-faura7ny873msnlc --- addons/stock_landed_costs/__openerp__.py | 1 + addons/stock_landed_costs/security/ir.model.access.csv | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 addons/stock_landed_costs/security/ir.model.access.csv diff --git a/addons/stock_landed_costs/__openerp__.py b/addons/stock_landed_costs/__openerp__.py index 6f3863be8b6..bcc1ef6009b 100644 --- a/addons/stock_landed_costs/__openerp__.py +++ b/addons/stock_landed_costs/__openerp__.py @@ -37,6 +37,7 @@ 'demo': [ ], 'data': [ + 'security/ir.model.access.csv', 'product_view.xml', 'stock_landed_costs_view.xml', 'stock_landed_costs_data.xml', diff --git a/addons/stock_landed_costs/security/ir.model.access.csv b/addons/stock_landed_costs/security/ir.model.access.csv new file mode 100644 index 00000000000..ee279151ef5 --- /dev/null +++ b/addons/stock_landed_costs/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +"access_stock_landed_cost","stock.landed.cost","model_stock_landed_cost","stock.group_stock_manager",1,1,1,1 +"access_stock_landed_cost_lines","stock.landed.cost.lines","model_stock_landed_cost_lines","stock.group_stock_manager",1,1,1,1 +"access_stock_valuation_adjustment_lines","stock.valuation.adjustment.lines","model_stock_valuation_adjustment_lines","stock.group_stock_manager",1,1,1,1 From 5f4876e7be5dfc4f5c748b9d15abc8658819fcbb Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Thu, 24 Apr 2014 17:46:56 +0530 Subject: [PATCH 37/44] [FIX] stock_landed_costs : You cannot validate a landed cost which has no valuation line. bzr revid: mdi@tinyerp.com-20140424121656-8wb2mefhh7ons92d --- addons/stock_landed_costs/stock_landed_costs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index d60d61e7d37..5a92a1ec31a 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -153,6 +153,9 @@ class stock_landed_cost(osv.osv): def button_validate(self, cr ,uid, ids, context=None): quant_obj = self.pool.get('stock.quant') for cost in self.browse(cr, uid, ids, context=context): + if not cost.valuation_adjustment_lines: + raise osv.except_osv(_('Error!'),_('You cannot validate a landed cost which has no valuation line.')) + quant_dict = {} for line in cost.valuation_adjustment_lines: per_unit = line.final_cost / line.quantity From 301b9a70eb564501f21fbca0a7d84748bbc8b2e4 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 24 Apr 2014 12:37:52 +0200 Subject: [PATCH 38/44] [IMP] stock_landed_cost: functional review of the module. bzr revid: qdp-launchpad@openerp.com-20140424103752-w12z1frkuujotnrb --- .../stock_landed_costs/stock_landed_costs.py | 121 ++++++++++-------- .../stock_landed_costs_view.xml | 12 +- 2 files changed, 73 insertions(+), 60 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 5a92a1ec31a..f7f529a1f0b 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -82,19 +82,21 @@ class stock_landed_cost(osv.osv): return {'value': result} _columns = { - 'name': fields.char('Name', size=256, required=True, states={'open': [('readonly', True)]}, track_visibility='always'), - 'date': fields.date('Date', required=True, states={'open': [('readonly', True)]}, track_visibility='onchange'), - 'picking_ids': fields.many2many('stock.picking', string='Pickings', states={'open': [('readonly', True)]}), - 'cost_lines': fields.one2many('stock.landed.cost.lines', 'cost_id', 'Cost Lines', states={'open': [('readonly', True)]}), - 'valuation_adjustment_lines': fields.one2many('stock.valuation.adjustment.lines', 'cost_id', 'Valuation Adjustments', states={'open': [('readonly', True)]}), - 'description': fields.text('Item Description', states={'open': [('readonly', True)]}), + 'name': fields.char('Name', size=256, required=True, states={'done': [('readonly', True)]}, track_visibility='always'), + 'date': fields.date('Date', required=True, states={'done': [('readonly', True)]}, track_visibility='onchange'), + 'picking_ids': fields.many2many('stock.picking', string='Pickings', states={'done': [('readonly', True)]}), + 'cost_lines': fields.one2many('stock.landed.cost.lines', 'cost_id', 'Cost Lines', states={'done': [('readonly', True)]}), + 'valuation_adjustment_lines': fields.one2many('stock.valuation.adjustment.lines', 'cost_id', 'Valuation Adjustments', states={'done': [('readonly', True)]}), + 'description': fields.text('Item Description', states={'done': [('readonly', True)]}), 'amount_total': fields.function(_total_amount, type='float', string='Total', digits_compute=dp.get_precision('Account'), store={ 'stock.landed.cost': (lambda self, cr, uid, ids, c={}: ids, ['cost_lines'], 20), 'stock.landed.cost.lines': (_get_cost_line, ['price_unit', 'quantity', 'cost_id'], 20), }, track_visibility='always' ), - 'state':fields.selection([('draft', 'Draft'), ('open', 'Open'), ('cancel', 'Cancelled')], 'State', readonly=True, track_visibility='onchange'), + 'state': fields.selection([('draft', 'Draft'), ('done', 'Posted'), ('cancel', 'Cancelled')], 'State', readonly=True, track_visibility='onchange'), + 'account_move_id': fields.many2one('account.move', 'Journal Entry', readonly=True), + 'account_journal_id': fields.many2one('account.journal', 'Account Journal', required=True), } _defaults = { @@ -102,60 +104,66 @@ class stock_landed_cost(osv.osv): 'date': lambda *a: time.strftime('%Y-%m-%d'), } - def _create_accounting_entries(self, cr, uid, cost, valuation_lines, context=None): - product_obj = self.pool.get('product.product') - for line in valuation_lines: - credit_account_id = line.product_id.property_stock_account_input and line.product_id.property_stock_account_input.id or False - debit_account_id = line.cost_line_id and line.cost_line_id.product_id and line.cost_line_id.product_id.property_account_expense and \ - line.cost_line_id.product_id.property_account_expense.id or False - if not credit_account_id: - raise osv.except_osv(_('Error!'), _('Please configure Stock Input Account for product: %s.') % (line.product_id.name)) - if not debit_account_id: - raise osv.except_osv(_('Error!'), _('Please configure Stock Expense Account for product: %s.') % (line.cost_line_id and line.cost_line_id.product_id and line.cost_line_id.product_id.name)) - accounts = product_obj.get_product_accounts(cr, uid, line.product_id.id, context=context) - journal_id = accounts['stock_journal'] - self._create_account_move(cr, uid, cost, line, credit_account_id=credit_account_id, debit_account_id=debit_account_id, journal_id=journal_id, context=context) - return True + def copy(self, cr, uid, id, default=None, context=None): + default = {} if default is None else default.copy() + default.update({ + 'account_move_id': False, + }) + return super(stock_landed_cost, self).copy(cr, uid, id, default=default, context=context) - def _prepare_account_move_line(self, cr, uid, cost, line, credit_account_id, debit_account_id, context=None): + def _create_accounting_entries(self, cr, uid, line, move_id, context=None): + product_obj = self.pool.get('product.product') + cost_product = line.cost_line_id and line.cost_line_id.product_id + if not cost_product: + return False + accounts = product_obj.get_product_accounts(cr, uid, line.product_id.id, context=context) + credit_account_id = accounts['stock_account_input'] + debit_account_id = cost_product.property_account_expense and cost_product.property_account_expense.id or cost_product.categ_id.property_account_expense_categ.id + if not credit_account_id: + raise osv.except_osv(_('Error!'), _('Please configure Stock Input Account for product: %s.') % (line.product_id.name)) + if not debit_account_id: + raise osv.except_osv(_('Error!'), _('Please configure Stock Expense Account for product: %s.') % (cost_product.name)) + return self._create_account_move_line(cr, uid, line, move_id, credit_account_id, debit_account_id, context=context) + + def _create_account_move_line(self, cr, uid, line, move_id, credit_account_id, debit_account_id, context=None): """ Generate the account.move.line values to track the landed cost. """ - debit_line_vals = { - 'name': cost.name, + aml_obj = self.pool.get('account.move.line') + aml_obj.create(cr, uid, { + 'name': line.name, + 'move_id': move_id, 'product_id': line.product_id.id, 'quantity': line.quantity, 'debit': line.additional_landed_cost, 'account_id': debit_account_id - } - credit_line_vals = { - 'name': cost.name, + }, context=context) + aml_obj.create(cr, uid, { + 'name': line.name, + 'move_id': move_id, 'product_id': line.product_id.id, 'quantity': line.quantity, 'credit': line.additional_landed_cost, 'account_id': credit_account_id - } - return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)] + }, context=context) + return True - def _create_account_move(self, cr, uid, cost, line, credit_account_id, debit_account_id, journal_id, context=None): - move_lines = self._prepare_account_move_line(cr, uid, cost, line, credit_account_id, debit_account_id, context=context) + def _create_account_move(self, cr, uid, cost, context=None): vals = { - 'journal_id': journal_id, - 'line_id': move_lines, + 'journal_id': cost.account_journal_id.id, 'period_id': self.pool.get('account.period').find(cr, uid, cost.date, context=context)[0], 'date': cost.date, 'ref': cost.name } - move = self.pool.get('account.move').create(cr, uid, vals, context=context) - self.pool.get('stock.valuation.adjustment.lines').write(cr, uid, line.id, {'account_move_id': move}, context=context) - return True + return self.pool.get('account.move').create(cr, uid, vals, context=context) - def button_validate(self, cr ,uid, ids, context=None): + def button_validate(self, cr, uid, ids, context=None): quant_obj = self.pool.get('stock.quant') for cost in self.browse(cr, uid, ids, context=context): if not cost.valuation_adjustment_lines: raise osv.except_osv(_('Error!'),_('You cannot validate a landed cost which has no valuation line.')) + move_id = self._create_account_move(cr, uid, cost, context=context) quant_dict = {} for line in cost.valuation_adjustment_lines: per_unit = line.final_cost / line.quantity @@ -166,12 +174,10 @@ class stock_landed_cost(osv.osv): quant_dict[quant.id] = quant.cost + diff else: quant_dict[quant.id] += diff - - for key, value in quant_dict.items(): - quant_obj.write(cr, uid, quant.id, {'cost': value}, context=context) - - self._create_accounting_entries(cr, uid, cost, cost.valuation_adjustment_lines, context=context) - self.write(cr, uid, cost.id, {'state': 'open'}, context=context) + for key, value in quant_dict.items(): + quant_obj.write(cr, uid, quant.id, {'cost': value}, context=context) + self._create_accounting_entries(cr, uid, line, move_id, context=context) + self.write(cr, uid, cost.id, {'state': 'done', 'account_move_id': move_id}, context=context) return True def compute_landed_cost(self, cr, uid, ids, context=None): @@ -255,9 +261,9 @@ class stock_landed_cost_lines(osv.osv): 'name': fields.char('Description', size=256), 'cost_id': fields.many2one('stock.landed.cost', 'Landed Cost', required=True, ondelete='cascade'), 'product_id': fields.many2one('product.product', 'Product', required=True), - 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')), + 'price_unit': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price')), 'split_method': fields.selection(product.SPLIT_METHOD, string='Split Method', required=True), - 'account_id': fields.many2one('account.account', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]), + 'account_id': fields.many2one('account.account', 'Account', domain=[('type', '<>', 'view'), ('type', '<>', 'closed')]), } class stock_valuation_adjustment_lines(osv.osv): @@ -275,20 +281,27 @@ class stock_valuation_adjustment_lines(osv.osv): result[line.id]['final_cost'] = (line.former_cost + line.additional_landed_cost) return result + def _get_name(self, cr, uid, ids, name, arg, context=None): + res = {} + for line in self.browse(cr, uid, ids, context=context): + res[line.id] = line.product_id.code or line.product_id.name or '' + if line.cost_line_id: + res[line.id] += ' - ' + line.cost_line_id.name + return res + _columns = { - 'name': fields.char('Description', size=256), + 'name': fields.function(_get_name, type='char', string='Description', store=True), 'cost_id': fields.many2one('stock.landed.cost', 'Landed Cost', required=True, ondelete='cascade'), 'cost_line_id': fields.many2one('stock.landed.cost.lines', 'Cost Line', readonly=True), 'move_id': fields.many2one('stock.move', 'Stock Move', readonly=True), - 'account_move_id': fields.many2one('account.move', 'Journal Entry', readonly=True), 'product_id': fields.many2one('product.product', 'Product', required=True), - 'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True), - 'weight': fields.float('Weight', digits_compute= dp.get_precision('Product Unit of Measure')), - 'volume': fields.float('Volume', digits_compute= dp.get_precision('Product Unit of Measure')), - 'former_cost': fields.float('Former Cost', digits_compute= dp.get_precision('Product Price')), - 'former_cost_per_unit': fields.function(_amount_final, multi='cost', string='Former Cost(Per Unit)', type='float', digits_compute= dp.get_precision('Account'), store=True), - 'additional_landed_cost': fields.float('Additional Landed Cost', digits_compute= dp.get_precision('Product Price')), - 'final_cost': fields.function(_amount_final, multi='cost', string='Final Cost', type='float', digits_compute= dp.get_precision('Account'), store=True), + 'quantity': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), + 'weight': fields.float('Weight', digits_compute=dp.get_precision('Product Unit of Measure')), + 'volume': fields.float('Volume', digits_compute=dp.get_precision('Product Unit of Measure')), + 'former_cost': fields.float('Former Cost', digits_compute=dp.get_precision('Product Price')), + 'former_cost_per_unit': fields.function(_amount_final, multi='cost', string='Former Cost(Per Unit)', type='float', digits_compute=dp.get_precision('Account'), store=True), + 'additional_landed_cost': fields.float('Additional Landed Cost', digits_compute=dp.get_precision('Product Price')), + 'final_cost': fields.function(_amount_final, multi='cost', string='Final Cost', type='float', digits_compute=dp.get_precision('Account'), store=True), 'flag': fields.selection([('original', 'Original'), ('duplicate', 'Duplicate')], 'Flag', readonly=True), } diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index 5343351225b..b470754bc7e 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -10,7 +10,7 @@
@@ -22,11 +22,13 @@ - - + + + + @@ -89,11 +91,9 @@ - - @@ -130,7 +130,7 @@ - + From f5a285267cec86cf482f0b55071cc8cd7b2929a6 Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Thu, 24 Apr 2014 18:34:03 +0530 Subject: [PATCH 39/44] [FIX] stock_landed_costs : Automatic computation of landed cost when we save the record. bzr revid: mdi@tinyerp.com-20140424130403-3tqm8okdqwgzkm73 --- addons/stock_landed_costs/stock_landed_costs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index f7f529a1f0b..526f458d824 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -104,6 +104,11 @@ class stock_landed_cost(osv.osv): 'date': lambda *a: time.strftime('%Y-%m-%d'), } + def write(self, cr, uid, ids, vals, context=None): + res = super(stock_landed_cost, self).write(cr, uid, ids, vals, context=context) + self.compute_landed_cost(cr, uid, ids, context=context) + return res + def copy(self, cr, uid, id, default=None, context=None): default = {} if default is None else default.copy() default.update({ From 5183909ade4b42b84c214356a59a6911513419bc Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 24 Apr 2014 15:08:46 +0200 Subject: [PATCH 40/44] [REF] stock_landed_costs usability imp bzr revid: qdp-launchpad@openerp.com-20140424130846-6348yoos4omernli --- addons/stock_landed_costs/stock_landed_costs.py | 7 ++++--- .../stock_landed_costs/stock_landed_costs_view.xml | 14 +++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 526f458d824..f0c7d5d1102 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -166,14 +166,15 @@ class stock_landed_cost(osv.osv): quant_obj = self.pool.get('stock.quant') for cost in self.browse(cr, uid, ids, context=context): if not cost.valuation_adjustment_lines: - raise osv.except_osv(_('Error!'),_('You cannot validate a landed cost which has no valuation line.')) - + raise osv.except_osv(_('Error!'), _('You cannot validate a landed cost which has no valuation line.')) move_id = self._create_account_move(cr, uid, cost, context=context) quant_dict = {} for line in cost.valuation_adjustment_lines: + if not line.move_id: + continue per_unit = line.final_cost / line.quantity diff = per_unit - line.former_cost_per_unit - quants = [quant for quant in line.move_id.quant_ids if line.move_id] + quants = [quant for quant in line.move_id.quant_ids] for quant in quants: if quant.id not in quant_dict: quant_dict[quant.id] = quant.cost + diff diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index b470754bc7e..77cb030dd7c 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -86,13 +86,13 @@ - - - - - - - + + + + + + + From 76a96723a2545da5b823d8c77339e059f4e251dc Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 24 Apr 2014 15:15:10 +0200 Subject: [PATCH 41/44] [FIX] stock_landed_costs: qty and volume computation bzr revid: qdp-launchpad@openerp.com-20140424131510-hx55angkbe6jh4um --- addons/stock_landed_costs/stock_landed_costs.py | 4 ++-- addons/stock_landed_costs/stock_landed_costs_view.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index f0c7d5d1102..6dfb26de4b6 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -71,8 +71,8 @@ class stock_landed_cost(osv.osv): continue total_cost = 0.0 total_qty = 0.0 - weight = move.product_id and move.product_id.weight - volume = move.product_id and move.product_id.volume + weight = move.product_id and move.product_id.weight * move.product_qty + volume = move.product_id and move.product_id.volume * move.product_qty for quant in move.quant_ids: total_cost += quant.cost total_qty += quant.qty diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index 77cb030dd7c..ac85159d016 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -88,8 +88,8 @@ - - + + From f71e545f65089934b45a02c1a75968e8ac7938d6 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 24 Apr 2014 15:20:20 +0200 Subject: [PATCH 42/44] [FIX] stock_landed_costs: do not recompute the landed cost at each write bzr revid: qdp-launchpad@openerp.com-20140424132020-mju7xq3g5c4j4j3r --- addons/stock_landed_costs/stock_landed_costs.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 6dfb26de4b6..9c6bb299dcd 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -33,7 +33,7 @@ class stock_landed_cost(osv.osv): _track = { 'state': { - 'stock_landed_costs.mt_stock_landed_cost_open': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open', + 'stock_landed_costs.mt_stock_landed_cost_open': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', }, } @@ -104,11 +104,6 @@ class stock_landed_cost(osv.osv): 'date': lambda *a: time.strftime('%Y-%m-%d'), } - def write(self, cr, uid, ids, vals, context=None): - res = super(stock_landed_cost, self).write(cr, uid, ids, vals, context=context) - self.compute_landed_cost(cr, uid, ids, context=context) - return res - def copy(self, cr, uid, id, default=None, context=None): default = {} if default is None else default.copy() default.update({ From c0d45ba62fd7f01283a1412c0f711303da106c62 Mon Sep 17 00:00:00 2001 From: DJ Patel Date: Thu, 24 Apr 2014 19:00:30 +0530 Subject: [PATCH 43/44] [FIX] stock_landed_costs : Added a button to cancel the landed cost. bzr revid: mdi@tinyerp.com-20140424133030-bxj3x4au976suj36 --- addons/stock_landed_costs/stock_landed_costs.py | 4 ++++ addons/stock_landed_costs/stock_landed_costs_view.xml | 1 + 2 files changed, 5 insertions(+) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 9c6bb299dcd..afe3a21ee81 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -181,6 +181,10 @@ class stock_landed_cost(osv.osv): self.write(cr, uid, cost.id, {'state': 'done', 'account_move_id': move_id}, context=context) return True + def button_cancel(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'state': 'cancel'}, context=context) + return True + def compute_landed_cost(self, cr, uid, ids, context=None): line_obj = self.pool.get('stock.valuation.adjustment.lines') for cost in self.browse(cr, uid, ids, context=None): diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index ac85159d016..c53a54b0648 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -10,6 +10,7 @@
From 09bc7c923af50539790024f48a6620e963c9e446 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 24 Apr 2014 15:35:18 +0200 Subject: [PATCH 44/44] [REF] stock_landed_costs: refactoring/improvements/fixes made during code review bzr revid: qdp-launchpad@openerp.com-20140424133518-nqi1sxggetaum72r --- .../stock_landed_costs/stock_landed_costs.py | 22 +++++++++---------- .../stock_landed_costs_view.xml | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index afe3a21ee81..4736b9bb92c 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -26,6 +26,7 @@ import openerp.addons.decimal_precision as dp from openerp.tools.translate import _ import product + class stock_landed_cost(osv.osv): _name = 'stock.landed.cost' _description = 'Stock Landed Cost' @@ -47,19 +48,19 @@ class stock_landed_cost(osv.osv): return result def _get_cost_line(self, cr, uid, ids, context=None): - result = {} + cost_to_recompute = [] for line in self.pool.get('stock.landed.cost.lines').browse(cr, uid, ids, context=context): - result[line.cost_id.id] = True - return result.keys() + cost_to_recompute.append(line.cost_id.id) + return cost_to_recompute - def onchange_pickings(self, cr, uid, ids, picking_ids=None): + def onchange_pickings(self, cr, uid, ids, picking_ids=None, context=None): result = {'valuation_adjustment_lines': []} line_obj = self.pool.get('stock.valuation.adjustment.lines') picking_obj = self.pool.get('stock.picking') lines = [] - for cost in self.browse(cr, uid, ids): + for cost in self.browse(cr, uid, ids, context=context): line_ids = [line.id for line in cost.valuation_adjustment_lines] - line_obj.unlink(cr, uid, line_ids) + line_obj.unlink(cr, uid, line_ids, context=context) picking_ids = picking_ids and picking_ids[0][2] or False if not picking_ids: return {'value': result} @@ -70,12 +71,11 @@ class stock_landed_cost(osv.osv): if move.product_id.valuation != 'real_time' or move.product_id.cost_method != 'real': continue total_cost = 0.0 - total_qty = 0.0 + total_qty = move.product_qty weight = move.product_id and move.product_id.weight * move.product_qty volume = move.product_id and move.product_id.volume * move.product_qty for quant in move.quant_ids: total_cost += quant.cost - total_qty += quant.qty vals = dict(product_id=move.product_id.id, move_id=move.id, quantity=move.product_uom_qty, former_cost=total_cost * total_qty, weight=weight, volume=volume, flag='original') lines.append(vals) result['valuation_adjustment_lines'] = lines @@ -101,7 +101,7 @@ class stock_landed_cost(osv.osv): _defaults = { 'state': 'draft', - 'date': lambda *a: time.strftime('%Y-%m-%d'), + 'date': fields.date.context_today, } def copy(self, cr, uid, id, default=None, context=None): @@ -243,9 +243,9 @@ class stock_landed_cost(osv.osv): for key, value in dict.items(): line_obj.write(cr, uid, key, {'additional_landed_cost': value}, context=context) - return True + class stock_landed_cost_lines(osv.osv): _name = 'stock.landed.cost.lines' _description = 'Stock Landed Cost Lines' @@ -259,7 +259,7 @@ class stock_landed_cost_lines(osv.osv): result['name'] = product.name result['split_method'] = product.split_method result['price_unit'] = product.standard_price - result['account_id'] = product.property_account_expense and product.property_account_expense.id or False + result['account_id'] = product.property_account_expense and cost_product.property_account_expense.id or cost_product.categ_id.property_account_expense_categ.id return {'value': result} _columns = { diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index c53a54b0648..d1165f6e6c6 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -24,7 +24,7 @@ + on_change="onchange_pickings(picking_ids, context)"/>