mirror of
https://github.com/bringout/oca-ocb-accounting.git
synced 2026-04-25 05:42:03 +02:00
Initial commit: Accounting packages
This commit is contained in:
commit
4ef34c2317
2661 changed files with 1709616 additions and 0 deletions
|
|
@ -0,0 +1,5 @@
|
|||
from . import test_account_move
|
||||
from . import test_anglo_saxon_valuation_reconciliation_common
|
||||
from . import test_stockvaluation
|
||||
from . import test_stockvaluationlayer
|
||||
from . import test_stock_valuation_layer_revaluation
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.addons.stock_account.tests.test_stockvaluation import _create_accounting_data
|
||||
from odoo.tests.common import tagged, Form
|
||||
from odoo import fields, Command
|
||||
|
||||
class TestAccountMoveStockCommon(AccountTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
(
|
||||
cls.stock_input_account,
|
||||
cls.stock_output_account,
|
||||
cls.stock_valuation_account,
|
||||
cls.expense_account,
|
||||
cls.stock_journal,
|
||||
) = _create_accounting_data(cls.env)
|
||||
|
||||
# `all_categ` should not be altered, so we can test the `post_init` hook of `stock_account`
|
||||
cls.all_categ = cls.env.ref('product.product_category_all')
|
||||
|
||||
cls.auto_categ = cls.env['product.category'].create({
|
||||
'name': 'child_category',
|
||||
'parent_id': cls.all_categ.id,
|
||||
"property_stock_account_input_categ_id": cls.stock_input_account.id,
|
||||
"property_stock_account_output_categ_id": cls.stock_output_account.id,
|
||||
"property_stock_valuation_account_id": cls.stock_valuation_account.id,
|
||||
"property_stock_journal": cls.stock_journal.id,
|
||||
"property_valuation": "real_time",
|
||||
"property_cost_method": "standard",
|
||||
})
|
||||
cls.product_A = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Product A",
|
||||
"type": "product",
|
||||
"default_code": "prda",
|
||||
"categ_id": cls.auto_categ.id,
|
||||
"taxes_id": [(5, 0, 0)],
|
||||
"supplier_taxes_id": [(5, 0, 0)],
|
||||
"lst_price": 100.0,
|
||||
"standard_price": 10.0,
|
||||
"property_account_income_id": cls.company_data["default_account_revenue"].id,
|
||||
"property_account_expense_id": cls.company_data["default_account_expense"].id,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestAccountMove(TestAccountMoveStockCommon):
|
||||
def test_standard_perpetual_01_mc_01(self):
|
||||
rate = self.currency_data["rates"].sorted()[0].rate
|
||||
|
||||
move_form = Form(self.env["account.move"].with_context(default_move_type="out_invoice"))
|
||||
move_form.partner_id = self.partner_a
|
||||
move_form.currency_id = self.currency_data["currency"]
|
||||
with move_form.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_A
|
||||
line_form.tax_ids.clear()
|
||||
invoice = move_form.save()
|
||||
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_total)
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 1)
|
||||
|
||||
invoice._post()
|
||||
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_total)
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_residual)
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 4)
|
||||
self.assertEqual(len(invoice.mapped("line_ids").filtered(lambda l: l.display_type == 'cogs')), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 2)
|
||||
|
||||
def test_fifo_perpetual_01_mc_01(self):
|
||||
self.product_A.categ_id.property_cost_method = "fifo"
|
||||
rate = self.currency_data["rates"].sorted()[0].rate
|
||||
|
||||
move_form = Form(self.env["account.move"].with_context(default_move_type="out_invoice"))
|
||||
move_form.partner_id = self.partner_a
|
||||
move_form.currency_id = self.currency_data["currency"]
|
||||
with move_form.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_A
|
||||
line_form.tax_ids.clear()
|
||||
invoice = move_form.save()
|
||||
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_total)
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 1)
|
||||
|
||||
invoice._post()
|
||||
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_total)
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_residual)
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 4)
|
||||
self.assertEqual(len(invoice.mapped("line_ids").filtered(lambda l: l.display_type == 'cogs')), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 2)
|
||||
|
||||
def test_average_perpetual_01_mc_01(self):
|
||||
self.product_A.categ_id.property_cost_method = "average"
|
||||
rate = self.currency_data["rates"].sorted()[0].rate
|
||||
|
||||
move_form = Form(self.env["account.move"].with_context(default_move_type="out_invoice"))
|
||||
move_form.partner_id = self.partner_a
|
||||
move_form.currency_id = self.currency_data["currency"]
|
||||
with move_form.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_A
|
||||
line_form.tax_ids.clear()
|
||||
invoice = move_form.save()
|
||||
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_total)
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 1)
|
||||
|
||||
invoice._post()
|
||||
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_total)
|
||||
self.assertAlmostEqual(self.product_A.lst_price * rate, invoice.amount_residual)
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 4)
|
||||
self.assertEqual(len(invoice.mapped("line_ids").filtered(lambda l: l.display_type == 'cogs')), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 2)
|
||||
|
||||
def test_storno_accounting(self):
|
||||
"""Storno accounting uses negative numbers on debit/credit to cancel other moves.
|
||||
This test checks that we do the same for the anglosaxon lines when storno is enabled.
|
||||
"""
|
||||
self.env.company.account_storno = True
|
||||
self.env.company.anglo_saxon_accounting = True
|
||||
|
||||
move = self.env['account.move'].create({
|
||||
'move_type': 'out_refund',
|
||||
'invoice_date': fields.Date.from_string('2019-01-01'),
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'invoice_line_ids': [
|
||||
(0, None, {'product_id': self.product_A.id}),
|
||||
]
|
||||
})
|
||||
move.action_post()
|
||||
|
||||
stock_output_line = move.line_ids.filtered(lambda l: l.account_id == self.stock_output_account)
|
||||
self.assertEqual(stock_output_line.debit, 0)
|
||||
self.assertEqual(stock_output_line.credit, -10)
|
||||
|
||||
expense_line = move.line_ids.filtered(lambda l: l.account_id == self.product_A.property_account_expense_id)
|
||||
self.assertEqual(expense_line.debit, -10)
|
||||
self.assertEqual(expense_line.credit, 0)
|
||||
|
||||
def test_standard_manual_tax_edit(self):
|
||||
''' Test manually editing tax amount, cogs creation should not reset tax amount '''
|
||||
move_form = Form(self.env["account.move"].with_context(default_move_type="out_invoice"))
|
||||
move_form.partner_id = self.partner_a
|
||||
with move_form.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_A
|
||||
invoice = move_form.save()
|
||||
|
||||
self.assertEqual(invoice.amount_total, 115)
|
||||
self.assertEqual(invoice.amount_untaxed, 100)
|
||||
self.assertEqual(invoice.amount_tax, 15)
|
||||
|
||||
# simulate manual tax edit via widget
|
||||
vals = {
|
||||
'tax_totals': {
|
||||
'amount_untaxed': 100,
|
||||
'amount_total': 114,
|
||||
'formatted_amount_total': '$\xa0114.00',
|
||||
'formatted_amount_untaxed': '$\xa0100.00',
|
||||
'groups_by_subtotal': {
|
||||
'Untaxed Amount': [{
|
||||
'group_key': 2,
|
||||
'tax_group_id': 2,
|
||||
'tax_group_name': 'Tax 15%',
|
||||
'tax_group_amount': 14,
|
||||
'tax_group_base_amount': 100,
|
||||
'formatted_tax_group_amount': '$\xa014.00',
|
||||
'formatted_tax_group_base_amount': '$\xa0100.00'
|
||||
}]
|
||||
},
|
||||
'subtotals': [{
|
||||
'name': 'Untaxed Amount',
|
||||
'amount': 100,
|
||||
'formatted_amount': '$\xa0100.00'
|
||||
}],
|
||||
'subtotals_order': ['Untaxed Amount'],
|
||||
'display_tax_base': False,
|
||||
}
|
||||
}
|
||||
invoice.write(vals)
|
||||
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 3)
|
||||
self.assertAlmostEqual(114.0, invoice.amount_total)
|
||||
|
||||
invoice._post()
|
||||
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 5)
|
||||
self.assertEqual(invoice.amount_total, 114)
|
||||
self.assertEqual(invoice.amount_untaxed, 100)
|
||||
self.assertEqual(invoice.amount_tax, 14)
|
||||
|
||||
def test_basic_bill(self):
|
||||
"""
|
||||
When billing a storable product with a basic category (manual
|
||||
valuation), the account used should be the expenses one. This test
|
||||
checks the flow with two companies:
|
||||
- One that existed before the installation of `stock_account` (to test
|
||||
the post-install hook)
|
||||
- One created after the module installation
|
||||
"""
|
||||
first_company = self.env['res.company'].browse(1)
|
||||
self.env.user.company_ids |= first_company
|
||||
basic_product = self.env['product.product'].create({
|
||||
'name': 'SuperProduct',
|
||||
'type': 'product',
|
||||
'categ_id': self.all_categ.id,
|
||||
})
|
||||
|
||||
for company in (self.env.company | first_company):
|
||||
bill_form = Form(self.env['account.move'].with_company(company.id).with_context(default_move_type='in_invoice'))
|
||||
bill_form.partner_id = self.partner_a
|
||||
bill_form.invoice_date = fields.Date.today()
|
||||
with bill_form.invoice_line_ids.new() as line:
|
||||
line.product_id = basic_product
|
||||
line.price_unit = 100
|
||||
bill = bill_form.save()
|
||||
bill.action_post()
|
||||
|
||||
product_accounts = basic_product.product_tmpl_id.with_company(company.id).get_product_accounts()
|
||||
self.assertEqual(bill.invoice_line_ids.account_id, product_accounts['expense'])
|
||||
|
||||
def test_product_valuation_method_change_to_automated_negative_on_hand_qty(self):
|
||||
"""
|
||||
We have a product whose category has manual valuation and on-hand quantity is negative:
|
||||
Upon switching to an automated valuation method for the product category, the following
|
||||
entries should be generated in the stock journal:
|
||||
1. CREDIT to valuation account
|
||||
2. DEBIT to stock output account
|
||||
"""
|
||||
stock_location = self.env['stock.warehouse'].search([
|
||||
('company_id', '=', self.env.company.id),
|
||||
], limit=1).lot_stock_id
|
||||
categ = self.env['product.category'].create({'name': 'categ'})
|
||||
product = self.product_a
|
||||
product.write({
|
||||
'type': 'product',
|
||||
'categ_id': categ.id,
|
||||
})
|
||||
|
||||
out_picking = self.env['stock.picking'].create({
|
||||
'location_id': stock_location.id,
|
||||
'location_dest_id': self.ref('stock.stock_location_customers'),
|
||||
'picking_type_id': stock_location.warehouse_id.out_type_id.id,
|
||||
})
|
||||
sm = self.env['stock.move'].create({
|
||||
'name': product.name,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': 1,
|
||||
'product_uom': product.uom_id.id,
|
||||
'location_id': out_picking.location_id.id,
|
||||
'location_dest_id': out_picking.location_dest_id.id,
|
||||
'picking_id': out_picking.id,
|
||||
})
|
||||
out_picking.action_confirm()
|
||||
sm.quantity_done = 1
|
||||
out_picking.button_validate()
|
||||
|
||||
categ.write({
|
||||
'property_valuation': 'real_time',
|
||||
'property_stock_account_input_categ_id': self.stock_input_account.id,
|
||||
'property_stock_account_output_categ_id': self.stock_output_account.id,
|
||||
'property_stock_valuation_account_id': self.stock_valuation_account.id,
|
||||
'property_stock_journal': self.stock_journal.id,
|
||||
})
|
||||
|
||||
amls = self.env['account.move.line'].search([('product_id', '=', product.id)])
|
||||
if amls[0].account_id == self.stock_valuation_account:
|
||||
stock_valuation_line = amls[0]
|
||||
output_line = amls[1]
|
||||
else:
|
||||
output_line = amls[0]
|
||||
stock_valuation_line = amls[1]
|
||||
|
||||
expected_valuation_line = {
|
||||
'account_id': self.stock_valuation_account.id,
|
||||
'credit': product.standard_price,
|
||||
'debit': 0,
|
||||
}
|
||||
expected_output_line = {
|
||||
'account_id': self.stock_output_account.id,
|
||||
'credit': 0,
|
||||
'debit': product.standard_price,
|
||||
}
|
||||
self.assertRecordValues(
|
||||
[stock_valuation_line, output_line],
|
||||
[expected_valuation_line, expected_output_line]
|
||||
)
|
||||
|
||||
def test_cogs_analytic_accounting(self):
|
||||
"""Check analytic distribution is correctly propagated to COGS lines"""
|
||||
self.env.company.anglo_saxon_accounting = True
|
||||
default_plan = self.env['account.analytic.plan'].create({'name': 'Default', 'company_id': False})
|
||||
analytic_account = self.env['account.analytic.account'].create({'name': 'Account 1', 'plan_id': default_plan.id})
|
||||
|
||||
move = self.env['account.move'].create({
|
||||
'move_type': 'out_refund',
|
||||
'invoice_date': fields.Date.from_string('2019-01-01'),
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': self.product_A.id,
|
||||
'analytic_distribution': {
|
||||
analytic_account.id: 100,
|
||||
},
|
||||
}),
|
||||
]
|
||||
})
|
||||
move.action_post()
|
||||
|
||||
cogs_line = move.line_ids.filtered(lambda l: l.account_id == self.product_A.property_account_expense_id)
|
||||
self.assertEqual(cogs_line.analytic_distribution, {str(analytic_account.id): 100})
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
from odoo import fields
|
||||
|
||||
|
||||
class ValuationReconciliationTestCommon(AccountTestInvoicingCommon):
|
||||
""" Base class for tests checking interim accounts reconciliation works
|
||||
in anglosaxon accounting. It sets up everything we need in the tests, and is
|
||||
extended in both sale_stock and purchase modules to run the 'true' tests.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
cls.stock_account_product_categ = cls.env['product.category'].create({
|
||||
'name': 'Test category',
|
||||
'property_valuation': 'real_time',
|
||||
'property_cost_method': 'fifo',
|
||||
'property_stock_valuation_account_id': cls.company_data['default_account_stock_valuation'].id,
|
||||
'property_stock_account_input_categ_id': cls.company_data['default_account_stock_in'].id,
|
||||
'property_stock_account_output_categ_id': cls.company_data['default_account_stock_out'].id,
|
||||
})
|
||||
|
||||
uom_unit = cls.env.ref('uom.product_uom_unit')
|
||||
|
||||
cls.test_product_order = cls.env['product.product'].create({
|
||||
'name': "Test product template invoiced on order",
|
||||
'standard_price': 42.0,
|
||||
'type': 'product',
|
||||
'categ_id': cls.stock_account_product_categ.id,
|
||||
'uom_id': uom_unit.id,
|
||||
'uom_po_id': uom_unit.id,
|
||||
})
|
||||
cls.test_product_delivery = cls.env['product.product'].create({
|
||||
'name': 'Test product template invoiced on delivery',
|
||||
'standard_price': 42.0,
|
||||
'type': 'product',
|
||||
'categ_id': cls.stock_account_product_categ.id,
|
||||
'uom_id': uom_unit.id,
|
||||
'uom_po_id': uom_unit.id,
|
||||
})
|
||||
|
||||
cls.res_users_stock_user = cls.env['res.users'].create({
|
||||
'name': "Inventory User",
|
||||
'login': "su",
|
||||
'email': "stockuser@yourcompany.com",
|
||||
'groups_id': [(6, 0, [cls.env.ref('stock.group_stock_user').id])],
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def setup_company_data(cls, company_name, chart_template=None, **kwargs):
|
||||
company_data = super().setup_company_data(company_name, chart_template=chart_template, **kwargs)
|
||||
|
||||
# Create stock config.
|
||||
company_data.update({
|
||||
'default_account_stock_in': cls.env['account.account'].create({
|
||||
'name': 'default_account_stock_in',
|
||||
'code': 'STOCKIN',
|
||||
'reconcile': True,
|
||||
'account_type': 'asset_current',
|
||||
'company_id': company_data['company'].id,
|
||||
}),
|
||||
'default_account_stock_out': cls.env['account.account'].create({
|
||||
'name': 'default_account_stock_out',
|
||||
'code': 'STOCKOUT',
|
||||
'reconcile': True,
|
||||
'account_type': 'asset_current',
|
||||
'company_id': company_data['company'].id,
|
||||
}),
|
||||
'default_account_stock_valuation': cls.env['account.account'].create({
|
||||
'name': 'default_account_stock_valuation',
|
||||
'code': 'STOCKVAL',
|
||||
'reconcile': True,
|
||||
'account_type': 'asset_current',
|
||||
'company_id': company_data['company'].id,
|
||||
}),
|
||||
'default_warehouse': cls.env['stock.warehouse'].search(
|
||||
[('company_id', '=', company_data['company'].id)],
|
||||
limit=1,
|
||||
),
|
||||
})
|
||||
return company_data
|
||||
|
||||
def check_reconciliation(self, invoice, picking, full_reconcile=True, operation='purchase'):
|
||||
interim_account_id = self.company_data['default_account_stock_in'].id if operation == 'purchase' else self.company_data['default_account_stock_out'].id
|
||||
invoice_line = invoice.line_ids.filtered(lambda line: line.account_id.id == interim_account_id)
|
||||
|
||||
stock_moves = picking.move_ids
|
||||
|
||||
valuation_line = stock_moves.mapped('account_move_ids.line_ids').filtered(lambda x: x.account_id.id == interim_account_id)
|
||||
|
||||
if invoice.is_purchase_document() and any(l.display_type == 'cogs' for l in invoice_line):
|
||||
self.assertEqual(len(invoice_line), 2, "Only two line2 should have been written by invoice in stock input account")
|
||||
self.assertTrue(all(vl.reconciled for vl in valuation_line) or invoice_line[0].reconciled or invoice_line[1].reconciled, "The valuation and invoice line should have been reconciled together.")
|
||||
else:
|
||||
self.assertEqual(len(invoice_line), 1, "Only one line should have been written by invoice in stock input account")
|
||||
self.assertTrue(all(vl.reconciled for vl in valuation_line) or invoice_line.reconciled, "The valuation and invoice line should have been reconciled together.")
|
||||
|
||||
if invoice.move_type not in ('out_refund', 'in_refund'):
|
||||
# self.assertEqual(len(valuation_line), 1, "Only one line should have been written for stock valuation in stock input account")
|
||||
|
||||
if full_reconcile:
|
||||
self.assertTrue(all(vl.full_reconcile_id for vl in valuation_line), "The reconciliation should be total at that point.")
|
||||
else:
|
||||
self.assertFalse(all(vl.full_reconcile_id for vl in valuation_line), "The reconciliation should not be total at that point.")
|
||||
|
||||
def _process_pickings(self, pickings, date=False, quantity=False):
|
||||
|
||||
def do_picking():
|
||||
pickings.action_confirm()
|
||||
pickings.action_assign()
|
||||
for picking in pickings:
|
||||
for ml in picking.move_line_ids:
|
||||
ml.qty_done = quantity or ml.reserved_qty
|
||||
pickings._action_done()
|
||||
|
||||
if not date:
|
||||
date = fields.Date.today()
|
||||
do_picking()
|
||||
return
|
||||
with freeze_time(date):
|
||||
do_picking()
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import Form
|
||||
from odoo.addons.stock_account.tests.test_stockvaluation import _create_accounting_data
|
||||
from odoo.addons.stock_account.tests.test_stockvaluationlayer import TestStockValuationCommon
|
||||
|
||||
|
||||
class TestStockValuationLayerRevaluation(TestStockValuationCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestStockValuationLayerRevaluation, cls).setUpClass()
|
||||
cls.stock_input_account, cls.stock_output_account, cls.stock_valuation_account, cls.expense_account, cls.stock_journal = _create_accounting_data(cls.env)
|
||||
cls.product1.write({
|
||||
'property_account_expense_id': cls.expense_account.id,
|
||||
})
|
||||
cls.product1.categ_id.write({
|
||||
'property_valuation': 'real_time',
|
||||
'property_stock_account_input_categ_id': cls.stock_input_account.id,
|
||||
'property_stock_account_output_categ_id': cls.stock_output_account.id,
|
||||
'property_stock_valuation_account_id': cls.stock_valuation_account.id,
|
||||
'property_stock_journal': cls.stock_journal.id,
|
||||
})
|
||||
|
||||
cls.product1.categ_id.property_valuation = 'real_time'
|
||||
|
||||
def test_stock_valuation_layer_revaluation_avco(self):
|
||||
self.product1.categ_id.property_cost_method = 'average'
|
||||
context = {
|
||||
'default_product_id': self.product1.id,
|
||||
'default_company_id': self.env.company.id,
|
||||
'default_added_value': 0.0
|
||||
}
|
||||
# Quantity of product1 is zero, raise
|
||||
with self.assertRaises(UserError):
|
||||
Form(self.env['stock.valuation.layer.revaluation'].with_context(context)).save()
|
||||
|
||||
self._make_in_move(self.product1, 10, unit_cost=2)
|
||||
self._make_in_move(self.product1, 10, unit_cost=4)
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 3)
|
||||
self.assertEqual(self.product1.quantity_svl, 20)
|
||||
|
||||
old_layers = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)], order="create_date desc, id desc")
|
||||
|
||||
self.assertEqual(len(old_layers), 2)
|
||||
self.assertEqual(old_layers[0].remaining_value, 40)
|
||||
|
||||
revaluation_wizard = Form(self.env['stock.valuation.layer.revaluation'].with_context(context))
|
||||
revaluation_wizard.added_value = 20
|
||||
revaluation_wizard.account_id = self.stock_valuation_account
|
||||
revaluation_wizard.save().action_validate_revaluation()
|
||||
|
||||
# Check standard price change
|
||||
self.assertEqual(self.product1.standard_price, 4)
|
||||
self.assertEqual(self.product1.quantity_svl, 20)
|
||||
|
||||
# Check the creation of stock.valuation.layer
|
||||
new_layer = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)], order="create_date desc, id desc", limit=1)
|
||||
self.assertEqual(new_layer.value, 20)
|
||||
|
||||
# Check the remaing value of current layers
|
||||
self.assertEqual(old_layers[0].remaining_value, 50)
|
||||
self.assertEqual(sum(slv.remaining_value for slv in old_layers), 80)
|
||||
|
||||
# Check account move
|
||||
self.assertTrue(bool(new_layer.account_move_id))
|
||||
self.assertEqual(len(new_layer.account_move_id.line_ids), 2)
|
||||
|
||||
self.assertEqual(sum(new_layer.account_move_id.line_ids.mapped("debit")), 20)
|
||||
self.assertEqual(sum(new_layer.account_move_id.line_ids.mapped("credit")), 20)
|
||||
|
||||
credit_lines = [l for l in new_layer.account_move_id.line_ids if l.credit > 0]
|
||||
self.assertEqual(len(credit_lines), 1)
|
||||
self.assertEqual(credit_lines[0].account_id.id, self.stock_valuation_account.id)
|
||||
|
||||
def test_stock_valuation_layer_revaluation_avco_rounding(self):
|
||||
self.product1.categ_id.property_cost_method = 'average'
|
||||
context = {
|
||||
'default_product_id': self.product1.id,
|
||||
'default_company_id': self.env.company.id,
|
||||
'default_added_value': 0.0
|
||||
}
|
||||
# Quantity of product1 is zero, raise
|
||||
with self.assertRaises(UserError):
|
||||
Form(self.env['stock.valuation.layer.revaluation'].with_context(context)).save()
|
||||
|
||||
self._make_in_move(self.product1, 1, unit_cost=1)
|
||||
self._make_in_move(self.product1, 1, unit_cost=1)
|
||||
self._make_in_move(self.product1, 1, unit_cost=1)
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 1)
|
||||
self.assertEqual(self.product1.quantity_svl, 3)
|
||||
|
||||
old_layers = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)], order="create_date desc, id desc")
|
||||
|
||||
self.assertEqual(len(old_layers), 3)
|
||||
self.assertEqual(old_layers[0].remaining_value, 1)
|
||||
|
||||
revaluation_wizard = Form(self.env['stock.valuation.layer.revaluation'].with_context(context))
|
||||
revaluation_wizard.added_value = 1
|
||||
revaluation_wizard.account_id = self.stock_valuation_account
|
||||
revaluation_wizard.save().action_validate_revaluation()
|
||||
|
||||
# Check standard price change
|
||||
self.assertEqual(self.product1.standard_price, 1.33)
|
||||
self.assertEqual(self.product1.quantity_svl, 3)
|
||||
|
||||
# Check the creation of stock.valuation.layer
|
||||
new_layer = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)], order="create_date desc, id desc", limit=1)
|
||||
self.assertEqual(new_layer.value, 1)
|
||||
|
||||
# Check the remaing value of current layers
|
||||
self.assertEqual(sum(slv.remaining_value for slv in old_layers), 4)
|
||||
self.assertTrue(1.34 in old_layers.mapped("remaining_value"))
|
||||
|
||||
# Check account move
|
||||
self.assertTrue(bool(new_layer.account_move_id))
|
||||
self.assertEqual(len(new_layer.account_move_id.line_ids), 2)
|
||||
|
||||
self.assertEqual(sum(new_layer.account_move_id.line_ids.mapped("debit")), 1)
|
||||
self.assertEqual(sum(new_layer.account_move_id.line_ids.mapped("credit")), 1)
|
||||
|
||||
credit_lines = [l for l in new_layer.account_move_id.line_ids if l.credit > 0]
|
||||
self.assertEqual(len(credit_lines), 1)
|
||||
self.assertEqual(credit_lines[0].account_id.id, self.stock_valuation_account.id)
|
||||
|
||||
def test_stock_valuation_layer_revaluation_avco_rounding_2_digits(self):
|
||||
"""
|
||||
Check that the rounding of the new price (cost) is equivalent to the rounding of the standard price (cost)
|
||||
The check is done indirectly via the layers valuations.
|
||||
If correct => rounding method is correct too
|
||||
"""
|
||||
self.product1.categ_id.property_cost_method = 'average'
|
||||
|
||||
self.env['decimal.precision'].search([
|
||||
('name', '=', 'Product Price'),
|
||||
]).digits = 2
|
||||
self.product1.write({'standard_price': 0})
|
||||
|
||||
# First Move
|
||||
self.product1.write({'standard_price': 0.022})
|
||||
self._make_in_move(self.product1, 10000)
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 0.02)
|
||||
self.assertEqual(self.product1.quantity_svl, 10000)
|
||||
|
||||
layer = self.product1.stock_valuation_layer_ids
|
||||
self.assertEqual(layer.value, 200)
|
||||
|
||||
# Second Move
|
||||
self.product1.write({'standard_price': 0.053})
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 0.05)
|
||||
self.assertEqual(self.product1.quantity_svl, 10000)
|
||||
|
||||
layers = self.product1.stock_valuation_layer_ids
|
||||
self.assertEqual(layers[0].value, 200)
|
||||
self.assertEqual(layers[1].value, 300)
|
||||
|
||||
def test_stock_valuation_layer_revaluation_avco_rounding_5_digits(self):
|
||||
"""
|
||||
Check that the rounding of the new price (cost) is equivalent to the rounding of the standard price (cost)
|
||||
The check is done indirectly via the layers valuations.
|
||||
If correct => rounding method is correct too
|
||||
"""
|
||||
self.product1.categ_id.property_cost_method = 'average'
|
||||
|
||||
self.env['decimal.precision'].search([
|
||||
('name', '=', 'Product Price'),
|
||||
]).digits = 5
|
||||
|
||||
# First Move
|
||||
self.product1.write({'standard_price': 0.00875})
|
||||
self._make_in_move(self.product1, 10000)
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 0.00875)
|
||||
self.assertEqual(self.product1.quantity_svl, 10000)
|
||||
|
||||
layer = self.product1.stock_valuation_layer_ids
|
||||
self.assertEqual(layer.value, 87.5)
|
||||
|
||||
# Second Move
|
||||
self.product1.write({'standard_price': 0.00975})
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 0.00975)
|
||||
self.assertEqual(self.product1.quantity_svl, 10000)
|
||||
|
||||
layers = self.product1.stock_valuation_layer_ids
|
||||
self.assertEqual(layers[0].value, 87.5)
|
||||
self.assertEqual(layers[1].value, 10)
|
||||
|
||||
def test_stock_valuation_layer_revaluation_fifo(self):
|
||||
self.product1.categ_id.property_cost_method = 'fifo'
|
||||
context = {
|
||||
'default_product_id': self.product1.id,
|
||||
'default_company_id': self.env.company.id,
|
||||
'default_added_value': 0.0
|
||||
}
|
||||
# Quantity of product1 is zero, raise
|
||||
with self.assertRaises(UserError):
|
||||
Form(self.env['stock.valuation.layer.revaluation'].with_context(context)).save()
|
||||
|
||||
self._make_in_move(self.product1, 10, unit_cost=2)
|
||||
self._make_in_move(self.product1, 10, unit_cost=4)
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 2)
|
||||
self.assertEqual(self.product1.quantity_svl, 20)
|
||||
|
||||
old_layers = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)], order="create_date desc, id desc")
|
||||
|
||||
self.assertEqual(len(old_layers), 2)
|
||||
self.assertEqual(old_layers[0].remaining_value, 40)
|
||||
|
||||
revaluation_wizard = Form(self.env['stock.valuation.layer.revaluation'].with_context(context))
|
||||
revaluation_wizard.added_value = 20
|
||||
revaluation_wizard.account_id = self.stock_valuation_account
|
||||
revaluation_wizard.save().action_validate_revaluation()
|
||||
|
||||
self.assertEqual(self.product1.standard_price, 3)
|
||||
|
||||
# Check the creation of stock.valuation.layer
|
||||
new_layer = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)], order="create_date desc, id desc", limit=1)
|
||||
self.assertEqual(new_layer.value, 20)
|
||||
|
||||
# Check the remaing value of current layers
|
||||
self.assertEqual(old_layers[0].remaining_value, 50)
|
||||
self.assertEqual(sum(slv.remaining_value for slv in old_layers), 80)
|
||||
|
||||
# Check account move
|
||||
self.assertTrue(bool(new_layer.account_move_id))
|
||||
self.assertTrue(len(new_layer.account_move_id.line_ids), 2)
|
||||
|
||||
self.assertEqual(sum(new_layer.account_move_id.line_ids.mapped("debit")), 20)
|
||||
self.assertEqual(sum(new_layer.account_move_id.line_ids.mapped("credit")), 20)
|
||||
|
||||
credit_lines = [l for l in new_layer.account_move_id.line_ids if l.credit > 0]
|
||||
self.assertEqual(len(credit_lines), 1)
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue