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..fc30536f0 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,20 @@
"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_move_views.xml",
+ #"views/product_value_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..da7c16f5a 100644
--- a/stock_currency_valuation/models/__init__.py
+++ b/stock_currency_valuation/models/__init__.py
@@ -1,7 +1,9 @@
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 product_value
+
+# from . import stock_valuation_layer
+# from . import stock_landed_cost
diff --git a/stock_currency_valuation/models/product_product.py b/stock_currency_valuation/models/product_product.py
index c10df97b7..19a50a9ae 100644
--- a/stock_currency_valuation/models/product_product.py
+++ b/stock_currency_valuation/models/product_product.py
@@ -1,4 +1,9 @@
-from odoo import fields, models
+from bisect import bisect
+from collections import defaultdict
+
+from odoo import _, api, fields, models
+from odoo.fields import Domain
+from odoo.tools import SQL
class productProduct(models.Model):
@@ -13,3 +18,347 @@ class productProduct(models.Model):
groups="base.group_user",
help="Cost of the product expressed in the secondary currency defined on the product category. Used for inventory valuation and cost calculations in that currency.",
)
+ avg_cost_in_currency = fields.Monetary(
+ string="Average Cost (Currency)",
+ compute="_compute_value_in_currency",
+ compute_sudo=True,
+ currency_field="valuation_currency_id",
+ )
+ total_value_in_currency = fields.Monetary(
+ string="Total Value (Currency)",
+ compute="_compute_value_in_currency",
+ compute_sudo=True,
+ currency_field="valuation_currency_id",
+ )
+
+ def write(self, vals):
+ old_price_in_currency = False
+ require_standard_price_compute = "standard_price" in vals and not self.env.context.get(
+ "disable_auto_revaluation"
+ )
+ if "standard_price_in_currency" in vals and not self.env.context.get("disable_auto_revaluation"):
+ old_price_in_currency = {product: product.standard_price_in_currency for product in self}
+ res = super(productProduct, self.with_context(old_price_in_currency=old_price_in_currency)).write(vals)
+ if not require_standard_price_compute and old_price_in_currency:
+ self.with_context(old_price_in_currency=old_price_in_currency)._change_standard_price({})
+ return res
+
+
+ @api.depends_context("to_date", "company", "warehouse_id")
+ @api.depends("cost_method", "stock_move_ids.value_in_currency", "standard_price_in_currency")
+ def _compute_value_in_currency(self):
+ # Only meaningful for products that have a secondary valuation currency.
+ # Products without one get zeroed out and we skip heavy computation for them.
+ for product in self.filtered(lambda p: not p.valuation_currency_id):
+ product.avg_cost_in_currency = 0
+ product.total_value_in_currency = 0
+
+ products = self.filtered(lambda p: p.valuation_currency_id)
+ if not products:
+ return
+
+ products = products._with_valuation_context()
+
+ at_date = fields.Datetime.to_datetime(self.env.context.get("to_date"))
+ if at_date:
+ at_date = at_date.replace(hour=23, minute=59, second=59)
+ products = products.with_context(at_date=at_date)
+
+ avg_cost_in_currency_by_product_id = {}
+ total_value_in_currency_by_product_id = {}
+ ratio_by_product_id = {}
+
+ product_ids_grouped_by_cost_method = defaultdict(set)
+ for product in products:
+ if product.lot_valuated:
+ # lot-valuated: not handled here, zero out
+ avg_cost_in_currency_by_product_id[product.id] = 0
+ total_value_in_currency_by_product_id[product.id] = 0
+ continue
+ product_whole_company_context = product.with_context(warehouse_id=False)
+ if product.uom_id.is_zero(product.qty_available):
+ total_value_in_currency_by_product_id[product.id] = 0
+ avg_cost_in_currency_by_product_id[product.id] = product.standard_price_in_currency
+ continue
+ if product.uom_id.is_zero(product_whole_company_context.qty_available):
+ total_value_in_currency_by_product_id[product.id] = (
+ product.standard_price_in_currency * product.qty_available
+ )
+ avg_cost_in_currency_by_product_id[product.id] = product.standard_price_in_currency
+ continue
+ if product.uom_id.compare(product.qty_available, product_whole_company_context.qty_available) != 0:
+ ratio_by_product_id[product.id] = (
+ product.qty_available / product_whole_company_context.qty_available
+ )
+ product_ids_grouped_by_cost_method[product.cost_method].add(product.id)
+
+ for cost_method, product_ids in product_ids_grouped_by_cost_method.items():
+ batch = self.env["product.product"].browse(product_ids).with_context(warehouse_id=False)
+ if cost_method == "standard":
+ avg_costs, total_values = batch._run_standard_batch_in_currency(at_date=at_date)
+ elif cost_method == "average":
+ avg_costs, total_values = batch._run_average_batch_in_currency(
+ at_date=at_date, force_recompute=True
+ )
+ else:
+ avg_costs, total_values = batch._run_fifo_batch_in_currency(at_date=at_date)
+ avg_cost_in_currency_by_product_id.update(avg_costs)
+ total_value_in_currency_by_product_id.update(total_values)
+
+ for product in self.filtered(lambda p: p.valuation_currency_id):
+ product.avg_cost_in_currency = avg_cost_in_currency_by_product_id.get(
+ product.id, product.standard_price_in_currency
+ )
+ product.total_value_in_currency = total_value_in_currency_by_product_id.get(
+ product.id, 0
+ ) * ratio_by_product_id.get(product.id, 1)
+
+ # -------------------------------------------------------------------------
+ # Private
+ # -------------------------------------------------------------------------
+
+ def _change_standard_price(self, old_price):
+ with_valuation_currency = self.filtered(lambda x: x.valuation_currency_id)
+ super(productProduct, self - with_valuation_currency)._change_standard_price(old_price)
+ old_price_in_currency = self.env.context.get("old_price_in_currency") or {}
+ for product in with_valuation_currency:
+ if product.cost_method == "fifo" or (
+ product.standard_price == old_price.get(product)
+ and product.standard_price_in_currency == old_price_in_currency.get(product)
+ ):
+ continue
+ self.env["product.value"].sudo().create(
+ {
+ "product_id": product.id,
+ "value": product.standard_price,
+ "value_in_currency": product.standard_price_in_currency,
+ "valuation_currency_id": product.valuation_currency_id.id,
+ "company_id": product.company_id.id or self.env.company.id,
+ "date": fields.Datetime.now(),
+ "description": _(
+ "Price update from %(old_price)s to %(new_price)s by %(user)s",
+ old_price=old_price.get(product) or old_price_in_currency.get(product),
+ new_price=product.standard_price,
+ user=self.env.user.name,
+ ),
+ }
+ )
+ return
+
+ def _get_last_product_value(self, date=None, lot=False):
+ # estoy reemplazando el metodo para agregar value_in_currency.
+ # es mejor ir por un monkey patch?
+ domain = Domain(
+ [
+ ("product_id", "in", self.ids),
+ ("move_id", "=", False),
+ ]
+ )
+ if lot:
+ domain &= Domain(["|", ("lot_id", "=", lot.id), ("lot_id", "=", False)])
+ else:
+ domain &= Domain([("lot_id", "=", False)])
+ if date:
+ domain &= Domain([("date", "<=", date)])
+
+ query = self.env["product.value"].sudo()._search(domain)
+ query_select = SQL("distinct ON (product_value.product_id) product_value.id")
+ query.order = SQL("product_value.product_id, product_value.date DESC, product_value.id DESC")
+ query._ids = tuple(id_ for (id_,) in self.env.execute_query(query.select(query_select)))
+ product_values = self.env["product.value"].browse(query._ids)
+ product_values.sudo().fetch(["product_id", "value", "value_in_currency", "date"])
+ return {pv.product_id: pv for pv in product_values}
+
+ def _run_standard_batch_in_currency(self, at_date=None, lot=None):
+ """Equivalent of _run_standard_batch but using standard_price_in_currency.
+ At a given date reads the last recorded product.value.value_in_currency."""
+ std_price_in_currency_by_product_id = {p.id: p.standard_price_in_currency for p in self}
+ if at_date:
+ product_value_by_product = self._get_last_product_value(at_date, lot=lot)
+ std_price_in_currency_by_product_id = {
+ p.id: (
+ product_value_by_product[p].value_in_currency
+ if p in product_value_by_product
+ else p.standard_price_in_currency
+ )
+ for p in self
+ }
+ value_by_product_id = {
+ p.id: p.qty_available * std_price_in_currency_by_product_id.get(p.id, 0) for p in self
+ }
+ return std_price_in_currency_by_product_id, value_by_product_id
+
+ def _run_fifo_batch_in_currency(self, at_date=None, lot=None):
+ """Equivalent of _run_fifo_batch but for the secondary valuation currency.
+ Translates the FIFO stack value (company currency) to valuation_currency
+ using today's exchange rate."""
+ std_price_in_currency_by_product_id = {}
+ value_in_currency_by_product_id = {}
+ for product in self:
+ quantity = product.qty_available
+ value = product._run_fifo(quantity, lot, at_date)
+ if value and product.valuation_currency_id:
+ value_in_currency = product.company_id.currency_id._convert(
+ from_amount=value,
+ to_currency=product.valuation_currency_id,
+ company=product.company_id,
+ date=fields.Date.today(),
+ )
+ else:
+ value_in_currency = 0
+ std_price = value_in_currency / quantity if quantity else 0
+ std_price_in_currency_by_product_id[product.id] = std_price
+ value_in_currency_by_product_id[product.id] = value_in_currency
+ return std_price_in_currency_by_product_id, value_in_currency_by_product_id
+
+ def _run_average_batch_in_currency(self, at_date=None, lot=None, force_recompute=False):
+ """Replica de _run_average_batch pero calculando el costo promedio en la moneda
+ secundaria (valuation_currency_id) usando value_in_currency de los moves.
+
+ Retorna (std_price_in_currency_by_product_id, value_in_currency_by_product_id).
+ Solo procesa productos que tienen valuation_currency_id definido.
+ """
+ std_price_in_currency_by_product_id = {}
+ value_in_currency_by_product_id = {}
+ quantity_by_product_id = {}
+
+ # Solo tiene sentido para productos con moneda de valuación
+ products = self.filtered(lambda p: p.valuation_currency_id)
+ if not products:
+ return std_price_in_currency_by_product_id, value_in_currency_by_product_id
+
+ if not at_date and not force_recompute:
+ std_price_in_currency_by_product_id = {p.id: p.standard_price_in_currency for p in products}
+ value_in_currency_by_product_id = {
+ p.id: p.qty_available * std_price_in_currency_by_product_id.get(p.id, 0) for p in products
+ }
+ return std_price_in_currency_by_product_id, value_in_currency_by_product_id
+
+ moves_domain = Domain([
+ ('product_id', 'in', products._as_query()),
+ ('company_id', '=', self.env.company.id),
+ '|', '|', ('is_in', '=', True), ('is_dropship', '=', True), ('is_out', '=', True),
+ ])
+ if lot:
+ moves_domain &= Domain([('move_line_ids.lot_id', 'in', lot.id)])
+ if at_date:
+ moves_domain &= Domain([('date', '<=', at_date)])
+
+ move_fields = [
+ 'date', 'is_dropship', 'is_in', 'is_out', 'location_dest_id', 'location_id',
+ 'move_line_ids', 'picked', 'value', 'value_in_currency', 'product_id',
+ ]
+ last_manual_value_by_product = products._get_last_product_value(at_date, lot=lot)
+ oldest_manual_value = (
+ min(pv.date for pv in last_manual_value_by_product.values())
+ if last_manual_value_by_product else False
+ )
+ if oldest_manual_value:
+ moves_domain &= Domain([('date', '>=', oldest_manual_value)])
+
+ moves = self.env['stock.move'].search_fetch(
+ moves_domain,
+ field_names=move_fields,
+ order='date, id',
+ )
+ moves.move_line_ids.fetch([
+ 'company_id', 'location_id', 'location_dest_id', 'lot_id', 'owner_id', 'picked', 'quantity_product_uom',
+ ])
+
+ moves_by_product = moves.grouped(key=lambda m: m.product_id)
+
+ # Punto de partida: último valor manual registrado en product.value
+ for manual_value in last_manual_value_by_product.values():
+ product = manual_value.product_id
+ quantity = product.with_context(to_date=manual_value.date).qty_available
+
+ std_price_in_currency_by_product_id[product.id] = manual_value.value_in_currency
+ quantity_by_product_id[product.id] = quantity
+ value_in_currency_by_product_id[product.id] = manual_value.value_in_currency * quantity
+
+ product_moves = moves_by_product.get(product, self.env['stock.move'])
+ index = bisect(product_moves, manual_value.date, key=lambda m: m.date)
+ moves_by_product[product] = product_moves[index:]
+
+ # Reproducir el historial de valuación en moneda secundaria
+ for product, product_moves in moves_by_product.items():
+ quantity = quantity_by_product_id.get(product.id, 0)
+ average_cost = std_price_in_currency_by_product_id.get(product.id, 0)
+ value = value_in_currency_by_product_id.get(product.id, 0)
+
+ for move in product_moves:
+ if move.is_in or move.is_dropship:
+ in_qty = move._get_valued_qty()
+ in_value = move.value_in_currency
+ if lot:
+ lot_qty = move._get_valued_qty(lot)
+ in_value = (in_value * lot_qty / in_qty) if in_qty else 0
+ in_qty = lot_qty
+ previous_qty = quantity
+ quantity += in_qty
+ if previous_qty > 0:
+ value += in_value
+ average_cost = value / quantity if quantity else average_cost
+ elif previous_qty <= 0:
+ average_cost = in_value / in_qty if in_qty else average_cost
+ value = average_cost * quantity
+ if move.is_out or move.is_dropship:
+ out_qty = move._get_valued_qty()
+ out_value = out_qty * average_cost
+ if lot:
+ lot_qty = move._get_valued_qty(lot)
+ out_value = (out_value * lot_qty / out_qty) if out_qty else 0
+ out_qty = lot_qty
+ value -= out_value
+ quantity -= out_qty
+
+ std_price_in_currency_by_product_id[product.id] = average_cost
+ value_in_currency_by_product_id[product.id] = value
+
+ return std_price_in_currency_by_product_id, value_in_currency_by_product_id
+
+ def _update_standard_price(self, extra_value=None, extra_quantity=None):
+ """Extiende _update_standard_price para también actualizar standard_price_in_currency
+ en productos que tienen valuation_currency_id definido."""
+ super()._update_standard_price(extra_value=extra_value, extra_quantity=extra_quantity)
+
+ products_with_currency = self.filtered(lambda p: p.valuation_currency_id and not p.lot_valuated)
+ if not products_with_currency:
+ return
+
+ # Agrupar por cost_method para actualizar standard_price_in_currency
+ products_by_cost_method = defaultdict(lambda: self.env['product.product'])
+ for product in products_with_currency:
+ products_by_cost_method[product.cost_method] |= product
+
+ for cost_method, products in products_by_cost_method.items():
+ if cost_method == 'standard':
+ # Para precio estándar no hay recálculo automático de AVCO;
+ # el usuario debe actualizar standard_price_in_currency manualmente.
+ continue
+
+ if cost_method == 'average':
+ new_prices_in_currency = products._run_average_batch_in_currency(force_recompute=True)[0]
+ for product in products:
+ if product.id in new_prices_in_currency:
+ product.with_context(disable_auto_revaluation=True).sudo().standard_price_in_currency = (
+ new_prices_in_currency[product.id]
+ )
+ continue
+
+ if cost_method == 'fifo':
+ # Para FIFO: ratio entre standard_price_in_currency y standard_price
+ # proporcional al último precio de entrada, usando la tasa de la moneda actual.
+ for product in products:
+ qty_available = product._with_valuation_context().qty_available
+ if product.uom_id.compare(qty_available, 0) > 0 and product.standard_price:
+ # Calcular usando la tasa de cambio actual entre company_currency y valuation_currency
+ new_price_in_currency = product.company_id.currency_id._convert(
+ from_amount=product.standard_price,
+ to_currency=product.valuation_currency_id,
+ company=product.company_id,
+ date=fields.Date.today(),
+ )
+ product.with_context(disable_auto_revaluation=True).sudo().standard_price_in_currency = (
+ new_price_in_currency
+ )
diff --git a/stock_currency_valuation/models/product_value.py b/stock_currency_valuation/models/product_value.py
new file mode 100644
index 000000000..8f97bf43f
--- /dev/null
+++ b/stock_currency_valuation/models/product_value.py
@@ -0,0 +1,64 @@
+from odoo import api, fields, models
+
+
+class ProductValue(models.Model):
+ _inherit = "product.value"
+
+ valuation_currency_id = fields.Many2one("res.currency", 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.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.id
+ elif product_value.product_id:
+ product_value.valuation_currency_id = product_value.product_id.with_company(
+ product_value.company_id
+ ).valuation_currency_id.id
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ """Override create to set default values from product's standard_price if not provided"""
+ for vals in vals_list:
+ # Obtener el producto según el contexto
+ product = None
+ if vals.get("product_id"):
+ product = self.env["product.product"].browse(vals["product_id"])
+ elif vals.get("lot_id"):
+ lot = self.env["stock.lot"].browse(vals["lot_id"])
+ product = lot.product_id
+ elif vals.get("move_id"):
+ move = self.env["stock.move"].browse(vals["move_id"])
+ product = move.product_id
+
+ if product:
+ # Obtener la compañía
+ company_id = vals.get("company_id")
+ if not company_id:
+ if vals.get("move_id"):
+ company_id = self.env["stock.move"].browse(vals["move_id"]).company_id.id
+ elif vals.get("lot_id"):
+ company_id = self.env["stock.lot"].browse(vals["lot_id"]).company_id.id
+ else:
+ company_id = self.env.company.id
+
+ company = self.env["res.company"].browse(company_id)
+ product_with_company = product.with_company(company)
+
+ # Si no está definido value, usar standard_price del producto
+ if "value" not in vals or not vals.get("value"):
+ vals["value"] = product_with_company.standard_price
+
+ # Si no está definido value_in_currency y el producto tiene moneda de valuación, usar standard_price_in_currency
+ if (
+ "value_in_currency" not in vals or not vals.get("value_in_currency")
+ ) and product_with_company.valuation_currency_id:
+ vals["value_in_currency"] = product_with_company.standard_price_in_currency
+ return super().create(vals_list)
diff --git a/stock_currency_valuation/models/stock_move.py b/stock_currency_valuation/models/stock_move.py
index 5fe09c929..db0a3e780 100644
--- a/stock_currency_valuation/models/stock_move.py
+++ b/stock_currency_valuation/models/stock_move.py
@@ -1,134 +1,180 @@
-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(
+ "Currency 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(
+ "Currency Manual Value",
+ currency_field="valuation_currency_id",
+ compute="_compute_value_manual",
+ inverse="_inverse_value_in_currency_manual",
+ )
+ standard_price_in_currency = fields.Float(
+ related="product_id.standard_price_in_currency", string="Standard Price in currency"
+ )
+
+ def _inverse_value_in_currency_manual(self):
+ for move in self:
+ if move.value_manual_in_currency == move.value_in_currency:
+ continue
+ self.env["product.value"].create(
+ {
+ "move_id": move.id,
+ "value_in_currency": move.value_manual_in_currency,
+ "company_id": move.company_id.id,
+ }
+ )
- 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 _compute_value_manual(self):
+ super()._compute_value_manual()
+ for move in self:
+ move.value_manual_in_currency = move.value_in_currency
+
+ 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
- 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
- )
+ # 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
- 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)
+ # 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
- 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}
- )
+ # 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
+ # )
- 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()}
- )
+ # 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)
- 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
+ # 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}
+ # )
- 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
+ # 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
+
+ # 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
diff --git a/stock_currency_valuation/views/product_value_views.xml b/stock_currency_valuation/views/product_value_views.xml
new file mode 100644
index 000000000..e6dd3fe92
--- /dev/null
+++ b/stock_currency_valuation/views/product_value_views.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ product.value.form.view.inherit.currency.valuation
+ product.value
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stock_currency_valuation/views/stock_move_views.xml b/stock_currency_valuation/views/stock_move_views.xml
new file mode 100644
index 000000000..c99a521ab
--- /dev/null
+++ b/stock_currency_valuation/views/stock_move_views.xml
@@ -0,0 +1,35 @@
+
+
+
+
+ stock.move.view.list.inherit.currency.valuation
+ stock.move
+
+
+
+
+
+
+
+
+
+
+
+ stock.move.view.list.valuation.inherit.currency
+ stock.move
+
+
+
+
+
+
+
+
+