From ae33f69e88e72e355e26c71df3a78e313cb0daa6 Mon Sep 17 00:00:00 2001 From: Martin Quinteros Date: Tue, 26 Nov 2024 07:37:41 -0300 Subject: [PATCH] [IMP] Some changes --- stock_currency_valuation/__manifest__.py | 1 + stock_currency_valuation/models/__init__.py | 1 + .../models/product_template.py | 50 +++--- .../models/stock_landed_cost.py | 37 ++++ .../models/stock_valuation_layer.py | 17 +- .../wizard/{__init__,py => __init__.py} | 0 .../stock_valuation_layer_revaluation.py | 163 ++++++++++++++++-- 7 files changed, 224 insertions(+), 45 deletions(-) create mode 100644 stock_currency_valuation/models/stock_landed_cost.py rename stock_currency_valuation/wizard/{__init__,py => __init__.py} (100%) diff --git a/stock_currency_valuation/__manifest__.py b/stock_currency_valuation/__manifest__.py index 5efadb631..875043aa5 100644 --- a/stock_currency_valuation/__manifest__.py +++ b/stock_currency_valuation/__manifest__.py @@ -10,6 +10,7 @@ ], 'depends': [ 'stock_account', + 'stock_landed_costs', 'product_replenishment_cost', ], 'data': [ diff --git a/stock_currency_valuation/models/__init__.py b/stock_currency_valuation/models/__init__.py index 1226f4470..6a7c0ee1a 100644 --- a/stock_currency_valuation/models/__init__.py +++ b/stock_currency_valuation/models/__init__.py @@ -3,3 +3,4 @@ from . import product_product from . import product_template from . import stock_move +from . import stock_landed_cost diff --git a/stock_currency_valuation/models/product_template.py b/stock_currency_valuation/models/product_template.py index af8661dd1..d07063688 100644 --- a/stock_currency_valuation/models/product_template.py +++ b/stock_currency_valuation/models/product_template.py @@ -19,46 +19,38 @@ class productTemplate(models.Model): ondelete={'average_in_currency': 'set default'} ) - @api.depends() + @api.depends('standard_price_in_currency') def _compute_replenishment_cost(self): use_average_in_currency = self.filtered(lambda x: x.replenishment_cost_type == "average_in_currency") super(productTemplate, self - use_average_in_currency)._compute_replenishment_cost() - if use_average_in_currency: - company_id = self.env.company - # domain = [ - # ('product_tmpl_id', 'in', use_average_in_currency.ids), - # ('company_id', '=', company_id.id), - # ] - # groups = self.env['stock.valuation.layer']._read_group(domain, ['value_in_currency:sum', 'quantity:sum'], - # ['product_tmpl_id']) - # products_avg_cost = {group['product_tmpl_id'][0]: group['value_in_currency'] / group['quantity'] - # if group['quantity'] else 0 for group in groups} - for rec in use_average_in_currency: - product_currency = rec.currency_id - #product_cost = products_avg_cost.get(rec.id) or 0 - price_unit = rec.valuation_currency_id._convert( - from_amount=rec.standard_price_in_currency, - to_currency=product_currency, - company=company_id, - date=fields.date.today(), - ) - rec.update({ - 'replenishment_base_cost_currency_id': rec.valuation_currency_id.id, - 'replenishment_base_cost_on_currency': price_unit, - 'replenishment_cost': price_unit, - 'replenishment_base_cost': rec.standard_price_in_currency, - }) + company_id = self.env.company + for rec in use_average_in_currency: + product_currency = rec.currency_id + replenishment_cost_rule = rec.replenishment_cost_rule_id + replenishment_cost = rec.standard_price_in_currency + if replenishment_cost_rule: + replenishment_cost = replenishment_cost_rule.compute_rule(replenishment_cost, rec) + replenishment_base_cost_on_currency = rec.valuation_currency_id._convert( + from_amount=replenishment_cost, + to_currency=product_currency, + company=company_id, + date=fields.date.today(), + ) + rec.update({ + 'replenishment_base_cost_on_currency': replenishment_base_cost_on_currency, + 'replenishment_cost': replenishment_cost, + }) @api.depends_context('company') @api.depends('product_variant_ids', 'product_variant_ids.standard_price') def _compute_standard_price_in_currency(self): - # Depends on force_company context because standard_price_in_currency is company_dependent - # on the product_product + # Por ahora hacemos esto porque replishment cost no es compatible al 100% con variantes + # obtenemos el precio del primer producto unique_variants = self.filtered(lambda template: len(template.product_variant_ids) == 1) for template in unique_variants: template.standard_price_in_currency = template.product_variant_ids.standard_price_in_currency for template in (self - unique_variants): - template.standard_price_in_currency = 0.0 + template.standard_price_in_currency = template.product_variant_ids[:1].standard_price_in_currency def _set_standard_price_in_currency(self): for template in self: diff --git a/stock_currency_valuation/models/stock_landed_cost.py b/stock_currency_valuation/models/stock_landed_cost.py new file mode 100644 index 000000000..40c3da131 --- /dev/null +++ b/stock_currency_valuation/models/stock_landed_cost.py @@ -0,0 +1,37 @@ +from odoo import models, api, _ +from odoo.tools.safe_eval import safe_eval +from odoo.exceptions import ValidationError +from odoo.tools import float_compare + + +class StockLandedCost(models.Model): + + _inherit = 'stock.landed.cost' + + def button_validate(self): + super().button_validate() + + return True +class AdjustmentLines(models.Model): + + _inherit = 'stock.valuation.adjustment.lines' + + def _create_accounting_entries(self, move, qty_out): + AccountMoveLine = super()._create_accounting_entries(move, qty_out) + if self.product_id.categ_id.valuation_currency_id: + amount = AccountMoveLine[0][2]['debit'] or AccountMoveLine[0][2]['credit'] * -1 + value_in_currency = self.cost_id.currency_id._convert( + from_amount=amount, + to_currency=self.product_id.categ_id.valuation_currency_id, + company=self.cost_id.company_id, + date=self.create_date, + ) + AccountMoveLine[0][2].update(({ + 'currency_id': self.product_id.categ_id.valuation_currency_id.id, + 'amount_currency': value_in_currency + })) + AccountMoveLine[1][2].update(({ + 'currency_id': self.product_id.categ_id.valuation_currency_id.id, + 'amount_currency': value_in_currency * -1 + })) + return AccountMoveLine diff --git a/stock_currency_valuation/models/stock_valuation_layer.py b/stock_currency_valuation/models/stock_valuation_layer.py index b575e0ad5..fdff71929 100644 --- a/stock_currency_valuation/models/stock_valuation_layer.py +++ b/stock_currency_valuation/models/stock_valuation_layer.py @@ -2,6 +2,7 @@ from odoo.tools.safe_eval import safe_eval from odoo.exceptions import ValidationError from odoo.tools import float_compare +from odoo.tools.float_utils import float_is_zero class StockValuationLayer(models.Model): @@ -17,8 +18,7 @@ class StockValuationLayer(models.Model): @api.depends('categ_id', 'value', 'bypass_currency_valuation', 'stock_valuation_layer_id') def _compute_other_currency_values(self): for rec in self: - ##rec.stock_valuation_layer_id - + # rec.stock_valuation_layer_id if not rec.bypass_currency_valuation and rec.valuation_currency_id: rec.value_in_currency = rec.currency_id._convert( from_amount=rec.value, @@ -33,3 +33,16 @@ def _compute_other_currency_values(self): else: rec.value_in_currency = False rec.unit_cost_in_currency = False + + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + res._update_currency_standard_price() + return res + + def _update_currency_standard_price(self): + for layer_id in self.filtered(lambda x: x.stock_landed_cost_id and x.product_id.cost_method == 'average' and x.value_in_currency): + # batch standard price computation avoid recompute quantity_svl at each iteration + product = layer_id.product_id.with_company(layer_id.company_id) + if not float_is_zero(product.quantity_svl, precision_rounding=product.uom_id.rounding): + product.sudo().with_context(disable_auto_svl=True).standard_price_in_currency += layer_id.value_in_currency / product.quantity_svl diff --git a/stock_currency_valuation/wizard/__init__,py b/stock_currency_valuation/wizard/__init__.py similarity index 100% rename from stock_currency_valuation/wizard/__init__,py rename to stock_currency_valuation/wizard/__init__.py diff --git a/stock_currency_valuation/wizard/stock_valuation_layer_revaluation.py b/stock_currency_valuation/wizard/stock_valuation_layer_revaluation.py index 57dc7e598..4cb52b995 100644 --- a/stock_currency_valuation/wizard/stock_valuation_layer_revaluation.py +++ b/stock_currency_valuation/wizard/stock_valuation_layer_revaluation.py @@ -1,22 +1,157 @@ -from odoo import models +from odoo import models, fields, _ +from odoo.exceptions import UserError +from odoo.tools import float_compare, float_is_zero class StockValuationLayerRevaluation(models.TransientModel): _inherit = 'stock.valuation.layer.revaluation' + # def action_validate_revaluation(self): + # res = super().action_validate_revaluation() + # # Update the stardard price in currency in case of AVCO + # product_id = self.product_id.with_company(self.company_id) + # if product_id.categ_id.property_cost_method in ('average', 'fifo') and product_id.categ_id.valuation_currency_id: + # # Para actualizar el costo en currency vuelvo a calcular el valor en moneda + # # Si bien hago dos veces el calculo (en el layer y aqui) esto es mas + # # sencillo que obtener el ultimo layer y agregar el valor. + # value_in_currency = self.currency_id._convert( + # from_amount=self.added_value, + # to_currency=product_id.categ_id.valuation_currency_id, + # company=self.company_id, + # date=self.create_date, + # ) + + # product_id.with_context(disable_auto_svl=True).standard_price_in_currency += value_in_currency / self.current_quantity_svl + # return res + + def action_validate_revaluation(self): - res = super().action_validate_revaluation() - # Update the stardard price in currency in case of AVCO + """ Revaluate the stock for `self.product_id` in `self.company_id`. + + - Change the stardard price with the new valuation by product unit. + - Create a manual stock valuation layer with the `added_value` of `self`. + - Distribute the `added_value` on the remaining_value of layers still in stock (with a remaining quantity) + - If the Inventory Valuation of the product category is automated, create + related account move. + """ + self.ensure_one() + if self.currency_id.is_zero(self.added_value): + raise UserError(_("The added value doesn't have any impact on the stock valuation")) + product_id = self.product_id.with_company(self.company_id) - if product_id.categ_id.property_cost_method in ('average', 'fifo') and product_id.categ_id.valuation_currency_id: - # Para actualizar el costo en currency vuelvo a calcular el valor en moneda - # Si bien hago dos veces el calculo (en el layer y aqui) esto es mas - # sencillo que obtener el ultimo layer y agregar el valor. - value_in_currency = self.currency_id._convert( - from_amount=self.added_value, - to_currency=product_id.categ_id.valuation_currency_id, - company=self.company_id, - date=self.create_date, + + remaining_svls = self.env['stock.valuation.layer'].search([ + ('product_id', '=', product_id.id), + ('remaining_qty', '>', 0), + ('company_id', '=', self.company_id.id), + ]) + + # Create a manual stock valuation layer + if self.reason: + description = _("Manual Stock Valuation: %s.", self.reason) + else: + description = _("Manual Stock Valuation: No Reason Given.") + if product_id.categ_id.property_cost_method == 'average': + description += _( + " Product cost updated from %(previous)s to %(new_cost)s.", + previous=product_id.standard_price, + new_cost=product_id.standard_price + self.added_value / self.current_quantity_svl ) - product_id.with_context(disable_auto_svl=True).standard_price_in_currency += value_in_currency / self.current_quantity_svl - return res + revaluation_svl_vals = { + 'company_id': self.company_id.id, + 'product_id': product_id.id, + 'description': description, + 'value': self.added_value, + 'quantity': 0, + } + + remaining_qty = sum(remaining_svls.mapped('remaining_qty')) + remaining_value = self.added_value + remaining_value_unit_cost = self.currency_id.round(remaining_value / remaining_qty) + for svl in remaining_svls: + if float_is_zero(svl.remaining_qty - remaining_qty, precision_rounding=self.product_id.uom_id.rounding): + taken_remaining_value = remaining_value + else: + taken_remaining_value = remaining_value_unit_cost * svl.remaining_qty + if float_compare(svl.remaining_value + taken_remaining_value, 0, precision_rounding=self.product_id.uom_id.rounding) < 0: + raise UserError(_('The value of a stock valuation layer cannot be negative. Landed cost could be use to correct a specific transfer.')) + + svl.remaining_value += taken_remaining_value + remaining_value -= taken_remaining_value + remaining_qty -= svl.remaining_qty + + previous_value_svl = self.current_value_svl + revaluation_svl = self.env['stock.valuation.layer'].create(revaluation_svl_vals) + + # Update the stardard price in case of AVCO + if product_id.categ_id.property_cost_method in ('average', 'fifo'): + product_id.with_context(disable_auto_svl=True).standard_price += self.added_value / self.current_quantity_svl + if product_id.categ_id.valuation_currency_id: + # Para actualizar el costo en currency vuelvo a calcular el valor en moneda + # Si bien hago dos veces el calculo (en el layer y aqui) esto es mas + # sencillo que obtener el ultimo layer y agregar el valor. + value_in_currency = self.currency_id._convert( + from_amount=self.added_value, + to_currency=product_id.categ_id.valuation_currency_id, + company=self.company_id, + date=self.create_date, + ) + product_id.with_context(disable_auto_svl=True).standard_price_in_currency += value_in_currency / self.current_quantity_svl + + # If the Inventory Valuation of the product category is automated, create related account move. + if self.property_valuation != 'real_time': + return True + + accounts = product_id.product_tmpl_id.get_product_accounts() + + if self.added_value < 0: + debit_account_id = self.account_id.id + credit_account_id = accounts.get('stock_valuation') and accounts['stock_valuation'].id + else: + debit_account_id = accounts.get('stock_valuation') and accounts['stock_valuation'].id + credit_account_id = self.account_id.id + + move_vals = { + 'journal_id': self.account_journal_id.id or accounts['stock_journal'].id, + 'company_id': self.company_id.id, + 'ref': _("Revaluation of %s", product_id.display_name), + 'stock_valuation_layer_ids': [(6, None, [revaluation_svl.id])], + 'date': self.date or fields.Date.today(), + 'move_type': 'entry', + 'line_ids': [(0, 0, { + 'name': _('%(user)s changed stock valuation from %(previous)s to %(new_value)s - %(product)s', + user=self.env.user.name, + previous=previous_value_svl, + new_value=previous_value_svl + self.added_value, + product=product_id.display_name, + ), + 'account_id': debit_account_id, + 'debit': abs(self.added_value), + 'credit': 0, + 'product_id': product_id.id, + }), (0, 0, { + 'name': _('%(user)s changed stock valuation from %(previous)s to %(new_value)s - %(product)s', + user=self.env.user.name, + previous=previous_value_svl, + new_value=previous_value_svl + self.added_value, + product=product_id.display_name, + ), + 'account_id': credit_account_id, + 'debit': 0, + 'credit': abs(self.added_value), + 'product_id': product_id.id, + })], + } + if product_id.categ_id.valuation_currency_id: + move_vals['line_ids'][0][2].update(({ + 'currency_id': product_id.categ_id.valuation_currency_id.id, + 'amount_currency': abs(value_in_currency) + })) + move_vals['line_ids'][1][2].update(({ + 'currency_id': product_id.categ_id.valuation_currency_id.id, + 'amount_currency': abs(value_in_currency) * -1 + })) + account_move = self.env['account.move'].create(move_vals) + account_move._post() + + return True