summaryrefslogtreecommitdiff
path: root/addons/stock_account/wizard
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/stock_account/wizard
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/stock_account/wizard')
-rw-r--r--addons/stock_account/wizard/__init__.py6
-rw-r--r--addons/stock_account/wizard/stock_picking_return.py31
-rw-r--r--addons/stock_account/wizard/stock_quantity_history.py18
-rw-r--r--addons/stock_account/wizard/stock_valuation_layer_revaluation.py160
-rw-r--r--addons/stock_account/wizard/stock_valuation_layer_revaluation_views.xml44
5 files changed, 259 insertions, 0 deletions
diff --git a/addons/stock_account/wizard/__init__.py b/addons/stock_account/wizard/__init__.py
new file mode 100644
index 00000000..b920576a
--- /dev/null
+++ b/addons/stock_account/wizard/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import stock_quantity_history
+from . import stock_picking_return
+from . import stock_valuation_layer_revaluation
diff --git a/addons/stock_account/wizard/stock_picking_return.py b/addons/stock_account/wizard/stock_picking_return.py
new file mode 100644
index 00000000..f359f921
--- /dev/null
+++ b/addons/stock_account/wizard/stock_picking_return.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class StockReturnPicking(models.TransientModel):
+ _inherit = "stock.return.picking"
+
+ @api.model
+ def default_get(self, default_fields):
+ res = super(StockReturnPicking, self).default_get(default_fields)
+ for i, k, vals in res.get('product_return_moves', []):
+ vals.update({'to_refund': True})
+ return res
+
+ def _create_returns(self):
+ new_picking_id, pick_type_id = super(StockReturnPicking, self)._create_returns()
+ new_picking = self.env['stock.picking'].browse([new_picking_id])
+ for move in new_picking.move_lines:
+ return_picking_line = self.product_return_moves.filtered(lambda r: r.move_id == move.origin_returned_move_id)
+ if return_picking_line and return_picking_line.to_refund:
+ move.to_refund = True
+ return new_picking_id, pick_type_id
+
+
+class StockReturnPickingLine(models.TransientModel):
+ _inherit = "stock.return.picking.line"
+
+ to_refund = fields.Boolean(string="Update quantities on SO/PO", default=True,
+ help='Trigger a decrease of the delivered/received quantity in the associated Sale Order/Purchase Order')
diff --git a/addons/stock_account/wizard/stock_quantity_history.py b/addons/stock_account/wizard/stock_quantity_history.py
new file mode 100644
index 00000000..1026e7bd
--- /dev/null
+++ b/addons/stock_account/wizard/stock_quantity_history.py
@@ -0,0 +1,18 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+from odoo.tools.misc import format_datetime
+
+
+class StockQuantityHistory(models.TransientModel):
+ _inherit = 'stock.quantity.history'
+
+ def open_at_date(self):
+ active_model = self.env.context.get('active_model')
+ if active_model == 'stock.valuation.layer':
+ action = self.env["ir.actions.actions"]._for_xml_id("stock_account.stock_valuation_layer_action")
+ action['domain'] = [('create_date', '<=', self.inventory_datetime), ('product_id.type', '=', 'product')]
+ action['display_name'] = format_datetime(self.env, self.inventory_datetime)
+ return action
+
+ return super(StockQuantityHistory, self).open_at_date()
diff --git a/addons/stock_account/wizard/stock_valuation_layer_revaluation.py b/addons/stock_account/wizard/stock_valuation_layer_revaluation.py
new file mode 100644
index 00000000..3c4b8870
--- /dev/null
+++ b/addons/stock_account/wizard/stock_valuation_layer_revaluation.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import _, api, fields, models
+from odoo.exceptions import UserError
+from odoo.tools import float_is_zero
+
+
+class StockValuationLayerRevaluation(models.TransientModel):
+ _name = 'stock.valuation.layer.revaluation'
+ _description = "Wizard model to reavaluate a stock inventory for a product"
+ _check_company_auto = True
+
+ @api.model
+ def default_get(self, default_fields):
+ res = super().default_get(default_fields)
+ if res.get('product_id'):
+ product = self.env['product.product'].browse(res['product_id'])
+ if product.categ_id.property_cost_method == 'standard':
+ raise UserError(_("You cannot revalue a product with a standard cost method."))
+ if product.quantity_svl <= 0:
+ raise UserError(_("You cannot revalue a product with an empty or negative stock."))
+ if 'account_journal_id' not in res and 'account_journal_id' in default_fields and product.categ_id.property_valuation == 'real_time':
+ accounts = product.product_tmpl_id.get_product_accounts()
+ res['account_journal_id'] = accounts['stock_journal'].id
+ return res
+
+ company_id = fields.Many2one('res.company', "Company", readonly=True, required=True)
+ currency_id = fields.Many2one('res.currency', "Currency", related='company_id.currency_id', required=True)
+
+ product_id = fields.Many2one('product.product', "Related product", required=True, check_company=True)
+ property_valuation = fields.Selection(related='product_id.categ_id.property_valuation')
+ product_uom_name = fields.Char("Unit of Measure", related='product_id.uom_id.name')
+ current_value_svl = fields.Float("Current Value", related="product_id.value_svl")
+ current_quantity_svl = fields.Float("Current Quantity", related="product_id.quantity_svl")
+
+ added_value = fields.Monetary("Added value", required=True)
+ new_value = fields.Monetary("New value", compute='_compute_new_value')
+ new_value_by_qty = fields.Monetary("New value by quantity", compute='_compute_new_value')
+ reason = fields.Char("Reason", help="Reason of the revaluation")
+
+ account_journal_id = fields.Many2one('account.journal', "Journal", check_company=True)
+ account_id = fields.Many2one('account.account', "Counterpart Account", domain=[('deprecated', '=', False)], check_company=True)
+ date = fields.Date("Accounting Date")
+
+ @api.depends('current_value_svl', 'current_quantity_svl', 'added_value')
+ def _compute_new_value(self):
+ for reval in self:
+ reval.new_value = reval.current_value_svl + reval.added_value
+ if not float_is_zero(reval.current_quantity_svl, precision_rounding=self.product_id.uom_id.rounding):
+ reval.new_value_by_qty = reval.new_value / reval.current_quantity_svl
+ else:
+ reval.new_value_by_qty = 0.0
+
+ def action_validate_revaluation(self):
+ """ 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)
+
+ 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
+ )
+ 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):
+ svl.remaining_value += remaining_value
+ else:
+ taken_remaining_value = remaining_value_unit_cost * svl.remaining_qty
+ svl.remaining_value += taken_remaining_value
+ remaining_value -= taken_remaining_value
+ remaining_qty -= svl.remaining_qty
+
+ 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 == 'average':
+ product_id.with_context(disable_auto_svl=True).standard_price += self.added_value / 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=self.current_value_svl,
+ new_value=self.current_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=self.current_value_svl,
+ new_value=self.current_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,
+ })],
+ }
+ account_move = self.env['account.move'].create(move_vals)
+ account_move._post()
+
+ return True
diff --git a/addons/stock_account/wizard/stock_valuation_layer_revaluation_views.xml b/addons/stock_account/wizard/stock_valuation_layer_revaluation_views.xml
new file mode 100644
index 00000000..c867b84b
--- /dev/null
+++ b/addons/stock_account/wizard/stock_valuation_layer_revaluation_views.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+ <record id="stock_valuation_layer_revaluation_form_view" model="ir.ui.view">
+ <field name="name">stock.valuation.layer.revaluation.form</field>
+ <field name="model">stock.valuation.layer.revaluation</field>
+ <field name="arch" type="xml">
+ <form string="Product Revaluation">
+ <sheet>
+ <group>
+ <label for="current_value_svl" string="Current Value"/>
+ <div class="o_row">
+ <span><field name="current_value_svl" widget="monetary"/> for <field name="current_quantity_svl"/> <field name="product_uom_name" class="mx-1"/> </span>
+ </div>
+ <label for="added_value" string="Added Value"/>
+ <div class="o_row">
+ <span><field name="added_value" class="oe_inline"/> = <field name="new_value"/> (<field name="new_value_by_qty"/> by <field name="product_uom_name" class="mx-1"/>)
+ <small class="mx-2 font-italic">Use a negative added value to record a decrease in the product value</small></span>
+ </div>
+ <field name="company_id" invisible="1"/>
+ <field name="currency_id" invisible="1"/>
+ <field name="product_id" invisible="1"/>
+ </group>
+ <group>
+ <field name="property_valuation" invisible="1"/>
+ <group>
+ <field name="reason"/>
+ <field name="account_journal_id" attrs="{'invisible':[('property_valuation', '!=', 'real_time')], 'required': [('property_valuation', '=', 'real_time')]}"/>
+ </group>
+ <group>
+ <field name="account_id" attrs="{'invisible':[('property_valuation', '!=', 'real_time')], 'required': [('property_valuation', '=', 'real_time')]}"/>
+ <field name="date" attrs="{'invisible':[('property_valuation', '!=', 'real_time')]}"/>
+ </group>
+ </group>
+ </sheet>
+ <footer>
+ <button name="action_validate_revaluation" string="Revalue" type="object" class="btn-primary"/>
+ <button string="Cancel" class="btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+</odoo>