mirror of
https://github.com/bringout/oca-ocb-accounting.git
synced 2026-04-25 17:22:03 +02:00
19.0 vanilla
This commit is contained in:
parent
ba20ce7443
commit
768b70e05e
2357 changed files with 1057103 additions and 712486 deletions
|
|
@ -1,7 +1,4 @@
|
|||
# -*- 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_request_count
|
||||
from . import stock_valuation_layer_revaluation
|
||||
from . import stock_inventory_adjustment_name
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
from odoo import models, fields
|
||||
|
||||
|
||||
class StockInventoryAdjustmentName(models.TransientModel):
|
||||
_inherit = 'stock.inventory.adjustment.name'
|
||||
|
||||
accounting_date = fields.Date(
|
||||
'Accounting Date',
|
||||
help="Date at which the accounting entries will be created"
|
||||
" in case of automated inventory valuation."
|
||||
" If empty, the inventory date will be used.")
|
||||
should_show_accounting_date = fields.Boolean(compute='_compute_should_show_accounting_date')
|
||||
|
||||
def _compute_should_show_accounting_date(self):
|
||||
for wizard in self:
|
||||
wizard.should_show_accounting_date = any(product.valuation == 'real_time' for product in wizard.quant_ids.product_id)
|
||||
|
||||
def _get_quants_context(self):
|
||||
res = super()._get_quants_context()
|
||||
res['force_period_date'] = self.accounting_date
|
||||
return res
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="stock_inventory_adjustment_name_form_view_inherit_stock_account" model="ir.ui.view">
|
||||
<field name="name">stock.inventory.adjustment.name.form.view.inherit.stock.account</field>
|
||||
<field name="model">stock.inventory.adjustment.name</field>
|
||||
<field name="inherit_id" ref="stock.stock_inventory_adjustment_name_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='inventory_adjustment_name']" position="after">
|
||||
<field name="accounting_date" invisible="not should_show_accounting_date"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,27 +1,7 @@
|
|||
# -*- 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_ids:
|
||||
return_picking_line = self.product_return_moves.filtered(lambda r: r.move_id == move.origin_returned_move_id)[:1]
|
||||
if return_picking_line and return_picking_line.to_refund:
|
||||
move.to_refund = True
|
||||
return new_picking_id, pick_type_id
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockReturnPickingLine(models.TransientModel):
|
||||
|
|
@ -29,3 +9,8 @@ class StockReturnPickingLine(models.TransientModel):
|
|||
|
||||
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')
|
||||
|
||||
def _prepare_move_default_values(self, new_picking):
|
||||
vals = super()._prepare_move_default_values(new_picking)
|
||||
vals['to_refund'] = self.to_refund
|
||||
return vals
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
# 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()
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_stock_quantity_history_inherit_stock_account" model="ir.ui.view">
|
||||
<field name="name">Valuation Report at Date</field>
|
||||
<field name="model">stock.quantity.history</field>
|
||||
<field name="inherit_id" ref="stock.view_stock_quantity_history"></field>
|
||||
<field name="arch" type="xml">
|
||||
<!-- ensure string/description matches the model we're looking at the history of -->
|
||||
<field name="inventory_datetime" position="attributes">
|
||||
<attribute name="invisible">
|
||||
context.get('active_model') != 'stock.quant'
|
||||
</attribute>
|
||||
</field>
|
||||
<field name="inventory_datetime" position="after">
|
||||
<field name="inventory_datetime" string="Valuation at Date"
|
||||
help="Choose a date to get the valuation at that date"
|
||||
invisible="context.get('active_model') != 'stock.valuation.layer'"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockRequestCount(models.TransientModel):
|
||||
_inherit = 'stock.request.count'
|
||||
|
||||
accounting_date = fields.Date('Accounting Date')
|
||||
|
||||
def _get_values_to_write(self):
|
||||
res = super()._get_values_to_write()
|
||||
if self.accounting_date:
|
||||
res['accounting_date'] = self.accounting_date
|
||||
return res
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="stock_inventory_request_count_form_view_inherit_stock_account" model="ir.ui.view">
|
||||
<field name="name">stock.request.count.form.view.inherit.stock.account</field>
|
||||
<field name="model">stock.request.count</field>
|
||||
<field name="inherit_id" ref="stock.stock_inventory_request_count_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_id" position="after">
|
||||
<field name="accounting_date"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
# -*- 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_compare, 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):
|
||||
taken_remaining_value = remaining_value
|
||||
else:
|
||||
taken_remaining_value = remaining_value_unit_cost * svl.remaining_qty
|
||||
if float_compare(svl.remaining_value + taken_remaining_value, 0, precision_rounding=self.product_id.uom_id.rounding) < 0:
|
||||
raise UserError(_('The value of a stock valuation layer cannot be negative. Landed cost could be use to correct a specific transfer.'))
|
||||
|
||||
svl.remaining_value += taken_remaining_value
|
||||
remaining_value -= taken_remaining_value
|
||||
remaining_qty -= svl.remaining_qty
|
||||
|
||||
previous_value_svl = self.current_value_svl
|
||||
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 in ('average', 'fifo'):
|
||||
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=previous_value_svl,
|
||||
new_value=previous_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=previous_value_svl,
|
||||
new_value=previous_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
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<?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" class="oe_inline" widget="monetary"/> for <field name="current_quantity_svl" class="oe_inline"/> <field name="product_uom_name" class="oe_inline"/>
|
||||
</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" class="oe_inline"/> (<field name="new_value_by_qty" class="oe_inline ms-1"/> by <field name="product_uom_name" class="oe_inline me-1"/>)
|
||||
<small class="mx-2 fst-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" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Loading…
Add table
Add a link
Reference in a new issue