diff --git a/stock_currency_valuation/__init__.py b/stock_currency_valuation/__init__.py index 9b4296142..daa2c3ffd 100644 --- a/stock_currency_valuation/__init__.py +++ b/stock_currency_valuation/__init__.py @@ -1,2 +1,2 @@ from . import models -from . import wizard +# from . import wizard diff --git a/stock_currency_valuation/__manifest__.py b/stock_currency_valuation/__manifest__.py index 1e3d96bc3..a38218da9 100644 --- a/stock_currency_valuation/__manifest__.py +++ b/stock_currency_valuation/__manifest__.py @@ -1,6 +1,6 @@ { "name": "Stock currency valuation", - "version": "18.0.1.1.0", + "version": "19.0.1.0.0", "category": "Warehouse Management", "sequence": 14, "summary": "", @@ -9,18 +9,18 @@ "images": [], "depends": [ "stock_account", - "stock_landed_costs", + # "stock_landed_costs", "product_replenishment_cost", ], "data": [ "views/product_category.xml", "views/stock_picking.xml", - "views/stock_landed_cost_views.xml", + # "views/stock_landed_cost_views.xml", "views/product.xml", - "views/stock_valuation_layer.xml", - "wizard/stock_valuation_layer_revaluation_views.xml", + # "views/stock_valuation_layer.xml", + # "wizard/stock_valuation_layer_revaluation_views.xml", ], - "installable": False, + "installable": True, "auto_install": False, "application": False, "assets": {}, diff --git a/stock_currency_valuation/models/__init__.py b/stock_currency_valuation/models/__init__.py index ebf7df320..0b1ee54dd 100644 --- a/stock_currency_valuation/models/__init__.py +++ b/stock_currency_valuation/models/__init__.py @@ -1,7 +1,8 @@ from . import product_category -from . import stock_valuation_layer from . import product_product from . import product_template from . import stock_move -from . import stock_landed_cost from . import stock_picking + +# from . import stock_valuation_layer +# from . import stock_landed_cost diff --git a/stock_currency_valuation/models/product_value.py b/stock_currency_valuation/models/product_value.py new file mode 100644 index 000000000..eea92103a --- /dev/null +++ b/stock_currency_valuation/models/product_value.py @@ -0,0 +1,24 @@ +from odoo import api, fields, models + + +class ProductValue(models.Model): + _inherit_id = "product.value" + + valuation_currency_id = fields.Many2one(compute="_compute_valuation_currency_id", store=True) + value_in_currency = fields.Monetary(string="Value", currency_field="valuation_currency_id") + + @api.depends("company_id", "move_id", "lot_id", "product_id") + def _compute_valuation_currency_id(self): + for product_value in self: + if product_value.move_id: + product_value.valuation_currency_id = product_value.move_id.product_id.with_company( + product_value.company_id + ).valuation_currency_id + elif product_value.lot_id: + product_value.valuation_currency_id = product_value.lot_id.product_id.with_company( + product_value.company_id + ).valuation_currency_id + elif product_value.product_id: + product_value.valuation_currency_id = product_value.product_id.with_company( + product_value.company_id + ).valuation_currency_id diff --git a/stock_currency_valuation/models/stock_move.py b/stock_currency_valuation/models/stock_move.py index 5fe09c929..d82e30be1 100644 --- a/stock_currency_valuation/models/stock_move.py +++ b/stock_currency_valuation/models/stock_move.py @@ -1,134 +1,163 @@ -from collections import defaultdict - from odoo import fields, models -from odoo.tools.float_utils import float_compare, float_is_zero class StockMove(models.Model): _inherit = "stock.move" - def _get_price_unit(self): - # Esto modifica el precio moneda de la compaƱia basandose en el valor la cotizacoin - # del dolar agregado en el picking. - # TODO: pude fallar si los lotes de un mismo producto tiene diferentes costos - self.ensure_one() - price_units = super()._get_price_unit() - for index in price_units.items(): - if ( - self.picking_id.currency_rate - and self.purchase_line_id.order_id.currency_id == self.picking_id.valuation_currency_id - ): - price_units[index[0]] = self.purchase_line_id.price_unit / self.picking_id.currency_rate - return price_units + valuation_currency_id = fields.Many2one( + related="product_id.valuation_currency_id", + ) + value_in_currency = fields.Monetary( + "Value", + currency_field="valuation_currency_id", + help="The current value of the move. It's zero if the move is not valued.", + ) + value_manual_in_currency = fields.Monetary( + "Manual Value", + currency_field="valuation_currency_id", + compute="_compute_value_manual", + inverse="_inverse_value_manual", + ) + standard_price_in_currency = fields.Float( + related="product_id.standard_price_in_currency", string="Standard Price in currency" + ) - def _account_entry_move(self, qty, description, svl_id, cost): - am_vals_list = super()._account_entry_move(qty, description, svl_id, cost) - layer = self.env["stock.valuation.layer"].browse(svl_id) - if layer.valuation_currency_id: - for am_vals in am_vals_list: - for line_id in am_vals["line_ids"]: - sign = -1 if line_id[2]["balance"] < 0 else 1 - line_id[2].update( - { - "currency_id": layer.valuation_currency_id.id, - "amount_currency": abs(layer.value_in_currency) * sign, - } + def _set_value(self, correction_quantity=None): + super()._set_value(correction_quantity=correction_quantity) + for move in self: + if move.with_company(move.company_id).valuation_currency_id and move.value: + if move.picking_id and move.picking_id.currency_rate: + move.value_in_currency = move.value * move.picking_id.currency_rate + elif move.picking_id: + move.value_in_currency = move.with_company(move.company_id).company_id.currency_id._convert( + from_amount=move.value, + to_currency=move.valuation_currency_id, + company=move.company_id, + date=move.date, ) - return am_vals_list - def product_price_update_before_done(self, forced_qty=None): - super().product_price_update_before_done(forced_qty=forced_qty) - # Actualizo tambien el costo en moneda - tmpl_dict = defaultdict(lambda: 0.0) - # adapt standard price on incomming moves if the product cost_method is 'average' - std_price_update = {} - for move in self.filtered( - lambda move: move._is_in() - and move.with_company(move.company_id).product_id.categ_id.valuation_currency_id - and move.with_company(move.company_id).product_id.cost_method == "average" - ): - product_tot_qty_available = ( - move.product_id.sudo().with_company(move.company_id).quantity_svl + tmpl_dict[move.product_id.id] - ) - rounding = move.product_id.uom_id.rounding + # def _get_price_unit(self): + # # Esto modifica el precio moneda de la compaƱia basandose en el valor la cotizacoin + # # del dolar agregado en el picking. + # # TODO: pude fallar si los lotes de un mismo producto tiene diferentes costos + # self.ensure_one() + # price_units = super()._get_price_unit() + # for index in price_units.items(): + # if ( + # self.picking_id.currency_rate + # and self.purchase_line_id.order_id.currency_id == self.picking_id.valuation_currency_id + # ): + # price_units[index[0]] = self.purchase_line_id.price_unit / self.picking_id.currency_rate + # return price_units + + # def _account_entry_move(self, qty, description, svl_id, cost): + # am_vals_list = super()._account_entry_move(qty, description, svl_id, cost) + # layer = self.env["stock.valuation.layer"].browse(svl_id) + # if layer.valuation_currency_id: + # for am_vals in am_vals_list: + # for line_id in am_vals["line_ids"]: + # sign = -1 if line_id[2]["balance"] < 0 else 1 + # line_id[2].update( + # { + # "currency_id": layer.valuation_currency_id.id, + # "amount_currency": abs(layer.value_in_currency) * sign, + # } + # ) + # return am_vals_list + + # def product_price_update_before_done(self, forced_qty=None): + # super().product_price_update_before_done(forced_qty=forced_qty) + # # Actualizo tambien el costo en moneda + # tmpl_dict = defaultdict(lambda: 0.0) + # # adapt standard price on incomming moves if the product cost_method is 'average' + # std_price_update = {} + # for move in self.filtered( + # lambda move: move._is_in() + # and move.with_company(move.company_id).product_id.categ_id.valuation_currency_id + # and move.with_company(move.company_id).product_id.cost_method == "average" + # ): + # product_tot_qty_available = ( + # move.product_id.sudo().with_company(move.company_id).quantity_svl + tmpl_dict[move.product_id.id] + # ) + # rounding = move.product_id.uom_id.rounding - valued_move_lines = move._get_in_move_lines() - qty_done = 0 - for valued_move_line in valued_move_lines: - qty_done += valued_move_line.product_uom_id._compute_quantity( - valued_move_line.qty_done, move.product_id.uom_id - ) + # valued_move_lines = move._get_in_move_lines() + # qty_done = 0 + # for valued_move_line in valued_move_lines: + # qty_done += valued_move_line.product_uom_id._compute_quantity( + # valued_move_line.qty_done, move.product_id.uom_id + # ) - qty = forced_qty or qty_done - if float_is_zero(product_tot_qty_available, precision_rounding=rounding): - new_std_price_in_currency = move._get_currency_price_unit( - default=move.product_id.standard_price_in_currency - ) - elif float_is_zero( - product_tot_qty_available + move.product_qty, precision_rounding=rounding - ) or float_is_zero(product_tot_qty_available + qty, precision_rounding=rounding): - new_std_price_in_currency = move._get_currency_price_unit( - default=move.product_id.standard_price_in_currency - ) - else: - # Get the standard price - amount_unit = ( - std_price_update.get((move.company_id.id, move.product_id.id)) - or move.product_id.with_company(move.company_id).standard_price_in_currency - ) - new_std_price_in_currency = ( - (amount_unit * product_tot_qty_available) + (move._get_currency_price_unit() * qty) - ) / (product_tot_qty_available + qty) + # qty = forced_qty or qty_done + # if float_is_zero(product_tot_qty_available, precision_rounding=rounding): + # new_std_price_in_currency = move._get_currency_price_unit( + # default=move.product_id.standard_price_in_currency + # ) + # elif float_is_zero( + # product_tot_qty_available + move.product_qty, precision_rounding=rounding + # ) or float_is_zero(product_tot_qty_available + qty, precision_rounding=rounding): + # new_std_price_in_currency = move._get_currency_price_unit( + # default=move.product_id.standard_price_in_currency + # ) + # else: + # # Get the standard price + # amount_unit = ( + # std_price_update.get((move.company_id.id, move.product_id.id)) + # or move.product_id.with_company(move.company_id).standard_price_in_currency + # ) + # new_std_price_in_currency = ( + # (amount_unit * product_tot_qty_available) + (move._get_currency_price_unit() * qty) + # ) / (product_tot_qty_available + qty) - tmpl_dict[move.product_id.id] += qty_done - # Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products - move.product_id.with_company(move.company_id.id).with_context(disable_auto_svl=True).sudo().write( - {"standard_price_in_currency": new_std_price_in_currency} - ) + # tmpl_dict[move.product_id.id] += qty_done + # # Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products + # move.product_id.with_company(move.company_id.id).with_context(disable_auto_svl=True).sudo().write( + # {"standard_price_in_currency": new_std_price_in_currency} + # ) - std_price_update[move.company_id.id, move.product_id.id] = new_std_price_in_currency - # adapt standard price on incomming moves if the product cost_method is 'fifo' - for move in self.filtered( - lambda move: move.with_company(move.company_id).product_id.cost_method == "fifo" - and float_is_zero(move.product_id.sudo().quantity_svl, precision_rounding=move.product_id.uom_id.rounding) - ): - move.product_id.with_company(move.company_id.id).sudo().write( - {"standard_price_in_currency": move._get_currency_price_unit()} - ) + # std_price_update[move.company_id.id, move.product_id.id] = new_std_price_in_currency + # # adapt standard price on incomming moves if the product cost_method is 'fifo' + # for move in self.filtered( + # lambda move: move.with_company(move.company_id).product_id.cost_method == "fifo" + # and float_is_zero(move.product_id.sudo().quantity_svl, precision_rounding=move.product_id.uom_id.rounding) + # ): + # move.product_id.with_company(move.company_id.id).sudo().write( + # {"standard_price_in_currency": move._get_currency_price_unit()} + # ) - def _get_currency_price_unit(self, default=0.0): - """Returns the unit price from this stock move""" - self.ensure_one() - currency_id = self.company_id.currency_id - if hasattr(self, "purchase_order_Line") and self.purchase_order_Line: - currency_id = self.purchase_order_Line.currency_id - if hasattr(self, "sale_line_id") and self.sale_line_id: - currency_id = self.sale_line_id.currency_id + # def _get_currency_price_unit(self, default=0.0): + # """Returns the unit price from this stock move""" + # self.ensure_one() + # currency_id = self.company_id.currency_id + # if hasattr(self, "purchase_order_Line") and self.purchase_order_Line: + # currency_id = self.purchase_order_Line.currency_id + # if hasattr(self, "sale_line_id") and self.sale_line_id: + # currency_id = self.sale_line_id.currency_id - price_unit = currency_id._convert( - from_amount=self.price_unit, - to_currency=self.product_id.categ_id.valuation_currency_id, - company=self.company_id, - date=fields.date.today(), - ) - precision = self.env["decimal.precision"].precision_get("Product Price") - # If the move is a return, use the original move's price unit. - if self.origin_returned_move_id and self.origin_returned_move_id.sudo().stock_valuation_layer_ids: - layers = self.origin_returned_move_id.sudo().stock_valuation_layer_ids - # dropshipping create additional positive svl to make sure there is no impact on the stock valuation - # We need to remove them from the computation of the price unit. - if ( - self.origin_returned_move_id._is_dropshipped() - or self.origin_returned_move_id._is_dropshipped_returned() - ): - layers = layers.filtered( - lambda l: float_compare(l.value, 0, precision_rounding=l.product_id.uom_id.rounding) <= 0 - ) - layers |= layers.stock_valuation_layer_ids - quantity = sum(layers.mapped("quantity")) - return ( - sum(layers.mapped("value_in_currency")) / quantity - if not float_is_zero(quantity, precision_rounding=layers.uom_id.rounding) - else 0 - ) - return price_unit if not float_is_zero(price_unit, precision) or self._should_force_price_unit() else default + # price_unit = currency_id._convert( + # from_amount=self.price_unit, + # to_currency=self.product_id.categ_id.valuation_currency_id, + # company=self.company_id, + # date=fields.date.today(), + # ) + # precision = self.env["decimal.precision"].precision_get("Product Price") + # # If the move is a return, use the original move's price unit. + # if self.origin_returned_move_id and self.origin_returned_move_id.sudo().stock_valuation_layer_ids: + # layers = self.origin_returned_move_id.sudo().stock_valuation_layer_ids + # # dropshipping create additional positive svl to make sure there is no impact on the stock valuation + # # We need to remove them from the computation of the price unit. + # if ( + # self.origin_returned_move_id._is_dropshipped() + # or self.origin_returned_move_id._is_dropshipped_returned() + # ): + # layers = layers.filtered( + # lambda l: float_compare(l.value, 0, precision_rounding=l.product_id.uom_id.rounding) <= 0 + # ) + # layers |= layers.stock_valuation_layer_ids + # quantity = sum(layers.mapped("quantity")) + # return ( + # sum(layers.mapped("value_in_currency")) / quantity + # if not float_is_zero(quantity, precision_rounding=layers.uom_id.rounding) + # else 0 + # ) + # return price_unit if not float_is_zero(price_unit, precision) or self._should_force_price_unit() else default