-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[IMP] Add stock_currency_valuation module
- Loading branch information
Showing
13 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import models | ||
from . import wizard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
'name': 'Stock currency valuation', | ||
'version': "16.0.1.0.0", | ||
'category': 'Warehouse Management', | ||
'sequence': 14, | ||
'summary': '', | ||
'author': 'ADHOC SA', | ||
'website': 'www.adhoc.com.ar', | ||
'images': [ | ||
], | ||
'depends': [ | ||
'stock_account', | ||
'product_replenishment_cost', | ||
], | ||
'data': [ | ||
'views/product_category.xml', | ||
'views/product.xml', | ||
'views/stock_valuation_layer.xml', | ||
], | ||
'installable': True, | ||
'auto_install': False, | ||
'application': False, | ||
'assets': { | ||
}, | ||
'license': 'AGPL-3', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from . import product_category | ||
from . import stock_valuation_layer | ||
from . import product_product | ||
from . import product_template | ||
from . import stock_move |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from odoo import models, fields | ||
|
||
|
||
class productCategory(models.Model): | ||
|
||
_inherit = 'product.category' | ||
|
||
valuation_currency_id = fields.Many2one( | ||
'res.currency', | ||
string='Secondary Currency Valuation', | ||
company_dependent=True, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from odoo import models, fields | ||
|
||
|
||
class productProduct(models.Model): | ||
|
||
_inherit = 'product.product' | ||
|
||
valuation_currency_id = fields.Many2one( | ||
related="categ_id.valuation_currency_id", | ||
currency_field='valuation_currency_id' | ||
) | ||
standard_price_in_currency = fields.Float( | ||
'Cost in currency', company_dependent=True, | ||
groups="base.group_user", | ||
) | ||
|
||
def _change_standard_price(self, new_price): | ||
super(productProduct, self.with_context(default_bypass_currency_valuation=True))._change_standard_price(new_price) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from odoo import models, fields, api | ||
|
||
|
||
class productTemplate(models.Model): | ||
|
||
_inherit = 'product.template' | ||
|
||
valuation_currency_id = fields.Many2one( | ||
related="categ_id.valuation_currency_id", | ||
currency_field='valuation_currency_id' | ||
) | ||
standard_price_in_currency = fields.Float( | ||
'Cost', compute='_compute_standard_price_in_currency', | ||
inverse='_set_standard_price_in_currency', search='_search_standard_price_in_currency', | ||
digits='Product Price', groups="base.group_user", | ||
) | ||
replenishment_cost_type = fields.Selection( | ||
selection_add=[('average_in_currency', 'Average Cost in Currency')], | ||
ondelete={'average_in_currency': 'set default'} | ||
) | ||
|
||
@api.depends() | ||
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, | ||
}) | ||
|
||
@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 | ||
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 | ||
|
||
def _set_standard_price_in_currency(self): | ||
for template in self: | ||
if len(template.product_variant_ids) == 1: | ||
template.product_variant_ids.standard_price_in_currency = template.standard_price_in_currency | ||
|
||
def _search_standard_price_in_currency(self, operator, value): | ||
products = self.env['product.product'].search([('standard_price_in_currency', operator, value)], limit=None) | ||
return [('id', 'in', products.mapped('product_tmpl_id').ids)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from collections import defaultdict | ||
|
||
from odoo import models, fields | ||
from odoo.tools.float_utils import float_compare, float_is_zero | ||
|
||
|
||
class StockMove(models.Model): | ||
_inherit = "stock.move" | ||
|
||
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) | ||
|
||
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() | ||
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() | ||
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}) | ||
|
||
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): | ||
""" 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 self.product_id.standard_price_in_currency |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from odoo import models, fields, api, _ | ||
from odoo.tools.safe_eval import safe_eval | ||
from odoo.exceptions import ValidationError | ||
from odoo.tools import float_compare | ||
|
||
|
||
class StockValuationLayer(models.Model): | ||
|
||
_inherit = 'stock.valuation.layer' | ||
|
||
valuation_currency_id = fields.Many2one(related="categ_id.valuation_currency_id") | ||
unit_cost_in_currency = fields.Monetary('Unit Value in currency', compute="_compute_other_currency_values", currency_field='valuation_currency_id', store=True) | ||
value_in_currency = fields.Monetary('Total Value incurrency', compute="_compute_other_currency_values", currency_field='valuation_currency_id', store=True) | ||
product_tmpl_id = fields.Many2one(store=True) | ||
bypass_currency_valuation = fields.Boolean() | ||
|
||
@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 | ||
|
||
if not rec.bypass_currency_valuation and rec.valuation_currency_id: | ||
rec.value_in_currency = rec.currency_id._convert( | ||
from_amount=rec.value, | ||
to_currency=rec.valuation_currency_id, | ||
company=rec.company_id, | ||
date=rec.create_date, | ||
) | ||
if rec.quantity: | ||
rec.unit_cost_in_currency = abs(rec.value_in_currency / rec.quantity) | ||
else: | ||
rec.unit_cost_in_currency = False | ||
else: | ||
rec.value_in_currency = False | ||
rec.unit_cost_in_currency = False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<odoo> | ||
<record id="product_template_form_view" model="ir.ui.view"> | ||
<field name="name">product.template.form</field> | ||
<field name="model">product.template</field> | ||
<field name="inherit_id" ref="product_replenishment_cost.product_template_form_view"/> | ||
<field name="arch" type="xml"> | ||
<group name="accounting_cost"> | ||
<label for="standard_price_in_currency"/> | ||
<div name="standard_price_uom"> | ||
<field name="standard_price_in_currency" class="oe_inline" widget="monetary" options="{'currency_field': 'valuation_currency_id', 'field_digits': True}"/> | ||
<span groups="uom.group_uom">per | ||
<field name="uom_name" class="oe_inline"/> | ||
</span> | ||
</div> | ||
</group> | ||
</field> | ||
</record> | ||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<odoo> | ||
<record id="product_category_form_view" model="ir.ui.view"> | ||
<field name="name">product.category.form</field> | ||
<field name="model">product.category</field> | ||
<field name="inherit_id" ref="product.product_category_form_view"/> | ||
<field name="arch" type="xml"> | ||
<form><field name="property_valuation" invisible="1"/></form> | ||
<field name="property_valuation" position="after"> | ||
<field name="valuation_currency_id" attrs="{'invisible':[('property_cost_method', 'not in', ['average', 'fifo']), ('property_valuation', '!=', 'real_time')]}"/> | ||
</field> | ||
</field> | ||
</record> | ||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<odoo> | ||
<record id="stock_valuation_layer_tree" model="ir.ui.view"> | ||
<field name="name">stock.valuation.layer.tree</field> | ||
<field name="model">stock.valuation.layer</field> | ||
<field name="inherit_id" ref="stock_account.stock_valuation_layer_tree"/> | ||
<field name="arch" type="xml"> | ||
<field name="value" position="after"> | ||
<field name="valuation_currency_id" invisible="1"/> | ||
<field name="unit_cost_in_currency" optional="hide"/> | ||
<field name="value_in_currency" sum="Total in currency" optional="show"/> | ||
</field> | ||
</field> | ||
</record> | ||
<record id="stock_valuation_layer_form" model="ir.ui.view"> | ||
<field name="name">stock.valuation.layer.form</field> | ||
<field name="model">stock.valuation.layer</field> | ||
<field name="inherit_id" ref="stock_account.stock_valuation_layer_form"/> | ||
<field name="arch" type="xml"> | ||
<field name="unit_cost" position="after"> | ||
<field name="unit_cost_in_currency"/> | ||
</field> | ||
<field name="value" position="after"> | ||
<field name="value_in_currency"/> | ||
</field> | ||
<form><field name="valuation_currency_id" invisible="1"/></form> | ||
</field> | ||
</record> | ||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import stock_valuation_layer_revaluation |
22 changes: 22 additions & 0 deletions
22
stock_currency_valuation/wizard/stock_valuation_layer_revaluation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from odoo import models | ||
|
||
|
||
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 |