diff --git a/stock_inventory_revaluation/README.rst b/stock_inventory_revaluation/README.rst index b87245140..2f9d31449 100644 --- a/stock_inventory_revaluation/README.rst +++ b/stock_inventory_revaluation/README.rst @@ -12,19 +12,17 @@ inventory revaluation. You can re-valuate inventory values by: * Changing the inventory valuation of a specific product. The cost price - is changed, and the inventory value is recalculated according to the new - price. In case of real price, you can select on which quants you want to - change the unit cost. + is changed, and the inventory value is recalculated according to the new + price. In case of real price, you can select on which quants you want to + change the unit cost. * Changing the value of the inventory. The quantity of inventory remains unchanged, resulting in a change in the price. - Configuration ============= - * Go to *Inventory / Configuration / Products / Product Categories* and define, for each category, a Valuation Increase Account and a Valuation Decrease Account. These accounts will be used as contra-accounts to the @@ -49,7 +47,7 @@ Usage .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/154/8.0 + :target: https://runbot.odoo-community.org/runbot/153/9.0 Bug Tracker =========== @@ -58,10 +56,7 @@ Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and -welcomed `feedback `_. +welcomed feedback. Credits ======= @@ -75,14 +70,14 @@ Contributors ------------ * Eficent Business and IT Consulting Services S.L. - +* Serpent Consulting Services Pvt. Ltd. Maintainer ---------- .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association - :target: http://odoo-community.org + :target: https://odoo-community.org This module is maintained by the OCA. @@ -90,4 +85,4 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit http://odoo-community.org. \ No newline at end of file +To contribute to this module, please visit https://odoo-community.org. diff --git a/stock_inventory_revaluation/__init__.py b/stock_inventory_revaluation/__init__.py index 8c649e73e..1598264a7 100644 --- a/stock_inventory_revaluation/__init__.py +++ b/stock_inventory_revaluation/__init__.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import models from . import wizards diff --git a/stock_inventory_revaluation/__openerp__.py b/stock_inventory_revaluation/__openerp__.py index 75e67d7f3..d6b0deb24 100644 --- a/stock_inventory_revaluation/__openerp__.py +++ b/stock_inventory_revaluation/__openerp__.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { "name": "Stock Inventory Revaluation", "summary": "Introduces inventory revaluation as single point to change " "the valuation of products.", - "version": "8.0.1.0.0", + "version": "9.0.1.0.0", "author": "Eficent Business and IT Consulting Services S.L., " + "Serpent Consulting Services Pvt. Ltd.," "Odoo Community Association (OCA)", "website": "http://www.eficent.com", "category": "Warehouse", @@ -20,6 +23,7 @@ "security/ir.model.access.csv", "views/stock_inventory_revaluation_view.xml", "views/product_view.xml", + "views/account_move_line_view.xml", "data/stock_inventory_revaluation_data.xml", "wizards/stock_inventory_revaluation_mass_post_view.xml", ], diff --git a/stock_inventory_revaluation/data/stock_inventory_revaluation_data.xml b/stock_inventory_revaluation/data/stock_inventory_revaluation_data.xml index 75ecf2d13..ce5df8e6a 100644 --- a/stock_inventory_revaluation/data/stock_inventory_revaluation_data.xml +++ b/stock_inventory_revaluation/data/stock_inventory_revaluation_data.xml @@ -2,12 +2,6 @@ - - Stock Inventory Revaluation - stock.inventory.revaluation - - Stock Inventory Revaluation stock.inventory.revaluation diff --git a/stock_inventory_revaluation/migrations/9.0.1.0.0/openupgrade_analysis.txt b/stock_inventory_revaluation/migrations/9.0.1.0.0/openupgrade_analysis.txt new file mode 100644 index 000000000..0ad7f68eb --- /dev/null +++ b/stock_inventory_revaluation/migrations/9.0.1.0.0/openupgrade_analysis.txt @@ -0,0 +1,4 @@ +---Fields in module 'stock_inventory_revaluation'--- +stock_inventory_revaluation / stock.inventory.revaluation / product_id (many2one) : NEW relation: product.product, required: required +---XML records in module 'stock_inventory_revaluation'--- +DEL ir.sequence.type: stock_inventory_revaluation.sequence_stock_inventory_revaluation_type diff --git a/stock_inventory_revaluation/migrations/9.0.1.0.0/openupgrade_analysis_work.txt b/stock_inventory_revaluation/migrations/9.0.1.0.0/openupgrade_analysis_work.txt new file mode 100644 index 000000000..1a4e4ee1b --- /dev/null +++ b/stock_inventory_revaluation/migrations/9.0.1.0.0/openupgrade_analysis_work.txt @@ -0,0 +1,10 @@ +---Fields in module 'stock_inventory_revaluation'--- + +stock_inventory_revaluation / stock.inventory.revaluation / product_id (many2one) : NEW relation: product.product, required: required +# TODO : Create as many IR records for each product variants of the template +# DONE : in post-migration script + + +---XML records in module 'stock_inventory_revaluation'--- +DEL ir.sequence.type: stock_inventory_revaluation.sequence_stock_inventory_revaluation_type +# NOTHING TO DO: Sequence type deprecated in v9 diff --git a/stock_inventory_revaluation/migrations/9.0.1.0.0/post-migration.py b/stock_inventory_revaluation/migrations/9.0.1.0.0/post-migration.py new file mode 100644 index 000000000..0ad1b685a --- /dev/null +++ b/stock_inventory_revaluation/migrations/9.0.1.0.0/post-migration.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# © 2016 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openerp import api, SUPERUSER_ID +from openupgradelib import openupgrade + + +def migrate_inventory_revaluation(env): + revaluations = env['stock.inventory.revaluation'].search([]) + for revaluation in revaluations: + product_variants = revaluation.product_template_id.\ + with_context(active_test=False).product_variant_ids.mapped('id') + if revaluation.product_template_id and\ + revaluation.product_template_id.product_variant_count > 1: + for product_id in product_variants[1:]: + new_revaluation = revaluation.\ + copy(default={'product_id': product_id, + 'state': 'posted', + 'account_move_id': revaluation. + account_move_id.id}) + quants = env['stock.inventory.revaluation.quant'].\ + search([('product_id', '=', product_id), + ('revaluation_id', '=', revaluation.id)]) + if quants: + quants.write({'revaluation_id': new_revaluation.id}) + if product_variants: + prod_dict = {'product_id': product_variants[0]} + revaluation.write(prod_dict) + + +@openupgrade.migrate() +def migrate(cr, version): + env = api.Environment(cr, SUPERUSER_ID, {}) + migrate_inventory_revaluation(env) diff --git a/stock_inventory_revaluation/migrations/9.0.1.0.0/pre-migration.py b/stock_inventory_revaluation/migrations/9.0.1.0.0/pre-migration.py new file mode 100644 index 000000000..e5e7febb5 --- /dev/null +++ b/stock_inventory_revaluation/migrations/9.0.1.0.0/pre-migration.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# © 2016 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(cr, version): + pass diff --git a/stock_inventory_revaluation/models/__init__.py b/stock_inventory_revaluation/models/__init__.py index ac3d5ed10..bf9112482 100644 --- a/stock_inventory_revaluation/models/__init__.py +++ b/stock_inventory_revaluation/models/__init__.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import product +from . import account_move from . import stock_inventory_revaluation diff --git a/stock_inventory_revaluation/models/account_move.py b/stock_inventory_revaluation/models/account_move.py new file mode 100644 index 000000000..01144d4ba --- /dev/null +++ b/stock_inventory_revaluation/models/account_move.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from openerp import api, exceptions, fields, models, _ + + +class AccountMove(models.Model): + + _inherit = 'account.move' + + stock_inventory_revaluation_id = fields.Many2one( + comodel_name='stock.inventory.revaluation', + string='Stock Inventory Revaluation', + ondelete='restrict', copy=False) + + @api.multi + def unlink(self): + for rec in self: + if rec.stock_inventory_revaluation_id: + raise exceptions.Warning( + _("You cannot remove the journal item that is related " + "to an inventory revaluation")) + return super(AccountMove, self).unlink() + + +class AccountMoveLine(models.Model): + + _inherit = 'account.move.line' + + stock_inventory_revaluation_id = fields.Many2one( + comodel_name='stock.inventory.revaluation', + related='move_id.stock_inventory_revaluation_id', + string='Stock Inventory Revaluation', + store=True, ondelete='restrict', copy=False) + + @api.multi + def unlink(self): + for rec in self: + if rec.stock_inventory_revaluation_id: + raise exceptions.Warning( + _("You cannot remove the journal item that is related " + "to an inventory revaluation")) + return super(AccountMoveLine, self).unlink() diff --git a/stock_inventory_revaluation/models/product.py b/stock_inventory_revaluation/models/product.py index 54f6c236f..503398f01 100644 --- a/stock_inventory_revaluation/models/product.py +++ b/stock_inventory_revaluation/models/product.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + from openerp import api, fields, models @@ -26,30 +29,30 @@ class ProductCategory(models.Model): "is decreased.") -class ProductTemplate(models.Model): +class Product(models.Model): - _inherit = 'product.template' + _inherit = 'product.product' @api.multi def do_change_standard_price(self, new_price): """Override standard method, as it was not suitable.""" reval_model = self.env["stock.inventory.revaluation"] - for product_template in self: + for product in self: increase_account_id = \ - product_template.categ_id.\ + product.categ_id.\ property_inventory_revaluation_increase_account_categ.id \ or False decrease_account_id = \ - product_template.categ_id.\ + product.categ_id.\ property_inventory_revaluation_decrease_account_categ.id \ or False reval = reval_model.create({ 'revaluation_type': 'price_change', - 'product_template_id': product_template.id, + 'product_id': product.id, 'new_cost': new_price, 'increase_account_id': increase_account_id, 'decrease_account_id': decrease_account_id }) - reval.post() + reval.button_post() return True diff --git a/stock_inventory_revaluation/models/stock_inventory_revaluation.py b/stock_inventory_revaluation/models/stock_inventory_revaluation.py index 559244737..398b6f3b8 100644 --- a/stock_inventory_revaluation/models/stock_inventory_revaluation.py +++ b/stock_inventory_revaluation/models/stock_inventory_revaluation.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + from openerp import api, fields, models, _ import openerp.addons.decimal_precision as dp import time -from openerp.exceptions import Warning as UserError +from openerp.exceptions import UserError class StockInventoryRevaluation(models.Model): @@ -19,23 +22,24 @@ class StockInventoryRevaluation(models.Model): return res and res[0] or False @api.multi - def _get_product_template_qty(self): + def _compute_get_product_qty(self): for revaluation in self: revaluation.qty_available = 0 for prod_variant in \ - revaluation.product_template_id.product_variant_ids: + revaluation.product_id: revaluation.qty_available += prod_variant.qty_available @api.multi - def _calc_product_template_value(self): + def _compute_calc_product_value(self): quant_obj = self.env['stock.quant'] for revaluation in self: qty_available = 0 current_value = 0.0 for prod_variant in \ - revaluation.product_template_id.product_variant_ids: + revaluation.product_id: qty_available += prod_variant.qty_available - if revaluation.product_template_id.cost_method == 'real': + if revaluation.product_id.\ + cost_method == 'real': quants = quant_obj.search([('product_id', '=', prod_variant.id), ('location_id.usage', '=', @@ -44,8 +48,7 @@ class StockInventoryRevaluation(models.Model): current_value += quant.cost else: current_value = \ - revaluation.product_template_id.standard_price * \ - qty_available + prod_variant.standard_price * qty_available revaluation.current_value = current_value name = fields.Char('Reference', @@ -108,12 +111,19 @@ class StockInventoryRevaluation(models.Model): readonly=True, states={'draft': [('readonly', False)]}) - product_template_id = fields.Many2one('product.template', 'Product', - required=True, - domain=[('type', '=', 'product')]) + product_id = fields.Many2one('product.product', 'Product', + required=True, + domain=[('type', '=', 'product')], + states={'draft': [('readonly', False)]}, + readonly=True) - cost_method = fields.Selection(string="Cost Method", readonly=True, - related='product_template_id.cost_method') + product_template_id = fields.Many2one('product.template', + 'Product Template', + related='product_id.product_tmpl_id', + store=True) + + cost_method = fields.Char(string="Cost Method", readonly=True, + related='product_id.cost_method') uom_id = fields.Many2one('product.uom', 'UoM', readonly=True, related="product_template_id.uom_id") @@ -128,20 +138,22 @@ class StockInventoryRevaluation(models.Model): help='Displays the current cost of the ' 'product.', digits=dp.get_precision('Product Price'), - compute="_calc_current_cost", + compute="_compute_calc_current_cost", readonly=True) new_cost = fields.Float('New cost', help="Enter the new cost you wish to assign to " "the product. Relevant only when the " "selected revaluation type is Price Change.", - digits=dp.get_precision('Product Price')) + digits=dp.get_precision('Product Price'), + states={'draft': [('readonly', False)]}, + readonly=True) current_value = fields.Float('Current value', help='Displays the current value of the ' 'product.', digits=dp.get_precision('Account'), - compute="_calc_product_template_value", + compute="_compute_calc_product_value", readonly=True) old_value = fields.Float('Old value', @@ -158,7 +170,7 @@ class StockInventoryRevaluation(models.Model): digits=dp.get_precision('Account')) qty_available = fields.Float( - 'Quantity On Hand', compute='_get_product_template_qty', + 'Quantity On Hand', compute='_compute_get_product_qty', digits_compute=dp.get_precision('Product Unit of Measure')) increase_account_id = fields.Many2one( @@ -166,128 +178,192 @@ class StockInventoryRevaluation(models.Model): help="Define the G/L accounts to be used as the balancing account in " "the transaction created by the revaluation. The Increase " "Account is used when the inventory value is increased due to " - "the revaluation.") + "the revaluation.", + states={'draft': [('readonly', False)]}, + readonly=True) decrease_account_id = fields.Many2one( 'account.account', 'Decrease Account', help="Define the G/L accounts to be used as the balancing account in " "the transaction created by the revaluation. The Decrease " - "Account is used when the inventory value is decreased.") + "Account is used when the inventory value is decreased.", + states={'draft': [('readonly', False)]}, + readonly=True) - account_move_id = fields.Many2one('account.move', 'Account move', - readonly=True, copy=False) + account_move_ids = fields.One2many( + comodel_name='account.move', + inverse_name='stock_inventory_revaluation_id', + readonly=True) + + post_date = fields.Datetime( + 'Posting Date', + help="Date of actual processing", + states={'draft': [('readonly', False)]}, + readonly=True) reval_quant_ids = fields.One2many('stock.inventory.revaluation.quant', 'revaluation_id', string='Revaluation line quants') @api.multi - @api.depends("product_template_id", "product_template_id.standard_price") - def _calc_current_cost(self): + @api.depends("product_id", "product_id.standard_price") + def _compute_calc_current_cost(self): for revaluation in self: revaluation.current_cost = \ - revaluation.product_template_id.standard_price + revaluation.product_id.standard_price - @api.multi - @api.constrains('product_template_id', 'company_id') - def _check_is_stockable(self): - for revaluation in self: - if revaluation.product_template_id.type != 'product': - raise UserError(_('Configuration error!\nThe product must be ' - 'stockable.')) - - @api.onchange("product_template_id") - def _onchange_product_template_id(self): - if self.product_template_id: - self.increase_account_id = self.product_template_id.categ_id and \ - self.product_template_id.categ_id.\ + @api.onchange("product_id") + def _onchange_product_product_id(self): + if self.product_id: + self.increase_account_id = self.product_id.categ_id and \ + self.product_id.categ_id.\ property_inventory_revaluation_increase_account_categ - self.decrease_account_id = self.product_template_id.categ_id and \ - self.product_template_id.categ_id.\ + self.decrease_account_id = self.product_id.categ_id and \ + self.product_id.categ_id.\ property_inventory_revaluation_decrease_account_categ @api.model def _prepare_move_data(self, date_move): - period = self.env['account.period'].find(date_move)[0] - return { 'narration': self.remarks, 'date': date_move, 'ref': self.name, 'journal_id': self.journal_id.id, - 'period_id': period.id, + 'stock_inventory_revaluation_id': self.id } @api.model - def _prepare_debit_move_line_data(self, amount, account_id, prod_id): + def _prepare_debit_move_line_data(self, move, amount, account_id, prod_id): return { 'name': self.name, - 'date': self.account_move_id.date, + 'date': move.date, 'product_id': prod_id, 'account_id': account_id, - 'move_id': self.account_move_id.id, + 'move_id': move.id, 'debit': amount } @api.model - def _prepare_credit_move_line_data(self, amount, account_id, prod_id): + def _prepare_credit_move_line_data(self, move, amount, account_id, + prod_id): return { 'name': self.name, - 'date': self.account_move_id.date, + 'date': move.date, 'product_id': prod_id, 'account_id': account_id, - 'move_id': self.account_move_id.id, + 'move_id': move.id, 'credit': amount } @api.model - def _create_accounting_entry(self, amount_diff): + def _create_accounting_entry(self): timenow = time.strftime('%Y-%m-%d') move_data = self._prepare_move_data(timenow) - datas = self.env['product.template'].get_product_accounts( - self.product_template_id.id) - self.account_move_id = self.env['account.move'].create(move_data).id + datas = self.product_template_id.get_product_accounts() move_line_obj = self.env['account.move.line'] - + stock_valuation_account_id = False + if datas.get('stock_valuation'): + stock_valuation_account_id = datas.get('stock_valuation').id + if not stock_valuation_account_id: + raise UserError(_("Please add Stock Valuation Account in " + "Product Category")) if not self.decrease_account_id or not self.increase_account_id: raise UserError(_("Please add an Increase Account and " "a Decrease Account.")) + prec = self.env['decimal.precision'].precision_get('Account') + for prod_variant in self.product_id: + amount_diff = 0.0 + if self.product_id.cost_method == 'real': + for reval_quant in self.reval_quant_ids: + if reval_quant.product_id == prod_variant: + amount_diff += reval_quant.get_total_value() + if amount_diff == 0.0: + return True + else: + if self.revaluation_type == 'price_change': + diff = self.old_cost - self.new_cost + amount_diff = prod_variant.qty_available * diff + else: + proportion = prod_variant.qty_available / \ + self.product_template_id.qty_available + amount_diff = round(self.new_value * proportion, prec) - for prod_variant in self.product_template_id.product_variant_ids: - qty = prod_variant.qty_available - if qty: - if amount_diff > 0: - debit_account_id = self.decrease_account_id.id - credit_account_id = \ - datas['property_stock_valuation_account_id'] - else: - debit_account_id = \ - datas['property_stock_valuation_account_id'] - credit_account_id = self.increase_account_id.id - move_line_data = self._prepare_debit_move_line_data( - abs(amount_diff), debit_account_id, prod_variant.id) - move_line_obj.create(move_line_data) - move_line_data = self._prepare_credit_move_line_data( - abs(amount_diff), credit_account_id, prod_variant.id) - move_line_obj.create(move_line_data) - if self.account_move_id.journal_id.entry_posted: - self.account_move_id.post() + qty = prod_variant.qty_available + if qty: + if amount_diff > 0: + debit_account_id = self.decrease_account_id.id + credit_account_id = \ + datas.get('stock_valuation').id + + else: + debit_account_id = \ + datas.get('stock_valuation').id + credit_account_id = self.increase_account_id.id + move = self.env['account.move'].create(move_data) + move_line_data = self._prepare_debit_move_line_data( + move, abs(amount_diff), debit_account_id, prod_variant.id) + move_line_obj.with_context({'check_move_validity': + False}).create(move_line_data) + move_line_data = self._prepare_credit_move_line_data( + move, abs(amount_diff), credit_account_id, prod_variant.id) + move_line_obj.create(move_line_data) + move.post() @api.multi def post(self): for revaluation in self: + if revaluation.product_id.\ + cost_method == 'real': + for reval_quant in revaluation.reval_quant_ids: + reval_quant.write_new_cost() + else: + if revaluation.product_id.\ + cost_method in ['standard', 'average']: + + if revaluation.revaluation_type == 'inventory_value': + if revaluation.new_value < 0: + raise UserError( + _("The new value for product %s cannot " + "be negative" % + revaluation.product_template_id.name)) + for variant in revaluation.product_id: + if variant.qty_available <= 0.0: + raise UserError( + _("Cannot do an inventory value change if the " + "quantity available for product %s " + "is 0 or negative" % + variant.name)) + if revaluation.revaluation_type == 'price_change': + revaluation.old_cost = revaluation.current_cost + revaluation.product_id.write( + {'standard_price': revaluation.new_cost}) + else: + revaluation.old_cost = revaluation.current_cost + revaluation.old_value = revaluation.current_value + value_diff = revaluation.current_value - \ + revaluation.new_value + new_cost = value_diff / revaluation.qty_available + revaluation.product_id.write( + {'standard_price': new_cost}) + if revaluation.product_id.categ_id.\ + property_valuation == 'real_time': + revaluation._create_accounting_entry() + self.post_date = fields.Datetime.now() + self.state = 'posted' + amount_diff = 0.0 - if revaluation.product_template_id.cost_method == 'real': + diff = 0.0 + if revaluation.product_id.\ + cost_method == 'real': for reval_quant in revaluation.reval_quant_ids: amount_diff += reval_quant.get_total_value() reval_quant.write_new_cost() if amount_diff == 0.0: return True else: - if revaluation.product_template_id.cost_method \ - in ['standard', 'average']: - + if revaluation.product_id.\ + cost_method in ['standard', 'average']: if revaluation.revaluation_type == 'price_change': diff = revaluation.current_cost - revaluation.new_cost amount_diff = revaluation.qty_available * diff @@ -306,22 +382,6 @@ class StockInventoryRevaluation(models.Model): "is 0 or negative" % revaluation.product_template_id.name)) - if revaluation.revaluation_type == 'price_change': - revaluation.old_cost = revaluation.current_cost - revaluation.product_template_id.write( - {'standard_price': revaluation.new_cost}) - else: - revaluation.old_cost = revaluation.current_cost - revaluation.old_value = revaluation.current_value - value_diff = revaluation.current_value - \ - revaluation.new_value - new_cost = value_diff / revaluation.qty_available - revaluation.product_template_id.write( - {'standard_price': new_cost}) - - if revaluation.product_template_id.valuation == 'real_time': - revaluation._create_accounting_entry(amount_diff) - @api.model def create(self, values): sequence_obj = self.env['ir.sequence'] @@ -332,7 +392,6 @@ class StockInventoryRevaluation(models.Model): @api.multi def button_post(self): self.post() - self.write({'state': 'posted'}) return True @api.multi @@ -342,19 +401,16 @@ class StockInventoryRevaluation(models.Model): @api.multi def button_cancel(self): - moves = self.env['account.move'] for revaluation in self: - if revaluation.account_move_id: - moves += revaluation.account_move_id for reval_quant in revaluation.reval_quant_ids: reval_quant.quant_id.write({'cost': reval_quant.old_cost}) - if moves: + if revaluation.account_move_ids: # second, invalidate the move(s) - moves.button_cancel() + revaluation.account_move_ids.button_cancel() # delete the move this revaluation was pointing to # Note that the corresponding move_lines and move_reconciles # will be automatically deleted too - moves.unlink() + revaluation.account_move_ids.unlink() revaluation.state = 'cancel' return True @@ -405,17 +461,20 @@ class StockInventoryRevaluationQuant(models.Model): comodel_name='res.company', string='Company', readonly=True, related="revaluation_id.company_id") + @api.model def get_total_value(self): amount_diff = 0.0 - if self.product_id.product_tmpl_id.cost_method == 'real': + if self.product_id.\ + cost_method == 'real': if self.revaluation_id.revaluation_type != 'price_change': raise UserError(_("You can only post quant cost changes.")) else: - diff = self.current_cost - self.new_cost + diff = self.old_cost - self.new_cost amount_diff = self.qty * diff return amount_diff + @api.model def write_new_cost(self): self.old_cost = self.current_cost - self.quant_id.write({'cost': self.new_cost}) + self.quant_id.sudo().write({'cost': self.new_cost}) return True diff --git a/stock_inventory_revaluation/security/ir.model.access.csv b/stock_inventory_revaluation/security/ir.model.access.csv index 836d2f88d..ba416d40f 100644 --- a/stock_inventory_revaluation/security/ir.model.access.csv +++ b/stock_inventory_revaluation/security/ir.model.access.csv @@ -1,3 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_stock_inventory_revaluation,stock.inventory.revaluation,model_stock_inventory_revaluation,stock_account.group_inventory_valuation,1,1,1,1 -access_stock_inventory_revaluation_quant,stock.inventory.revaluation.quant,model_stock_inventory_revaluation_quant,stock_account.group_inventory_valuation,1,1,1,1 \ No newline at end of file +access_stock_inventory_revaluation_quant,stock.inventory.revaluation.quant,model_stock_inventory_revaluation_quant,stock_account.group_inventory_valuation,1,1,1,1 +access_stock_inventory_revaluation_account_user,stock.inventory.revaluation.account.user,model_stock_inventory_revaluation,account.group_account_user,1,0,0,0 diff --git a/stock_inventory_revaluation/tests/__init__.py b/stock_inventory_revaluation/tests/__init__.py index 32858f1ba..f7ecad1b3 100644 --- a/stock_inventory_revaluation/tests/__init__.py +++ b/stock_inventory_revaluation/tests/__init__.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import test_stock_inventory_revaluation diff --git a/stock_inventory_revaluation/tests/test_stock_inventory_revaluation.py b/stock_inventory_revaluation/tests/test_stock_inventory_revaluation.py index 6ecf286d6..167cdf009 100644 --- a/stock_inventory_revaluation/tests/test_stock_inventory_revaluation.py +++ b/stock_inventory_revaluation/tests/test_stock_inventory_revaluation.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from openerp.tests.common import TransactionCase @@ -16,56 +18,54 @@ class TestStockInventoryRevaluation(TransactionCase): super(TestStockInventoryRevaluation, self).setUp() # Get required Model self.product_model = self.env['product.product'] + self.template_model = self.env['product.template'] self.product_ctg_model = self.env['product.category'] self.reval_model = self.env['stock.inventory.revaluation'] self.account_model = self.env['account.account'] - self.acc_type_model = self.env['account.account.type'] - self.reval_quant_model = self.\ - env['stock.inventory.revaluation.quant'] self.get_quant_model = self.\ env['stock.inventory.revaluation.get.quant'] self.mass_post_model = self.\ env['stock.inventory.revaluation.mass.post'] self.stock_change_model = self.env['stock.change.product.qty'] - self.stock_lot_model = self.env['stock.production.lot'] self.stock_location_model = self.env['stock.location'] + # Get required Model data - self.fixed_account = self.env.ref('account.xfa') - self.purchased_stock = self.env.ref('account.stk') - self.debtors_account = self.env.ref('account.a_recv') - self.cash_account = self.env.ref('account.cash') self.product_uom = self.env.ref('product.product_uom_unit') - self.journal = self.env.ref('account.miscellaneous_journal') self.company = self.env.ref('base.main_company') location = self.stock_location_model.search([('name', '=', 'WH')]) self.location = self.stock_location_model.search([('location_id', '=', location.id)]) + # Account types + expense_type = self.env.ref('account.data_account_type_expenses') + equity_type = self.env.ref('account.data_account_type_equity') + asset_type = self.env.ref('account.data_account_type_fixed_assets')\ + # Create account for Goods Received Not Invoiced name = 'Goods Received Not Invoiced' code = 'grni' - acc_type = 'equity' + acc_type = equity_type self.account_grni = self._create_account(acc_type, name, code, self.company) # Create account for Cost of Goods Sold name = 'Cost of Goods Sold' code = 'cogs' - acc_type = 'expense' + acc_type = expense_type self.account_cogs = self._create_account(acc_type, name, code, self.company) # Create account for Inventory name = 'Inventory' code = 'inventory' - acc_type = 'asset' + acc_type = asset_type self.account_inventory = self._create_account(acc_type, name, code, self.company) # Create account for Inventory Revaluation name = 'Inventory Revaluation' code = 'revaluation' - acc_type = 'expense' + acc_type = expense_type self.account_revaluation = self._create_account(acc_type, name, code, self.company) @@ -75,30 +75,39 @@ class TestStockInventoryRevaluation(TransactionCase): # Create a Product with real cost standard_price = 10.0 list_price = 20.0 - self.product_real = self._create_product('real', standard_price, - list_price) + self.product_real_1 = self._create_product('real', standard_price, + False, list_price) + self.product_real_2 = self._create_product( + False, False, self.product_real_1.product_tmpl_id, list_price) + # Add default quantity - quantity = 20.00 - self._update_product_qty(self.product_real, self.location, quantity) + quantity = 10.00 + self._update_product_qty(self.product_real_1, self.location, quantity) + self._update_product_qty(self.product_real_2, self.location, quantity) # Create a Product with average cost standard_price = 10.0 list_price = 20.0 - self.product_average = self._create_product('average', standard_price, - list_price) + self.product_average_1 = self._create_product('average', + standard_price, + False, + list_price) + self.product_average_2 = self._create_product( + False, False, self.product_average_1.product_tmpl_id, list_price) # Add default quantity - quantity = 20.00 - self._update_product_qty(self.product_average, self.location, quantity) + quantity = 10.00 + self._update_product_qty(self.product_average_1, self.location, + quantity) + self._update_product_qty(self.product_average_2, self.location, + quantity) def _create_account(self, acc_type, name, code, company): """Create an account.""" - type_ids = self.acc_type_model.search([('code', '=', acc_type)]) account = self.account_model.create({ 'name': name, 'code': code, - 'type': 'other', - 'user_type': type_ids.ids and type_ids.ids[0], + 'user_type_id': acc_type.id, 'company_id': company.id }) return account @@ -111,25 +120,31 @@ class TestStockInventoryRevaluation(TransactionCase): self.account_revaluation.id, 'property_inventory_revaluation_decrease_account_categ': self.account_revaluation.id, + 'property_valuation': 'real_time' }) return product_ctg - def _create_product(self, cost_method, standard_price, list_price): - """Create a Product with inventory valuation set to auto.""" - product = self.product_model.create({ - 'name': 'test_product', - 'categ_id': self.product_ctg.id, - 'type': 'product', - 'standard_price': standard_price, - 'list_price': list_price, - 'valuation': 'real_time', - 'cost_method': cost_method, - 'property_stock_account_input': self.account_grni.id, - 'property_stock_account_output': self.account_cogs.id, - }) + def _create_product(self, cost_method, standard_price, template, + list_price): + """Create a Product variant.""" + if not template: + template = self.template_model.create({ + 'name': 'test_product', + 'categ_id': self.product_ctg.id, + 'type': 'product', + 'standard_price': standard_price, + 'valuation': 'real_time', + 'cost_method': cost_method, + 'property_stock_account_input': self.account_grni.id, + 'property_stock_account_output': self.account_cogs.id + }) + return template.product_variant_ids[0] + product = self.product_model.create( + {'product_tmpl_id': template.id, + 'list_price': list_price}) return product - def _create_inventory_revaluation(self, journal, revaluation_type, + def _create_inventory_revaluation(self, revaluation_type, product): """Create a Inventory Revaluation by applying increase and decrease account to it.""" @@ -144,8 +159,7 @@ class TestStockInventoryRevaluation(TransactionCase): 'name': 'test_inventory_revaluation', 'document_date': datetime.today(), 'revaluation_type': revaluation_type, - 'journal_id': journal.id, - 'product_template_id': product.id, + 'product_id': product.id, 'increase_account_id': self.increase_account_id.id, 'decrease_account_id': self.decrease_account_id.id, }) @@ -195,13 +209,12 @@ class TestStockInventoryRevaluation(TransactionCase): """Test that the inventory is revaluated when the inventory price for a product managed under real costing method is changed.""" - # Create an Inventory Revaluation for real cost product revaluation_type = 'price_change' invent_price_change_real = \ self._create_inventory_revaluation( - self.journal, revaluation_type, - self.product_real.product_tmpl_id) + revaluation_type, + self.product_real_1) # Create an Inventory Revaluation Line Quant date_from = date.today() - timedelta(1) @@ -209,11 +222,14 @@ class TestStockInventoryRevaluation(TransactionCase): invent_price_change_real.button_post() - expected_result = (10.00 - 8.00) * 20.00 + expected_result = (10.00 - 8.00) * 10.00 + self.assertEqual(len( + invent_price_change_real.account_move_ids[0].line_ids), 2, + 'Incorrect accounting entry generated') - for move_line in invent_price_change_real.account_move_id.line_id: - if move_line.debit: - self.assertEqual(move_line.debit, expected_result, + for move_line in invent_price_change_real.account_move_ids[0].line_ids: + if move_line.account_id == self.account_inventory: + self.assertEqual(move_line.credit, expected_result, 'Incorrect inventory revaluation for ' 'type Price Change.') @@ -221,9 +237,8 @@ class TestStockInventoryRevaluation(TransactionCase): revaluation_type = 'price_change' # Create an Inventory Revaluation for average cost product invent_price_change_average = self._create_inventory_revaluation( - self.journal, revaluation_type, - self.product_average.product_tmpl_id) - # Post the inventory revaluation + revaluation_type, + self.product_average_1) invent_price_change_average.new_cost = 8.00 return invent_price_change_average @@ -233,21 +248,39 @@ class TestStockInventoryRevaluation(TransactionCase): changed.""" invent_price_change_average = \ self.create_inventory_revaluation_price_change_average() + # Post the inventory revaluation invent_price_change_average.button_post() - expected_result = (10.00 - 8.00) * 20.00 - for move_line in invent_price_change_average.account_move_id.line_id: - if move_line.debit: - self.assertEqual(move_line.debit, expected_result, + expected_result = (10.00 - 8.00) * 10.00 + + self.assertEqual(len( + invent_price_change_average.account_move_ids[0].line_ids), 2, + 'Incorrect accounting entry generated') + + for move_line in \ + invent_price_change_average.account_move_ids[0].line_ids: + if move_line.account_id == self.account_inventory: + self.assertEqual(move_line.credit, expected_result, 'Incorrect inventory revaluation for ' - 'type Price Change.') + 'Standard Product .') + + # Set the standard price and assert the amount changed + context = {'active_model': u'product.product', + 'active_ids': self.product_average_1.ids, + 'active_id': self.product_average_1.id} + + self.product_average_1.with_context(context).\ + do_change_standard_price(5.00) + if self.product_average_1: + self.assertEqual(self.product_average_1.standard_price, 5.0, + 'Incorrect Product Price.') def create_inventory_revaluation_value_change(self): # Create an Inventory Revaluation for value change for average # cost product revaluation_type = 'inventory_value' invent_value_change = self._create_inventory_revaluation( - self.journal, revaluation_type, - self.product_average.product_tmpl_id) + revaluation_type, + self.product_average_1) invent_value_change.new_value = 100.00 return invent_value_change @@ -258,9 +291,13 @@ class TestStockInventoryRevaluation(TransactionCase): # Post the inventory revaluation invent_value_change.button_post() - for move_line in invent_value_change.account_move_id.line_id: - if move_line.debit: - self.assertEqual(move_line.debit, 100.0, + self.assertEqual(len( + invent_value_change.account_move_ids[0].line_ids), 2, + 'Incorrect accounting entry generated') + + for move_line in invent_value_change.account_move_ids[0].line_ids: + if move_line.account_id == self.account_inventory: + self.assertEqual(move_line.credit, 50.0, 'Incorrect inventory revaluation for ' 'type Inventory Debit/Credit.') diff --git a/stock_inventory_revaluation/views/account_move_line_view.xml b/stock_inventory_revaluation/views/account_move_line_view.xml new file mode 100644 index 000000000..d12a2ba47 --- /dev/null +++ b/stock_inventory_revaluation/views/account_move_line_view.xml @@ -0,0 +1,18 @@ + + + + + + account.move.line.form + account.move.line + + + + + + + + + + diff --git a/stock_inventory_revaluation/views/product_view.xml b/stock_inventory_revaluation/views/product_view.xml index 7f85728d7..5254869f3 100644 --- a/stock_inventory_revaluation/views/product_view.xml +++ b/stock_inventory_revaluation/views/product_view.xml @@ -9,8 +9,8 @@ ref="stock_account.view_category_property_form"/> - - + + diff --git a/stock_inventory_revaluation/views/stock_inventory_revaluation_view.xml b/stock_inventory_revaluation/views/stock_inventory_revaluation_view.xml index a4113444c..097f38ea2 100644 --- a/stock_inventory_revaluation/views/stock_inventory_revaluation_view.xml +++ b/stock_inventory_revaluation/views/stock_inventory_revaluation_view.xml @@ -32,6 +32,7 @@ + @@ -45,7 +46,8 @@ - + + @@ -68,7 +70,6 @@ - @@ -93,6 +94,10 @@ + + + @@ -121,7 +126,6 @@ - @@ -170,6 +174,7 @@ + diff --git a/stock_inventory_revaluation/wizards/__init__.py b/stock_inventory_revaluation/wizards/__init__.py index 8a02f8a7e..17f9c6f98 100644 --- a/stock_inventory_revaluation/wizards/__init__.py +++ b/stock_inventory_revaluation/wizards/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# © 2016 Serpent Consulting Services Pvt. Ltd. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import stock_inventory_revaluation_get_quants diff --git a/stock_inventory_revaluation/wizards/stock_inventory_revaluation_get_quants.py b/stock_inventory_revaluation/wizards/stock_inventory_revaluation_get_quants.py index da777baac..75add2e93 100644 --- a/stock_inventory_revaluation/wizards/stock_inventory_revaluation_get_quants.py +++ b/stock_inventory_revaluation/wizards/stock_inventory_revaluation_get_quants.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# © 2016 Serpent Consulting Services Pvt. Ltd. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from openerp import api, fields, models @@ -28,7 +29,7 @@ class StockInventoryRevaluationGetQuants(models.TransientModel): quant_l = [] quant_obj = self.env['stock.quant'] for prod_variant in \ - revaluation.product_template_id.product_variant_ids: + revaluation.product_id: search_domain = self._get_quant_search_criteria(prod_variant) quants = quant_obj.search(search_domain) for quant in quants: diff --git a/stock_inventory_revaluation/wizards/stock_inventory_revaluation_mass_post.py b/stock_inventory_revaluation/wizards/stock_inventory_revaluation_mass_post.py index 9828fc516..11b41e684 100644 --- a/stock_inventory_revaluation/wizards/stock_inventory_revaluation_mass_post.py +++ b/stock_inventory_revaluation/wizards/stock_inventory_revaluation_mass_post.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# © 2015 Eficent Business and IT Consulting Services S.L. -# - Jordi Ballester Alomar +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# © 2016 Serpent Consulting Services Pvt. Ltd. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from openerp import api, models, _, exceptions @@ -10,21 +11,11 @@ class StockInventoryRevaluationMassPost(models.TransientModel): _name = 'stock.inventory.revaluation.mass.post' _description = 'Post multiple inventory revaluations' - @api.model - def default_get(self, fields): - res = super(StockInventoryRevaluationMassPost, self).default_get( - fields) - revaluation_ids = self.env.context['active_ids'] or [] - active_model = self.env.context['active_model'] - - if not revaluation_ids: - return res - assert active_model == 'stock.inventory.revaluation', \ - 'Bad context propagation' - return res - @api.multi def process(self): + active_model = self.env.context['active_model'] + assert active_model == 'stock.inventory.revaluation', \ + 'Bad context propagation' self.ensure_one() revaluation_ids = self.env.context.get('active_ids', []) revaluation_obj = self.env['stock.inventory.revaluation']