[ADD] stock_landed_costs: Add test case for incoming shipment.
The current test in landed costs puts landed costs on outgoing pickings. It is a bit weird as they are put on incoming shipments normally. It checks if the lines of the landed costs have been calculated well, but not if the amounts/accounts/... on the account move and account move lines have been calculated correctly. It should also check the entries created when some part is already out and when we create negative landed costs. (in order to be able to revert them) Scenario : - Two new products without stock as is done in current test in real-time valuation,... - Both products are received - One product goes out (or a part of the quantity) - A landed cost is created for the incoming picking - Check that the journal items created, have the correct amounts and that there is a part that went out and is put on the out account - Create a negative landed cost as the first landed cost was a little bit too high - Check again that the journal items created have the correct amounts
This commit is contained in:
committed by
Yannick Tivisse
parent
00f01b8b1d
commit
3855ab7d85
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import test_stock_landed_costs_purchase
|
||||
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo.tests import common
|
||||
|
||||
class TestStockLandedCostsCommon(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestStockLandedCostsCommon, self).setUp()
|
||||
# Objects
|
||||
self.Product = self.env['product.product']
|
||||
self.Picking = self.env['stock.picking']
|
||||
self.Move = self.env['stock.move']
|
||||
self.LandedCost = self.env['stock.landed.cost']
|
||||
self.CostLine = self.env['stock.landed.cost.lines']
|
||||
# References
|
||||
self.supplier_id = self.ref('base.res_partner_2')
|
||||
self.customer_id = self.ref('base.res_partner_4')
|
||||
self.picking_type_in_id = self.ref('stock.picking_type_in')
|
||||
self.picking_type_out_id = self.ref('stock.picking_type_out')
|
||||
self.supplier_location_id = self.ref('stock.stock_location_suppliers')
|
||||
self.stock_location_id = self.ref('stock.stock_location_stock')
|
||||
self.customer_location_id = self.ref('stock.stock_location_customers')
|
||||
self.categ_all = self.env.ref('product.product_category_all')
|
||||
# Create account
|
||||
self.default_account = self.env['account.account'].create({
|
||||
'name': "Purchased Stocks",
|
||||
'code': "X1101",
|
||||
'user_type_id': self.env['account.account.type'].create({
|
||||
'name': 'Expenses',
|
||||
'type': 'other'}).id,
|
||||
'reconcile': True})
|
||||
self.expenses_journal = self.env['account.journal'].create({
|
||||
'name': 'Expenses - Test',
|
||||
'code': 'TEXJ',
|
||||
'type': 'purchase',
|
||||
'default_debit_account_id': self.default_account.id,
|
||||
'default_credit_account_id': self.default_account.id})
|
||||
# Create product refrigerator & oven
|
||||
self.product_refrigerator = self.Product.create({
|
||||
'name': 'Refrigerator',
|
||||
'type': 'product',
|
||||
'cost_method': 'real',
|
||||
'valuation': 'real_time',
|
||||
'standard_price': 1.0,
|
||||
'weight': 10,
|
||||
'volume': 1,
|
||||
'categ_id': self.categ_all.id})
|
||||
self.product_oven = self.Product.create({
|
||||
'name': 'Microwave Oven',
|
||||
'type': 'product',
|
||||
'cost_method': 'real',
|
||||
'valuation': 'real_time',
|
||||
'standard_price': 1.0,
|
||||
'weight': 20,
|
||||
'volume': 1.5,
|
||||
'categ_id': self.categ_all.id})
|
||||
# Create service type product 1.Labour 2.Brokerage 3.Transportation 4.Packaging
|
||||
self.landed_cost = self._create_services('Landed Cost')
|
||||
self.brokerage_quantity = self._create_services('Brokerage Cost')
|
||||
self.transportation_weight = self._create_services('Transportation Cost')
|
||||
self.packaging_volume = self._create_services('Packaging Cost')
|
||||
|
||||
def _create_services(self, name):
|
||||
return self.Product.create({
|
||||
'name': name,
|
||||
'landed_cost_ok': True,
|
||||
'type': 'service'})
|
||||
@@ -0,0 +1,248 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo
|
||||
from odoo.addons.stock_landed_costs.tests.common import TestStockLandedCostsCommon
|
||||
|
||||
@odoo.tests.common.at_install(False)
|
||||
@odoo.tests.common.post_install(True)
|
||||
class TestLandedCosts(TestStockLandedCostsCommon):
|
||||
|
||||
def setUp(self):
|
||||
super(TestLandedCosts, self).setUp()
|
||||
# Create picking incoming shipment
|
||||
self.picking_in = self.Picking.create({
|
||||
'partner_id': self.supplier_id,
|
||||
'picking_type_id': self.picking_type_in_id,
|
||||
'location_id': self.supplier_location_id,
|
||||
'location_dest_id': self.stock_location_id})
|
||||
self.Move.create({
|
||||
'name': self.product_refrigerator.name,
|
||||
'product_id': self.product_refrigerator.id,
|
||||
'product_uom_qty': 5,
|
||||
'product_uom': self.product_refrigerator.uom_id.id,
|
||||
'picking_id': self.picking_in.id,
|
||||
'location_id': self.supplier_location_id,
|
||||
'location_dest_id': self.stock_location_id})
|
||||
self.Move.create({
|
||||
'name': self.product_oven.name,
|
||||
'product_id': self.product_oven.id,
|
||||
'product_uom_qty': 10,
|
||||
'product_uom': self.product_oven.uom_id.id,
|
||||
'picking_id': self.picking_in.id,
|
||||
'location_id': self.supplier_location_id,
|
||||
'location_dest_id': self.stock_location_id})
|
||||
# Create picking outgoing shipment
|
||||
self.picking_out = self.Picking.create({
|
||||
'partner_id': self.customer_id,
|
||||
'picking_type_id': self.picking_type_out_id,
|
||||
'location_id': self.stock_location_id,
|
||||
'location_dest_id': self.customer_location_id})
|
||||
self.Move.create({
|
||||
'name': self.product_refrigerator.name,
|
||||
'product_id': self.product_refrigerator.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.product_refrigerator.uom_id.id,
|
||||
'picking_id': self.picking_out.id,
|
||||
'location_id': self.stock_location_id,
|
||||
'location_dest_id': self.customer_location_id})
|
||||
|
||||
def test_00_landed_costs_on_incoming_shipment(self):
|
||||
""" Test landed cost on incoming shipment """
|
||||
#
|
||||
# (A) Purchase product
|
||||
|
||||
# Services Quantity Weight Volume
|
||||
# -----------------------------------------------------
|
||||
# 1. Refrigerator 5 10 1
|
||||
# 2. Oven 10 20 1.5
|
||||
|
||||
# (B) Add some costs on purchase
|
||||
|
||||
# Services Amount Split Method
|
||||
# -------------------------------------------
|
||||
# 1.labour 10 By Equal
|
||||
# 2.brokerage 150 By Quantity
|
||||
# 3.transportation 250 By Weight
|
||||
# 4.packaging 20 By Volume
|
||||
|
||||
# Process incoming shipment
|
||||
income_ship = self._process_incoming_shipment()
|
||||
# Create landed costs
|
||||
stock_landed_cost = self._create_landed_costs({
|
||||
'equal_price_unit': 10,
|
||||
'quantity_price_unit': 150,
|
||||
'weight_price_unit': 250,
|
||||
'volume_price_unit': 20}, income_ship)
|
||||
# Compute landed costs
|
||||
stock_landed_cost.compute_landed_cost()
|
||||
|
||||
valid_vals = {
|
||||
'equal': 5.0,
|
||||
'by_quantity_refrigerator': 50.0,
|
||||
'by_quantity_oven': 100.0,
|
||||
'by_weight_refrigerator': 50.0,
|
||||
'by_weight_oven': 200,
|
||||
'by_volume_refrigerator': 5.0,
|
||||
'by_volume_oven': 15.0}
|
||||
|
||||
# Check valuation adjustment line recognized or not
|
||||
self._validate_additional_landed_cost_lines(stock_landed_cost, valid_vals)
|
||||
# Validate the landed cost.
|
||||
stock_landed_cost.button_validate()
|
||||
self.assertTrue(stock_landed_cost.account_move_id, 'Landed costs should be available account move lines')
|
||||
account_entry = self.env['account.move.line'].read_group(
|
||||
[('move_id', '=', stock_landed_cost.account_move_id.id)], ['debit', 'credit', 'move_id'], ['move_id'])[0]
|
||||
self.assertEqual(account_entry['debit'], account_entry['credit'], 'Debit and credit are not equal')
|
||||
self.assertEqual(account_entry['debit'], 430.0, 'Wrong Account Entry')
|
||||
|
||||
def test_01_negative_landed_costs_on_incoming_shipment(self):
|
||||
""" Test negative landed cost on incoming shipment """
|
||||
#
|
||||
# (A) Purchase Product
|
||||
|
||||
# Services Quantity Weight Volume
|
||||
# -----------------------------------------------------
|
||||
# 1. Refrigerator 5 10 1
|
||||
# 2. Oven 10 20 1.5
|
||||
|
||||
# (B) Sale refrigerator's part of the quantity
|
||||
|
||||
# (C) Add some costs on purchase
|
||||
|
||||
# Services Amount Split Method
|
||||
# -------------------------------------------
|
||||
# 1.labour 10 By Equal
|
||||
# 2.brokerage 150 By Quantity
|
||||
# 3.transportation 250 By Weight
|
||||
# 4.packaging 20 By Volume
|
||||
|
||||
# (D) Decrease cost that already added on purchase
|
||||
# (apply negative entry)
|
||||
|
||||
# Services Amount Split Method
|
||||
# -------------------------------------------
|
||||
# 1.labour -5 By Equal
|
||||
# 2.brokerage -50 By Quantity
|
||||
# 3.transportation -50 By Weight
|
||||
# 4.packaging -5 By Volume
|
||||
|
||||
# Process incoming shipment
|
||||
income_ship = self._process_incoming_shipment()
|
||||
# Refrigerator outgoing shipment.
|
||||
self._process_outgoing_shipment()
|
||||
# Apply landed cost for incoming shipment.
|
||||
stock_landed_cost = self._create_landed_costs({
|
||||
'equal_price_unit': 10,
|
||||
'quantity_price_unit': 150,
|
||||
'weight_price_unit': 250,
|
||||
'volume_price_unit': 20}, income_ship)
|
||||
# Compute landed costs
|
||||
stock_landed_cost.compute_landed_cost()
|
||||
valid_vals = {
|
||||
'equal': 5.0,
|
||||
'by_quantity_refrigerator': 50.0,
|
||||
'by_quantity_oven': 100.0,
|
||||
'by_weight_refrigerator': 50.0,
|
||||
'by_weight_oven': 200.0,
|
||||
'by_volume_refrigerator': 5.0,
|
||||
'by_volume_oven': 15.0}
|
||||
# Check valuation adjustment line recognized or not
|
||||
self._validate_additional_landed_cost_lines(stock_landed_cost, valid_vals)
|
||||
# Validate the landed cost.
|
||||
stock_landed_cost.button_validate()
|
||||
self.assertTrue(stock_landed_cost.account_move_id, 'Landed costs should be available account move lines')
|
||||
# Create negative landed cost for previously incoming shipment.
|
||||
stock_negative_landed_cost = self._create_landed_costs({
|
||||
'equal_price_unit': -5,
|
||||
'quantity_price_unit': -50,
|
||||
'weight_price_unit': -50,
|
||||
'volume_price_unit': -5}, income_ship)
|
||||
# Compute negative landed costs
|
||||
stock_negative_landed_cost.compute_landed_cost()
|
||||
valid_vals = {
|
||||
'equal': -2.5,
|
||||
'by_quantity_refrigerator': -16.67,
|
||||
'by_quantity_oven': -33.33,
|
||||
'by_weight_refrigerator': -10.00,
|
||||
'by_weight_oven': -40.00,
|
||||
'by_volume_refrigerator': -1.25,
|
||||
'by_volume_oven': -3.75}
|
||||
# Check valuation adjustment line recognized or not
|
||||
self._validate_additional_landed_cost_lines(stock_negative_landed_cost, valid_vals)
|
||||
# Validate the landed cost.
|
||||
stock_negative_landed_cost.button_validate()
|
||||
self.assertEqual(stock_negative_landed_cost.state, 'done', 'Negative landed costs should be in done state')
|
||||
self.assertTrue(stock_negative_landed_cost.account_move_id, 'Landed costs should be available account move lines')
|
||||
account_entry = self.env['account.move.line'].read_group(
|
||||
[('move_id', '=', stock_negative_landed_cost.account_move_id.id)], ['debit', 'credit', 'move_id'], ['move_id'])[0]
|
||||
self.assertEqual(account_entry['debit'], account_entry['credit'], 'Debit and credit are not equal')
|
||||
self.assertEqual(account_entry['debit'], 122.168, 'Wrong Account Entry')
|
||||
|
||||
def _process_incoming_shipment(self):
|
||||
""" Two product incoming shipment. """
|
||||
# Confirm incoming shipment.
|
||||
self.picking_in.action_confirm()
|
||||
# Transfer incoming shipment
|
||||
self.picking_in.do_transfer()
|
||||
return self.picking_in
|
||||
|
||||
def _process_outgoing_shipment(self):
|
||||
""" One product Outgoing shipment. """
|
||||
# Confirm outgoing shipment.
|
||||
self.picking_out.action_confirm()
|
||||
# Product assign to outgoing shipments
|
||||
self.picking_out.action_assign()
|
||||
# Transfer picking.
|
||||
self.picking_out.do_transfer()
|
||||
|
||||
def _create_landed_costs(self, value, picking_in):
|
||||
return self.LandedCost.create(dict(
|
||||
picking_ids=[(6, 0, [picking_in.id])],
|
||||
account_journal_id=self.expenses_journal.id,
|
||||
cost_lines=[
|
||||
(0, 0, {
|
||||
'name': 'equal split',
|
||||
'split_method': 'equal',
|
||||
'price_unit': value['equal_price_unit'],
|
||||
'product_id': self.landed_cost.id}),
|
||||
(0, 0, {
|
||||
'name': 'split by quantity',
|
||||
'split_method': 'by_quantity',
|
||||
'price_unit': value['quantity_price_unit'],
|
||||
'product_id': self.brokerage_quantity.id}),
|
||||
(0, 0, {
|
||||
'name': 'split by weight',
|
||||
'split_method': 'by_weight',
|
||||
'price_unit': value['weight_price_unit'],
|
||||
'product_id': self.transportation_weight.id}),
|
||||
(0, 0, {
|
||||
'name': 'split by volume',
|
||||
'split_method': 'by_volume',
|
||||
'price_unit': value['volume_price_unit'],
|
||||
'product_id': self.packaging_volume.id})
|
||||
],
|
||||
))
|
||||
|
||||
def _validate_additional_landed_cost_lines(self, stock_landed_cost, valid_vals):
|
||||
for valuation in stock_landed_cost.valuation_adjustment_lines:
|
||||
add_cost = valuation.additional_landed_cost
|
||||
split_method = valuation.cost_line_id.split_method
|
||||
product = valuation.move_id.product_id
|
||||
if split_method == 'equal':
|
||||
self.assertEqual(add_cost, valid_vals['equal'], self._error_message(valid_vals['equal'], add_cost))
|
||||
elif split_method == 'by_quantity' and product == self.product_refrigerator:
|
||||
self.assertEqual(add_cost, valid_vals['by_quantity_refrigerator'], self._error_message(valid_vals['by_quantity_refrigerator'], add_cost))
|
||||
elif split_method == 'by_quantity' and product == self.product_oven:
|
||||
self.assertEqual(add_cost, valid_vals['by_quantity_oven'], self._error_message(valid_vals['by_quantity_oven'], add_cost))
|
||||
elif split_method == 'by_weight' and product == self.product_refrigerator:
|
||||
self.assertEqual(add_cost, valid_vals['by_weight_refrigerator'], self._error_message(valid_vals['by_weight_refrigerator'], add_cost))
|
||||
elif split_method == 'by_weight' and product == self.product_oven:
|
||||
self.assertEqual(add_cost, valid_vals['by_weight_oven'], self._error_message(valid_vals['by_weight_oven'], add_cost))
|
||||
elif split_method == 'by_volume' and product == self.product_refrigerator:
|
||||
self.assertEqual(add_cost, valid_vals['by_volume_refrigerator'], self._error_message(valid_vals['by_volume_refrigerator'], add_cost))
|
||||
elif split_method == 'by_volume' and product == self.product_oven:
|
||||
self.assertEqual(add_cost, valid_vals['by_volume_oven'], self._error_message(valid_vals['by_volume_oven'], add_cost))
|
||||
|
||||
def _error_message(self, actucal_cost, computed_cost):
|
||||
return 'Additional Landed Cost should be %s instead of %s' % (actucal_cost, computed_cost)
|
||||
Reference in New Issue
Block a user