1730 lines
77 KiB
Python
1730 lines
77 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo.tests import Form, tagged
|
||
|
from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
|
||
|
from odoo.exceptions import UserError
|
||
|
|
||
|
|
||
|
@tagged('post_install', '-at_install')
|
||
|
class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls, chart_template_ref=None):
|
||
|
super().setUpClass(chart_template_ref=chart_template_ref)
|
||
|
|
||
|
cls.env.user.company_id.anglo_saxon_accounting = True
|
||
|
|
||
|
cls.product = cls.env['product.product'].create({
|
||
|
'name': 'product',
|
||
|
'type': 'product',
|
||
|
'categ_id': cls.stock_account_product_categ.id,
|
||
|
})
|
||
|
|
||
|
def _inv_adj_two_units(self):
|
||
|
self.env['stock.quant'].with_context(inventory_mode=True).create({
|
||
|
'product_id': self.product.id, # tracking serial
|
||
|
'inventory_quantity': 2,
|
||
|
'location_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
}).action_apply_inventory()
|
||
|
|
||
|
def _so_and_confirm_two_units(self):
|
||
|
sale_order = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 2.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 12,
|
||
|
'tax_id': False, # no love taxes amls
|
||
|
})],
|
||
|
})
|
||
|
sale_order.flush_recordset()
|
||
|
sale_order.action_confirm()
|
||
|
return sale_order
|
||
|
|
||
|
def _fifo_in_one_eight_one_ten(self):
|
||
|
# Put two items in stock.
|
||
|
in_move_1 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': 8,
|
||
|
})
|
||
|
in_move_1._action_confirm()
|
||
|
in_move_1.write({'quantity': 1, 'picked': True})
|
||
|
in_move_1._action_done()
|
||
|
in_move_2 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': 10,
|
||
|
})
|
||
|
in_move_2._action_confirm()
|
||
|
in_move_2.write({'quantity': 1, 'picked': True})
|
||
|
in_move_2._action_done()
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Standard Ordered
|
||
|
# -------------------------------------------------------------------------
|
||
|
def test_standard_ordered_invoice_pre_delivery(self):
|
||
|
"""Standard price set to 10. Get 2 units in stock. Sale order 2@12. Standard price set
|
||
|
to 14. Invoice 2 without delivering. The amount in Stock OUT and COGS should be 14*2.
|
||
|
"""
|
||
|
self.product.categ_id.property_cost_method = 'standard'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
self.product.standard_price = 10.0
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# standard price to 14
|
||
|
self.product.standard_price = 14.0
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 28)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 28)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_standard_ordered_invoice_post_partial_delivery_1(self):
|
||
|
"""Standard price set to 10. Get 2 units in stock. Sale order 2@12. Deliver 1, invoice 1,
|
||
|
change the standard price to 14, deliver one, change the standard price to 16, invoice 1.
|
||
|
The amounts used in Stock OUT and COGS should be 10 then 14."""
|
||
|
self.product.categ_id.property_cost_method = 'standard'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
self.product.standard_price = 10.0
|
||
|
|
||
|
# Put two items in stock.
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# Invoice 1
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice_form = Form(invoice)
|
||
|
with invoice_form.invoice_line_ids.edit(0) as invoice_line:
|
||
|
invoice_line.quantity = 1
|
||
|
invoice_form.save()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 10)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 10)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 12)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 12)
|
||
|
|
||
|
# change the standard price to 14
|
||
|
self.product.standard_price = 14.0
|
||
|
|
||
|
# deliver the backorder
|
||
|
sale_order.picking_ids[0].move_ids.write({'quantity': 1, 'picked': True})
|
||
|
sale_order.picking_ids[0].button_validate()
|
||
|
|
||
|
# change the standard price to 16
|
||
|
self.product.standard_price = 16.0
|
||
|
|
||
|
# invoice 1
|
||
|
invoice2 = sale_order._create_invoices()
|
||
|
invoice2.action_post()
|
||
|
amls = invoice2.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 14)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 14)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 12)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 12)
|
||
|
|
||
|
def test_standard_ordered_invoice_post_delivery(self):
|
||
|
"""Standard price set to 10. Get 2 units in stock. Sale order 2@12. Deliver 1, change the
|
||
|
standard price to 14, deliver one, invoice 2. The amounts used in Stock OUT and COGS should
|
||
|
be 12*2."""
|
||
|
self.product.categ_id.property_cost_method = 'standard'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# change the standard price to 14
|
||
|
self.product.standard_price = 14.0
|
||
|
|
||
|
# deliver the backorder
|
||
|
sale_order.picking_ids.filtered('backorder_id').move_ids.write({'quantity': 1, 'picked': True})
|
||
|
sale_order.picking_ids.filtered('backorder_id').button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 24)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 24)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Standard Delivered
|
||
|
# -------------------------------------------------------------------------
|
||
|
def test_standard_delivered_invoice_pre_delivery(self):
|
||
|
"""Not possible to invoice pre delivery."""
|
||
|
self.product.categ_id.property_cost_method = 'standard'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
# Nothing delivered = nothing to invoice.
|
||
|
with self.assertRaises(UserError):
|
||
|
sale_order._create_invoices()
|
||
|
|
||
|
def test_standard_delivered_invoice_post_partial_delivery(self):
|
||
|
"""Standard price set to 10. Get 2 units in stock. Sale order 2@12. Deliver 1, invoice 1,
|
||
|
change the standard price to 14, deliver one, change the standard price to 16, invoice 1.
|
||
|
The amounts used in Stock OUT and COGS should be 10 then 14."""
|
||
|
self.product.categ_id.property_cost_method = 'standard'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# Invoice 1
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice_form = Form(invoice)
|
||
|
with invoice_form.invoice_line_ids.edit(0) as invoice_line:
|
||
|
invoice_line.quantity = 1
|
||
|
invoice_form.save()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 10)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 10)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 12)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 12)
|
||
|
|
||
|
# change the standard price to 14
|
||
|
self.product.standard_price = 14.0
|
||
|
|
||
|
# deliver the backorder
|
||
|
sale_order.picking_ids[0].move_ids.write({'quantity': 1, 'picked': True})
|
||
|
sale_order.picking_ids[0].button_validate()
|
||
|
|
||
|
# change the standard price to 16
|
||
|
self.product.standard_price = 16.0
|
||
|
|
||
|
# invoice 1
|
||
|
invoice2 = sale_order._create_invoices()
|
||
|
invoice2.action_post()
|
||
|
amls = invoice2.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 14)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 14)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 12)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 12)
|
||
|
|
||
|
def test_standard_delivered_invoice_post_delivery(self):
|
||
|
"""Standard price set to 10. Get 2 units in stock. Sale order 2@12. Deliver 1, change the
|
||
|
standard price to 14, deliver one, invoice 2. The amounts used in Stock OUT and COGS should
|
||
|
be 12*2."""
|
||
|
self.product.categ_id.property_cost_method = 'standard'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# change the standard price to 14
|
||
|
self.product.standard_price = 14.0
|
||
|
|
||
|
# deliver the backorder
|
||
|
sale_order.picking_ids.filtered('backorder_id').move_ids.write({'quantity': 1, 'picked': True})
|
||
|
sale_order.picking_ids.filtered('backorder_id').button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 24)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 24)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# AVCO Ordered
|
||
|
# -------------------------------------------------------------------------
|
||
|
def test_avco_ordered_invoice_pre_delivery(self):
|
||
|
"""Standard price set to 10. Sale order 2@12. Invoice without delivering."""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 20)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 20)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_avco_ordered_invoice_post_partial_delivery(self):
|
||
|
"""Standard price set to 10. Sale order 2@12. Invoice after delivering 1."""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 20)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 20)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_avco_ordered_invoice_post_delivery(self):
|
||
|
"""Standard price set to 10. Sale order 2@12. Invoice after full delivery."""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 20)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 20)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_avco_ordered_return_and_receipt(self):
|
||
|
""" Sell and deliver some products before the user encodes the products receipt """
|
||
|
product = self.product
|
||
|
product.invoice_policy = 'order'
|
||
|
product.type = 'product'
|
||
|
product.categ_id.property_cost_method = 'average'
|
||
|
product.categ_id.property_valuation = 'real_time'
|
||
|
product.list_price = 100
|
||
|
product.standard_price = 50
|
||
|
|
||
|
so = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'partner_invoice_id': self.partner_a.id,
|
||
|
'partner_shipping_id': self.partner_a.id,
|
||
|
'order_line': [(0, 0, {
|
||
|
'name': product.name,
|
||
|
'product_id': product.id,
|
||
|
'product_uom_qty': 5.0,
|
||
|
'product_uom': product.uom_id.id,
|
||
|
'price_unit': product.list_price})],
|
||
|
})
|
||
|
so.action_confirm()
|
||
|
|
||
|
pick = so.picking_ids
|
||
|
pick.move_ids.write({'quantity': 5, 'picked': True})
|
||
|
pick.button_validate()
|
||
|
|
||
|
product.standard_price = 40
|
||
|
|
||
|
stock_return_picking_form = Form(self.env['stock.return.picking']
|
||
|
.with_context(active_ids=pick.ids, active_id=pick.sorted().ids[0], active_model='stock.picking'))
|
||
|
return_wiz = stock_return_picking_form.save()
|
||
|
return_wiz.product_return_moves.quantity = 1
|
||
|
return_wiz.product_return_moves.to_refund = False
|
||
|
res = return_wiz.create_returns()
|
||
|
|
||
|
return_pick = self.env['stock.picking'].browse(res['res_id'])
|
||
|
return_pick.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
return_pick.button_validate()
|
||
|
|
||
|
picking = self.env['stock.picking'].create({
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'picking_type_id': self.company_data['default_warehouse'].in_type_id.id,
|
||
|
})
|
||
|
# We don't set the price_unit so that the `standard_price` will be used (see _get_price_unit()):
|
||
|
self.env['stock.move'].create({
|
||
|
'name': 'test_immediate_validate_1',
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'picking_id': picking.id,
|
||
|
'product_id': product.id,
|
||
|
'product_uom': product.uom_id.id,
|
||
|
'quantity': 1,
|
||
|
'picked': True,
|
||
|
})
|
||
|
picking.button_validate()
|
||
|
|
||
|
invoice = so._create_invoices()
|
||
|
invoice.action_post()
|
||
|
self.assertEqual(invoice.state, 'posted')
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# AVCO Delivered
|
||
|
# -------------------------------------------------------------------------
|
||
|
def test_avco_delivered_invoice_pre_delivery(self):
|
||
|
"""Standard price set to 10. Sale order 2@12. Invoice without delivering. """
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
# Nothing delivered = nothing to invoice.
|
||
|
with self.assertRaises(UserError):
|
||
|
sale_order._create_invoices()
|
||
|
|
||
|
def test_avco_delivered_invoice_post_partial_delivery(self):
|
||
|
"""Standard price set to 10. Sale order 2@12. Invoice after delivering 1."""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 10)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 10)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 12)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 12)
|
||
|
|
||
|
def test_avco_delivered_invoice_post_delivery(self):
|
||
|
"""Standard price set to 10. Sale order 2@12. Invoice after full delivery."""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
# Put two items in stock.
|
||
|
self._inv_adj_two_units()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 20)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 20)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_avco_partially_owned_and_delivered_invoice_post_delivery(self):
|
||
|
"""
|
||
|
Standard price set to 10. Sale order 2@12. One of the delivered
|
||
|
products was owned by an external partner. Invoice after full delivery.
|
||
|
"""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
self.env['stock.quant']._update_available_quantity(self.product, self.company_data['default_warehouse'].lot_stock_id, 1, owner_id=self.partner_b)
|
||
|
self.env['stock.quant']._update_available_quantity(self.product, self.company_data['default_warehouse'].lot_stock_id, 1)
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
# Deliver both products (there should be two SML)
|
||
|
sale_order.picking_ids.move_line_ids.write({'quantity': 1, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice one by one
|
||
|
invoice01 = sale_order._create_invoices()
|
||
|
with Form(invoice01) as invoice_form:
|
||
|
with invoice_form.invoice_line_ids.edit(0) as line_form:
|
||
|
line_form.quantity = 1
|
||
|
invoice01.action_post()
|
||
|
|
||
|
invoice02 = sale_order._create_invoices()
|
||
|
invoice02.action_post()
|
||
|
|
||
|
# COGS should ignore the owned product
|
||
|
self.assertRecordValues(invoice01.line_ids, [
|
||
|
# pylint: disable=bad-whitespace
|
||
|
{'account_id': self.company_data['default_account_revenue'].id, 'debit': 0, 'credit': 12},
|
||
|
{'account_id': self.company_data['default_account_receivable'].id, 'debit': 12, 'credit': 0},
|
||
|
{'account_id': self.company_data['default_account_stock_out'].id, 'debit': 0, 'credit': 10},
|
||
|
{'account_id': self.company_data['default_account_expense'].id, 'debit': 10, 'credit': 0},
|
||
|
])
|
||
|
self.assertRecordValues(invoice02.line_ids, [
|
||
|
# pylint: disable=bad-whitespace
|
||
|
{'account_id': self.company_data['default_account_revenue'].id, 'debit': 0, 'credit': 12},
|
||
|
{'account_id': self.company_data['default_account_receivable'].id, 'debit': 12, 'credit': 0},
|
||
|
])
|
||
|
|
||
|
def test_avco_fully_owned_and_delivered_invoice_post_delivery(self):
|
||
|
"""
|
||
|
Standard price set to 10. Sale order 2@12. The products are owned by an
|
||
|
external partner. Invoice after full delivery.
|
||
|
"""
|
||
|
self.product.categ_id.property_cost_method = 'average'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
self.env['stock.quant']._update_available_quantity(self.product, self.company_data['default_warehouse'].lot_stock_id, 2, owner_id=self.partner_b)
|
||
|
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
sale_order.picking_ids.move_line_ids.write({'quantity': 2, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# COGS should not exist because the products are owned by an external partner
|
||
|
amls = invoice.line_ids
|
||
|
self.assertRecordValues(amls, [
|
||
|
# pylint: disable=bad-whitespace
|
||
|
{'account_id': self.company_data['default_account_revenue'].id, 'debit': 0, 'credit': 24},
|
||
|
{'account_id': self.company_data['default_account_receivable'].id, 'debit': 24, 'credit': 0},
|
||
|
])
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# FIFO Ordered
|
||
|
# -------------------------------------------------------------------------
|
||
|
def test_fifo_ordered_invoice_pre_delivery(self):
|
||
|
"""Receive at 8 then at 10. Sale order 2@12. Invoice without delivering.
|
||
|
As no standard price is set, the Stock OUT and COGS amounts are 0."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertAlmostEqual(stock_out_aml.credit, 18)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertAlmostEqual(cogs_aml.debit, 18)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_fifo_ordered_invoice_post_partial_delivery(self):
|
||
|
"""Receive 1@8, 1@10, so 2@12, standard price 12, deliver 1, invoice 2: the COGS amount
|
||
|
should be 20: 1 really delivered at 10 and the other valued at the standard price 10."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# upate the standard price to 12
|
||
|
self.product.standard_price = 12
|
||
|
|
||
|
# Invoice 2
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice_form = Form(invoice)
|
||
|
with invoice_form.invoice_line_ids.edit(0) as invoice_line:
|
||
|
invoice_line.quantity = 2
|
||
|
invoice_form.save()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 20)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 20)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_fifo_ordered_invoice_post_delivery(self):
|
||
|
"""Receive at 8 then at 10. Sale order 2@12. Invoice after delivering everything."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'order'
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 18)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 18)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# FIFO Delivered
|
||
|
# -------------------------------------------------------------------------
|
||
|
def test_fifo_delivered_invoice_pre_delivery(self):
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
# Nothing delivered = nothing to invoice.
|
||
|
with self.assertRaises(UserError):
|
||
|
invoice_id = sale_order._create_invoices()
|
||
|
|
||
|
def test_fifo_delivered_invoice_post_partial_delivery(self):
|
||
|
"""Receive 1@8, 1@10, so 2@12, standard price 12, deliver 1, invoice 2: the price used should be 10:
|
||
|
one at 8 and one at 10."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
wiz = sale_order.picking_ids.button_validate()
|
||
|
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
|
||
|
wiz.process()
|
||
|
|
||
|
# upate the standard price to 12
|
||
|
self.product.standard_price = 12
|
||
|
|
||
|
# Invoice 2
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice_form = Form(invoice)
|
||
|
with invoice_form.invoice_line_ids.edit(0) as invoice_line:
|
||
|
invoice_line.quantity = 2
|
||
|
invoice_form.save()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 20)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 20)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_fifo_delivered_invoice_post_delivery(self):
|
||
|
"""Receive at 8 then at 10. Sale order 2@12. Invoice after delivering everything."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 18)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 18)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 24)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 24)
|
||
|
|
||
|
def test_fifo_delivered_invoice_post_delivery_2(self):
|
||
|
"""Receive at 8 then at 10. Sale order 10@12 and deliver without receiving the 2 missing.
|
||
|
receive 2@12. Invoice."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
in_move_1 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 8,
|
||
|
'price_unit': 10,
|
||
|
})
|
||
|
in_move_1._action_confirm()
|
||
|
in_move_1.write({'quantity': 8, 'picked': True})
|
||
|
in_move_1._action_done()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 10.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 12,
|
||
|
'tax_id': False, # no love taxes amls
|
||
|
})],
|
||
|
})
|
||
|
sale_order.action_confirm()
|
||
|
|
||
|
# Deliver 10
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 10, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Make the second receipt
|
||
|
in_move_2 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 2,
|
||
|
'price_unit': 12,
|
||
|
})
|
||
|
in_move_2._action_confirm()
|
||
|
in_move_2.write({'quantity': 2, 'picked': True})
|
||
|
in_move_2._action_done()
|
||
|
self.assertEqual(self.product.stock_valuation_layer_ids[-1].value, -4) # we sent two at 10 but they should have been sent at 12
|
||
|
self.assertEqual(self.product.stock_valuation_layer_ids[-1].quantity, 0)
|
||
|
self.assertEqual(sale_order.order_line.move_ids.stock_valuation_layer_ids[-1].quantity, 0)
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls = invoice.line_ids
|
||
|
self.assertEqual(len(amls), 4)
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 104)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 104)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml.debit, 120)
|
||
|
self.assertEqual(receivable_aml.credit, 0)
|
||
|
income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml.debit, 0)
|
||
|
self.assertEqual(income_aml.credit, 120)
|
||
|
|
||
|
def test_fifo_delivered_invoice_post_delivery_3(self):
|
||
|
"""Receive 5@8, receive 8@12, sale 1@20, deliver, sale 6@20, deliver. Make sure no rouding
|
||
|
issues appear on the second invoice."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
|
||
|
# +5@8
|
||
|
in_move_1 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 5,
|
||
|
'price_unit': 8,
|
||
|
})
|
||
|
in_move_1._action_confirm()
|
||
|
in_move_1.write({'quantity': 5, 'picked': True})
|
||
|
in_move_1._action_done()
|
||
|
|
||
|
# +8@12
|
||
|
in_move_2 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 8,
|
||
|
'price_unit': 12,
|
||
|
})
|
||
|
in_move_2._action_confirm()
|
||
|
in_move_2.write({'quantity': 8, 'picked': True})
|
||
|
in_move_2._action_done()
|
||
|
|
||
|
# sale 1@20, deliver, invoice
|
||
|
sale_order = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 20,
|
||
|
'tax_id': False,
|
||
|
})],
|
||
|
})
|
||
|
sale_order.action_confirm()
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# sale 6@20, deliver, invoice
|
||
|
sale_order = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 6,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 20,
|
||
|
'tax_id': False,
|
||
|
})],
|
||
|
})
|
||
|
sale_order.action_confirm()
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 6, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# check the last anglo saxon invoice line
|
||
|
amls = invoice.line_ids
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 56)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
|
||
|
def test_fifo_delivered_invoice_post_delivery_4(self):
|
||
|
"""Receive 8@10. Sale order 10@12. Deliver and also invoice it without receiving the 2 missing.
|
||
|
Now, receive 2@12. Make sure price difference is correctly reflected in expense account."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
|
||
|
in_move_1 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 8,
|
||
|
'price_unit': 10,
|
||
|
})
|
||
|
in_move_1._action_confirm()
|
||
|
in_move_1.write({'quantity': 8, 'picked': True})
|
||
|
in_move_1._action_done()
|
||
|
|
||
|
# Create and confirm a sale order for 10@12
|
||
|
sale_order = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 10.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 12,
|
||
|
'tax_id': False, # no love taxes amls
|
||
|
})],
|
||
|
})
|
||
|
sale_order.action_confirm()
|
||
|
|
||
|
# Deliver 10
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 10, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice = sale_order._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Make the second receipt
|
||
|
in_move_2 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 2,
|
||
|
'price_unit': 12,
|
||
|
})
|
||
|
in_move_2._action_confirm()
|
||
|
in_move_2.write({'quantity': 2, 'picked': True})
|
||
|
in_move_2._action_done()
|
||
|
|
||
|
# check the last anglo saxon move line
|
||
|
revalued_anglo_expense_amls = sale_order.picking_ids.move_ids.stock_valuation_layer_ids[-1].stock_move_id.account_move_ids[-1].line_ids
|
||
|
revalued_cogs_aml = revalued_anglo_expense_amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(revalued_cogs_aml.debit, 4, 'Price difference should have correctly reflected in expense account.')
|
||
|
|
||
|
def test_fifo_delivered_invoice_post_delivery_with_return(self):
|
||
|
"""Receive 2@10. SO1 2@12. Return 1 from SO1. SO2 1@12. Receive 1@20.
|
||
|
Re-deliver returned from SO1. Invoice after delivering everything."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
|
||
|
# Receive 2@10.
|
||
|
in_move_1 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 2,
|
||
|
'price_unit': 10,
|
||
|
})
|
||
|
in_move_1._action_confirm()
|
||
|
in_move_1.write({'quantity': 2, 'picked': True})
|
||
|
in_move_1._action_done()
|
||
|
|
||
|
# Create, confirm and deliver a sale order for 2@12 (SO1)
|
||
|
so_1 = self._so_and_confirm_two_units()
|
||
|
so_1.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
|
||
|
so_1.picking_ids.button_validate()
|
||
|
|
||
|
# Return 1 from SO1
|
||
|
stock_return_picking_form = Form(
|
||
|
self.env['stock.return.picking'].with_context(
|
||
|
active_ids=so_1.picking_ids.ids, active_id=so_1.picking_ids.ids[0], active_model='stock.picking')
|
||
|
)
|
||
|
stock_return_picking = stock_return_picking_form.save()
|
||
|
stock_return_picking.product_return_moves.quantity = 1.0
|
||
|
stock_return_picking_action = stock_return_picking.create_returns()
|
||
|
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
|
||
|
return_pick.action_assign()
|
||
|
return_pick.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
return_pick._action_done()
|
||
|
|
||
|
# Create, confirm and deliver a sale order for 1@12 (SO2)
|
||
|
so_2 = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 1.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 12,
|
||
|
'tax_id': False, # no love taxes amls
|
||
|
})],
|
||
|
})
|
||
|
so_2.action_confirm()
|
||
|
so_2.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
so_2.picking_ids.button_validate()
|
||
|
|
||
|
# Receive 1@20
|
||
|
in_move_2 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': 20,
|
||
|
})
|
||
|
in_move_2._action_confirm()
|
||
|
in_move_2.write({'quantity': 1, 'picked': True})
|
||
|
in_move_2._action_done()
|
||
|
|
||
|
# Re-deliver returned 1 from SO1
|
||
|
stock_redeliver_picking_form = Form(
|
||
|
self.env['stock.return.picking'].with_context(
|
||
|
active_ids=return_pick.ids, active_id=return_pick.ids[0], active_model='stock.picking')
|
||
|
)
|
||
|
stock_redeliver_picking = stock_redeliver_picking_form.save()
|
||
|
stock_redeliver_picking.product_return_moves.quantity = 1.0
|
||
|
stock_redeliver_picking_action = stock_redeliver_picking.create_returns()
|
||
|
redeliver_pick = self.env['stock.picking'].browse(stock_redeliver_picking_action['res_id'])
|
||
|
redeliver_pick.action_assign()
|
||
|
redeliver_pick.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
redeliver_pick._action_done()
|
||
|
|
||
|
# Invoice the sale orders
|
||
|
invoice_1 = so_1._create_invoices()
|
||
|
invoice_1.action_post()
|
||
|
invoice_2 = so_2._create_invoices()
|
||
|
invoice_2.action_post()
|
||
|
|
||
|
# Check the resulting accounting entries
|
||
|
amls_1 = invoice_1.line_ids
|
||
|
self.assertEqual(len(amls_1), 4)
|
||
|
stock_out_aml_1 = amls_1.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml_1.debit, 0)
|
||
|
self.assertEqual(stock_out_aml_1.credit, 30)
|
||
|
cogs_aml_1 = amls_1.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml_1.debit, 30)
|
||
|
self.assertEqual(cogs_aml_1.credit, 0)
|
||
|
receivable_aml_1 = amls_1.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml_1.debit, 24)
|
||
|
self.assertEqual(receivable_aml_1.credit, 0)
|
||
|
income_aml_1 = amls_1.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml_1.debit, 0)
|
||
|
self.assertEqual(income_aml_1.credit, 24)
|
||
|
|
||
|
amls_2 = invoice_2.line_ids
|
||
|
self.assertEqual(len(amls_2), 4)
|
||
|
stock_out_aml_2 = amls_2.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml_2.debit, 0)
|
||
|
self.assertEqual(stock_out_aml_2.credit, 10)
|
||
|
cogs_aml_2 = amls_2.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml_2.debit, 10)
|
||
|
self.assertEqual(cogs_aml_2.credit, 0)
|
||
|
receivable_aml_2 = amls_2.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable'])
|
||
|
self.assertEqual(receivable_aml_2.debit, 12)
|
||
|
self.assertEqual(receivable_aml_2.credit, 0)
|
||
|
income_aml_2 = amls_2.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
|
||
|
self.assertEqual(income_aml_2.debit, 0)
|
||
|
self.assertEqual(income_aml_2.credit, 12)
|
||
|
|
||
|
def test_fifo_uom_computation(self):
|
||
|
self.env.company.anglo_saxon_accounting = True
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.categ_id.property_valuation = 'real_time'
|
||
|
quantity = 50.0
|
||
|
self.product.list_price = 1.5
|
||
|
self.product.standard_price = 2.0
|
||
|
unit_12 = self.env['uom.uom'].create({
|
||
|
'name': 'Pack of 12 units',
|
||
|
'category_id': self.product.uom_id.category_id.id,
|
||
|
'uom_type': 'bigger',
|
||
|
'factor_inv': 12,
|
||
|
'rounding': 1,
|
||
|
})
|
||
|
|
||
|
# Create, confirm and deliver a sale order for 12@1.5 without reception with std_price = 2.0 (SO1)
|
||
|
so_1 = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'product_uom': unit_12.id,
|
||
|
'price_unit': 18,
|
||
|
'tax_id': False, # no love taxes amls
|
||
|
})],
|
||
|
})
|
||
|
so_1.action_confirm()
|
||
|
so_1.picking_ids.move_ids.write({'quantity': 12, 'picked': True})
|
||
|
so_1.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice_1 = so_1._create_invoices()
|
||
|
invoice_1.action_post()
|
||
|
|
||
|
# Invoice 1
|
||
|
|
||
|
# Correct Journal Items
|
||
|
|
||
|
# Name Debit Credit
|
||
|
|
||
|
# Product Sales 0.00$ 18.00$
|
||
|
# Account Receivable 18.00$ 0.00$
|
||
|
# Default Account Stock Out 0.00$ 24.00$
|
||
|
# Expenses 24.00$ 0.00$
|
||
|
|
||
|
aml = invoice_1.line_ids
|
||
|
# Product Sales
|
||
|
self.assertEqual(aml[0].debit, 0.0)
|
||
|
self.assertEqual(aml[0].credit, 18.0)
|
||
|
# Account Receivable
|
||
|
self.assertEqual(aml[1].debit, 18.0)
|
||
|
self.assertEqual(aml[1].credit, 0.0)
|
||
|
# Default Account Stock Out
|
||
|
self.assertEqual(aml[2].debit, 0.0)
|
||
|
self.assertEqual(aml[2].credit, 24.0)
|
||
|
# Expenses
|
||
|
self.assertEqual(aml[3].debit, 24.0)
|
||
|
self.assertEqual(aml[3].credit, 0.0)
|
||
|
|
||
|
# Create stock move 1
|
||
|
in_move_1 = self.env['stock.move'].create({
|
||
|
'name': 'a',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': quantity,
|
||
|
'price_unit': 1.0,
|
||
|
})
|
||
|
in_move_1._action_confirm()
|
||
|
in_move_1.write({'quantity': quantity, 'picked': True})
|
||
|
in_move_1._action_done()
|
||
|
|
||
|
# Create, confirm and deliver a sale order for 12@1.5 with reception (50 * 1.0, 50 * 0.0)(SO2)
|
||
|
so_2 = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'product_uom': unit_12.id,
|
||
|
'price_unit': 18,
|
||
|
'tax_id': False, # no love taxes amls
|
||
|
})],
|
||
|
})
|
||
|
so_2.action_confirm()
|
||
|
so_2.picking_ids.move_ids.write({'quantity': 12, 'picked': True})
|
||
|
so_2.picking_ids.button_validate()
|
||
|
|
||
|
# Invoice the sale order.
|
||
|
invoice_2 = so_2._create_invoices()
|
||
|
invoice_2.action_post()
|
||
|
|
||
|
# Invoice 2
|
||
|
|
||
|
# Correct Journal Items
|
||
|
|
||
|
# Name Debit Credit
|
||
|
|
||
|
# Product Sales 0.00$ 18.0$
|
||
|
# Account Receivable 18.00$ 0.0$
|
||
|
# Default Account Stock Out 0.00$ 12.0$
|
||
|
# Expenses 12.00$ 0.0$
|
||
|
|
||
|
aml = invoice_2.line_ids
|
||
|
# Product Sales
|
||
|
self.assertEqual(aml[0].debit, 0.0)
|
||
|
self.assertEqual(aml[0].credit, 18.0)
|
||
|
# Account Receivable
|
||
|
self.assertEqual(aml[1].debit, 18.0)
|
||
|
self.assertEqual(aml[1].credit, 0.0)
|
||
|
# Default Account Stock Out
|
||
|
self.assertEqual(aml[2].debit, 0.0)
|
||
|
self.assertEqual(aml[2].credit, 12.0)
|
||
|
# Expenses
|
||
|
self.assertEqual(aml[3].debit, 12.0)
|
||
|
self.assertEqual(aml[3].credit, 0.0)
|
||
|
|
||
|
def test_fifo_return_and_credit_note(self):
|
||
|
"""
|
||
|
When posting a credit note for a returned product, the value of the anglo-saxo lines
|
||
|
should be based on the returned product's value
|
||
|
"""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
|
||
|
# Receive one @10, one @20 and one @60
|
||
|
in_moves = self.env['stock.move'].create([{
|
||
|
'name': 'IN move @%s' % p,
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': p,
|
||
|
} for p in [10, 20, 60]])
|
||
|
in_moves._action_confirm()
|
||
|
in_moves.write({'quantity': 1, 'picked': True})
|
||
|
in_moves._action_done()
|
||
|
|
||
|
# Sell 3 units
|
||
|
so = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 3.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 100,
|
||
|
'tax_id': False,
|
||
|
})],
|
||
|
})
|
||
|
so.action_confirm()
|
||
|
|
||
|
# Deliver 1@10, then 1@20 and then 1@60
|
||
|
pickings = []
|
||
|
picking = so.picking_ids
|
||
|
while picking:
|
||
|
pickings.append(picking)
|
||
|
picking.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
action = picking.button_validate()
|
||
|
if isinstance(action, dict):
|
||
|
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
|
||
|
wizard.process()
|
||
|
picking = picking.backorder_ids
|
||
|
|
||
|
invoice = so._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Receive one @100
|
||
|
in_moves = self.env['stock.move'].create({
|
||
|
'name': 'IN move @100',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': 100,
|
||
|
})
|
||
|
in_moves._action_confirm()
|
||
|
in_moves.write({'quantity': 1, 'picked': True})
|
||
|
in_moves._action_done()
|
||
|
|
||
|
# Return the second picking (i.e. 1@20)
|
||
|
ctx = {'active_id': pickings[1].id, 'active_model': 'stock.picking'}
|
||
|
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
|
||
|
return_picking_id, dummy = return_wizard._create_returns()
|
||
|
return_picking = self.env['stock.picking'].browse(return_picking_id)
|
||
|
return_picking.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
return_picking.button_validate()
|
||
|
|
||
|
# Add a credit note for the returned product
|
||
|
ctx = {'active_model': 'account.move', 'active_ids': invoice.ids}
|
||
|
refund_wizard = self.env['account.move.reversal'].with_context(ctx).create({
|
||
|
'journal_id': invoice.journal_id.id,
|
||
|
})
|
||
|
action = refund_wizard.refund_moves()
|
||
|
reverse_invoice = self.env['account.move'].browse(action['res_id'])
|
||
|
with Form(reverse_invoice) as reverse_invoice_form:
|
||
|
with reverse_invoice_form.invoice_line_ids.edit(0) as line:
|
||
|
line.quantity = 1
|
||
|
reverse_invoice.action_post()
|
||
|
|
||
|
amls = reverse_invoice.line_ids
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 20, 'Should be to the value of the returned product')
|
||
|
self.assertEqual(stock_out_aml.credit, 0)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 0)
|
||
|
self.assertEqual(cogs_aml.credit, 20, 'Should be to the value of the returned product')
|
||
|
|
||
|
def test_fifo_return_and_create_invoice(self):
|
||
|
"""
|
||
|
When creating an invoice for a returned product, the value of the anglo-saxo lines
|
||
|
should be based on the returned product's value
|
||
|
"""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
|
||
|
# Receive one @10, one @20 and one @60
|
||
|
in_moves = self.env['stock.move'].create([{
|
||
|
'name': 'IN move @%s' % p,
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': p,
|
||
|
} for p in [10, 20, 60]])
|
||
|
in_moves._action_confirm()
|
||
|
in_moves.write({'quantity': 1, 'picked': True})
|
||
|
in_moves._action_done()
|
||
|
|
||
|
# Sell 3 units
|
||
|
so = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 3.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 100,
|
||
|
'tax_id': False,
|
||
|
})],
|
||
|
})
|
||
|
so.action_confirm()
|
||
|
|
||
|
# Deliver 1@10, then 1@20 and then 1@60
|
||
|
pickings = []
|
||
|
picking = so.picking_ids
|
||
|
while picking:
|
||
|
pickings.append(picking)
|
||
|
picking.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
action = picking.button_validate()
|
||
|
if isinstance(action, dict):
|
||
|
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
|
||
|
wizard.process()
|
||
|
picking = picking.backorder_ids
|
||
|
|
||
|
invoice = so._create_invoices()
|
||
|
invoice.action_post()
|
||
|
|
||
|
# Receive one @100
|
||
|
in_moves = self.env['stock.move'].create({
|
||
|
'name': 'IN move @100',
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': 100,
|
||
|
})
|
||
|
in_moves._action_confirm()
|
||
|
in_moves.write({'quantity': 1, 'picked': True})
|
||
|
in_moves._action_done()
|
||
|
|
||
|
# Return the second picking (i.e. 1@20)
|
||
|
ctx = {'active_id': pickings[1].id, 'active_model': 'stock.picking'}
|
||
|
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
|
||
|
return_picking_id, dummy = return_wizard._create_returns()
|
||
|
return_picking = self.env['stock.picking'].browse(return_picking_id)
|
||
|
return_picking.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
return_picking.button_validate()
|
||
|
|
||
|
# Create a new invoice for the returned product
|
||
|
ctx = {'active_model': 'sale.order', 'active_ids': so.ids}
|
||
|
create_invoice_wizard = self.env['sale.advance.payment.inv'].with_context(ctx).create({'advance_payment_method': 'delivered'})
|
||
|
create_invoice_wizard.create_invoices()
|
||
|
reverse_invoice = so.invoice_ids[-1]
|
||
|
with Form(reverse_invoice) as reverse_invoice_form:
|
||
|
with reverse_invoice_form.invoice_line_ids.edit(0) as line:
|
||
|
line.quantity = 1
|
||
|
reverse_invoice.action_post()
|
||
|
|
||
|
amls = reverse_invoice.line_ids
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 20, 'Should be to the value of the returned product')
|
||
|
self.assertEqual(stock_out_aml.credit, 0)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 0)
|
||
|
self.assertEqual(cogs_aml.credit, 20, 'Should be to the value of the returned product')
|
||
|
|
||
|
def test_fifo_several_invoices_reset_repost(self):
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
|
||
|
svl_values = [10, 15, 65]
|
||
|
total_value = sum(svl_values)
|
||
|
in_moves = self.env['stock.move'].create([{
|
||
|
'name': 'IN move @%s' % p,
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': p,
|
||
|
} for p in svl_values])
|
||
|
in_moves._action_confirm()
|
||
|
in_moves.write({'quantity': 1, 'picked': True})
|
||
|
in_moves._action_done()
|
||
|
|
||
|
so = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 3.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 100,
|
||
|
'tax_id': False,
|
||
|
})],
|
||
|
})
|
||
|
so.action_confirm()
|
||
|
|
||
|
# Deliver one by one, so it creates an out-SVL each time.
|
||
|
# Then invoice the delivered quantity
|
||
|
invoices = self.env['account.move']
|
||
|
picking = so.picking_ids
|
||
|
while picking:
|
||
|
picking.move_ids.write({'quantity': 1, 'picked': True})
|
||
|
action = picking.button_validate()
|
||
|
if isinstance(action, dict):
|
||
|
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
|
||
|
wizard.process()
|
||
|
picking = picking.backorder_ids
|
||
|
|
||
|
invoice = so._create_invoices()
|
||
|
invoice.action_post()
|
||
|
invoices |= invoice
|
||
|
|
||
|
out_account = self.product.categ_id.property_stock_account_output_categ_id
|
||
|
invoice01, _invoice02, invoice03 = invoices
|
||
|
cogs = invoices.line_ids.filtered(lambda l: l.account_id == out_account)
|
||
|
self.assertEqual(cogs.mapped('credit'), svl_values)
|
||
|
|
||
|
# Reset and repost each invoice
|
||
|
for i, inv in enumerate(invoices):
|
||
|
inv.button_draft()
|
||
|
inv.action_post()
|
||
|
cogs = invoices.line_ids.filtered(lambda l: l.account_id == out_account)
|
||
|
self.assertEqual(cogs.mapped('credit'), svl_values, 'Incorrect values while posting again invoice %s' % (i + 1))
|
||
|
|
||
|
# Reset and repost all invoices (we only check the total value as the
|
||
|
# distribution changes but does not really matter)
|
||
|
invoices.button_draft()
|
||
|
invoices.action_post()
|
||
|
cogs = invoices.line_ids.filtered(lambda l: l.account_id == out_account)
|
||
|
self.assertEqual(sum(cogs.mapped('credit')), total_value)
|
||
|
|
||
|
# Reset and repost few invoices (we only check the total value as the
|
||
|
# distribution changes but does not really matter)
|
||
|
(invoice01 | invoice03).button_draft()
|
||
|
(invoice01 | invoice03).action_post()
|
||
|
cogs = invoices.line_ids.filtered(lambda l: l.account_id == out_account)
|
||
|
self.assertEqual(sum(cogs.mapped('credit')), total_value)
|
||
|
|
||
|
def test_fifo_reverse_and_create_new_invoice(self):
|
||
|
"""
|
||
|
FIFO automated
|
||
|
Receive 1@10, 1@50
|
||
|
Deliver 1
|
||
|
Post the invoice, add a credit note with option 'new draft inv'
|
||
|
Post the second invoice
|
||
|
COGS should be based on the delivered product
|
||
|
|
||
|
Note: This test will also ensure that a user who only has access to
|
||
|
account app can post such an invoice
|
||
|
"""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
|
||
|
accountman = self.env['res.users'].create({
|
||
|
'name': 'Super Accountman',
|
||
|
'login': 'super_accountman',
|
||
|
'password': 'super_accountman',
|
||
|
'groups_id': [(6, 0, self.env.ref('account.group_account_invoice').ids)],
|
||
|
})
|
||
|
|
||
|
in_moves = self.env['stock.move'].create([{
|
||
|
'name': 'IN move @%s' % p,
|
||
|
'product_id': self.product.id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'product_uom_qty': 1,
|
||
|
'price_unit': p,
|
||
|
} for p in [10, 50]])
|
||
|
in_moves._action_confirm()
|
||
|
in_moves.write({'quantity': 1, 'picked': True})
|
||
|
in_moves._action_done()
|
||
|
|
||
|
so = self.env['sale.order'].create({
|
||
|
'partner_id': self.partner_a.id,
|
||
|
'order_line': [
|
||
|
(0, 0, {
|
||
|
'name': self.product.name,
|
||
|
'product_id': self.product.id,
|
||
|
'product_uom_qty': 1.0,
|
||
|
'product_uom': self.product.uom_id.id,
|
||
|
'price_unit': 100,
|
||
|
'tax_id': False,
|
||
|
})],
|
||
|
})
|
||
|
so.action_confirm()
|
||
|
|
||
|
picking = so.picking_ids
|
||
|
picking.move_ids.write({'quantity': 1.0, 'picked': True})
|
||
|
picking.button_validate()
|
||
|
|
||
|
invoice01 = so._create_invoices()
|
||
|
|
||
|
# Clear the cache to ensure access rights
|
||
|
self.env.invalidate_all()
|
||
|
invoice01.with_user(accountman.id).action_post()
|
||
|
|
||
|
move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=invoice01.ids).create({
|
||
|
'journal_id': invoice01.journal_id.id,
|
||
|
})
|
||
|
reversal = move_reversal.modify_moves()
|
||
|
invoice02 = self.env['account.move'].browse(reversal['res_id'])
|
||
|
invoice02.action_post()
|
||
|
|
||
|
amls = invoice02.line_ids
|
||
|
stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
self.assertEqual(stock_out_aml.debit, 0)
|
||
|
self.assertEqual(stock_out_aml.credit, 10)
|
||
|
cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
|
||
|
self.assertEqual(cogs_aml.debit, 10)
|
||
|
self.assertEqual(cogs_aml.credit, 0)
|
||
|
|
||
|
def test_fifo_edit_svl_without_reinvoice(self):
|
||
|
"""Edit SVL move line after delivering. Check no reinvoicing occurs."""
|
||
|
self.product.categ_id.property_cost_method = 'fifo'
|
||
|
self.product.invoice_policy = 'delivery'
|
||
|
self.product.standard_price = 10
|
||
|
self.product.expense_policy = 'cost'
|
||
|
|
||
|
self._fifo_in_one_eight_one_ten()
|
||
|
|
||
|
# Create and confirm a sale order for 2@12
|
||
|
sale_order = self._so_and_confirm_two_units()
|
||
|
self.assertEqual(len(sale_order.order_line), 1)
|
||
|
self.assertEqual(sale_order.order_line.product_uom_qty, 2.0)
|
||
|
|
||
|
# Deliver one.
|
||
|
sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
|
||
|
sale_order.picking_ids.button_validate()
|
||
|
svl_am = sale_order.order_line.move_ids.stock_valuation_layer_ids.account_move_id
|
||
|
svl_am.button_draft()
|
||
|
svl_line = svl_am.line_ids.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
|
||
|
|
||
|
svl_line.write({'analytic_distribution': {sale_order.analytic_account_id.id: 100}})
|
||
|
svl_am.action_post()
|
||
|
|
||
|
# Check no reinvoice line addded to the sale order
|
||
|
self.assertEqual(len(sale_order.order_line), 1)
|
||
|
self.assertEqual(sale_order.order_line.product_uom_qty, 2.0)
|