Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stock_currency_valuation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from . import models
from . import wizard
# from . import wizard
12 changes: 6 additions & 6 deletions stock_currency_valuation/__manifest__.py
Original file line number Diff line number Diff line change
@@ -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": "",
Expand All @@ -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": {},
Expand Down
5 changes: 3 additions & 2 deletions stock_currency_valuation/models/__init__.py
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions stock_currency_valuation/models/product_value.py
Original file line number Diff line number Diff line change
@@ -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
267 changes: 148 additions & 119 deletions stock_currency_valuation/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -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