diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/stock_account/wizard | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/stock_account/wizard')
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> |
