[IMP] mrp_subcontracting{, _dropshipping}: split subcontracting account move

Considering a subcontracting use case where:
 component cost = 50
 service cost = 100

we post:
|account,              |debit,         |credit
-----------------------------------------------
Receipt Component from vendor
|Stock Valuation       |50             |
|Stock Input           |               |50
-----------------------------------------------
Invoice Component from vendor
|Stock Input           |50             |
|Payable               |               |50
-----------------------------------------------
Resupply Component to subcontractor
|Stock Valuation       |               |50
|Stock Output          |50             |
----------------------------------------------
Receive Final from subcontractor
|Stock Valuation       |150            |
|Stock Input           |               |150
----------------------------------------------
Invoice Final from subcontractor
|Stock Input           |100            |
|Payable               |               |100
-----------------------------------------------
Deliver Final to client
|Stock Valuation       |               |150
|Stock Output          |150            |
-----------------------------------------------
Invoice Client
|Stock Output          |               |150
|Receivable            |150            |

Stock Input/Output account are not balanced.
In this commit, we split the credit value when received final from
subcontractor to make them balanced.

|account,              |debit,         |credit
-----------------------------------------------
Receipt Component from vendor
|Stock Valuation       |50             |
|Stock Input           |               |50
-----------------------------------------------
Invoice Component from vendor
|Stock Input           |50             |
|Payable               |               |50
-----------------------------------------------
Resupply Component to subcontractor
|Stock Valuation       |               |50
|Stock Output          |50             |
----------------------------------------------
Receive Final from subcontractor
|Stock Valuation       |150            |
|Stock Input           |               |100
|Stock Output          |               |50
----------------------------------------------
Invoice Final from subcontractor
|Stock Input           |100            |
|Payable               |               |100
-----------------------------------------------
Deliver Final to client
|Stock Valuation       |               |150
|Stock Output          |150            |
-----------------------------------------------
Invoice Client
|Stock Output          |               |150
|Receivable            |150            |
---

closes odoo/odoo#113853

Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
This commit is contained in:
yhu-odoo
2023-03-03 17:05:07 +01:00
committed by Arnold Moyaux
parent 8ad736fe48
commit f7a681abff
3 changed files with 75 additions and 2 deletions
@@ -10,3 +10,38 @@ class StockMove(models.Model):
def _should_force_price_unit(self):
self.ensure_one()
return self.is_subcontract or super()._should_force_price_unit()
def _generate_valuation_lines_data(self, partner_id, qty, debit_value, credit_value, debit_account_id, credit_account_id, svl_id, description):
rslt = super()._generate_valuation_lines_data(partner_id, qty, debit_value, credit_value, debit_account_id, credit_account_id, svl_id, description)
subcontract_production = self.production_id.filtered(lambda p: p.subcontractor_id)
if not subcontract_production:
return rslt
# split the credit line to two, one for component cost, one for subcontracting service cost
currency = self.company_id.currency_id
subcontract_service_cost = currency.round(subcontract_production.extra_cost * qty)
if not currency.is_zero(subcontract_service_cost):
del rslt['credit_line_vals']
component_cost = -credit_value + subcontract_service_cost
component_cost_account = self.product_id.product_tmpl_id.get_product_accounts()['stock_output']
rslt['subcontract_credit_line_vals'] = {
'name': description,
'product_id': self.product_id.id,
'quantity': qty,
'product_uom_id': self.product_id.uom_id.id,
'ref': description,
'partner_id': partner_id,
'balance': -subcontract_service_cost,
'account_id': credit_account_id,
}
rslt['component_credit_line_vals'] = {
'name': description,
'product_id': self.product_id.id,
'quantity': qty,
'product_uom_id': self.product_id.uom_id.id,
'ref': description,
'partner_id': partner_id,
'balance': component_cost,
'account_id': component_cost_account.id,
}
return rslt
@@ -8,13 +8,20 @@ from odoo.tools.float_utils import float_round, float_compare
from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon
from odoo.addons.mrp_account.tests.test_bom_price import TestBomPriceCommon
class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
def test_subcontracting_account_flow_1(self):
# pylint: disable=bad-whitespace
self.stock_location = self.env.ref('stock.stock_location_stock')
self.customer_location = self.env.ref('stock.stock_location_customers')
self.supplier_location = self.env.ref('stock.stock_location_suppliers')
self.uom_unit = self.env.ref('uom.product_uom_unit')
self.env.ref('product.product_category_all').property_cost_method = 'fifo'
product_category_all = self.env.ref('product.product_category_all')
product_category_all.property_cost_method = 'fifo'
product_category_all.property_valuation = 'real_time'
stock_in_acc_id = product_category_all.property_stock_account_input_categ_id.id
stock_out_acc_id = product_category_all.property_stock_account_output_categ_id.id
stock_valu_acc_id = product_category_all.property_stock_valuation_account_id.id
# IN 10@10 comp1 10@20 comp2
move1 = self.env['stock.move'].create({
@@ -44,6 +51,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
move2.move_line_ids.qty_done = 10.0
move2._action_done()
all_amls_ids = self.env['account.move.line'].search([]).ids
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
picking_form.partner_id = self.subcontractor_partner1
@@ -70,6 +79,21 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
self.assertEqual(picking_receipt.move_ids.stock_valuation_layer_ids.value, 0)
self.assertEqual(picking_receipt.move_ids.product_id.value_svl, 60)
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
all_amls_ids += amls.ids
self.assertRecordValues(amls, [
# Receipt from subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.finished.id, 'debit': 60.0, 'credit': 0.0},
{'account_id': stock_in_acc_id, 'product_id': self.finished.id, 'debit': 0.0, 'credit': 30.0},
{'account_id': stock_out_acc_id, 'product_id': self.finished.id, 'debit': 0.0, 'credit': 30.0},
# Delivery com2 to subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.comp2.id, 'debit': 0.0, 'credit': 20.0},
{'account_id': stock_out_acc_id, 'product_id': self.comp2.id, 'debit': 20.0, 'credit': 0.0},
# Delivery com2 to subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.comp1.id, 'debit': 0.0, 'credit': 10.0},
{'account_id': stock_out_acc_id, 'product_id': self.comp1.id, 'debit': 10.0, 'credit': 0.0},
])
# Do the same without any additionnal cost
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
@@ -90,6 +114,19 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
self.assertEqual(mo.move_finished_ids.stock_valuation_layer_ids.value, 30)
self.assertEqual(picking_receipt.move_ids.product_id.value_svl, 90)
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
self.assertRecordValues(amls, [
# Receipt from subcontractor
{'account_id': stock_in_acc_id, 'product_id': self.finished.id, 'debit': 0.0, 'credit': 30.0},
{'account_id': stock_valu_acc_id, 'product_id': self.finished.id, 'debit': 30.0, 'credit': 0.0},
# Delivery com2 to subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.comp2.id, 'debit': 0.0, 'credit': 20.0},
{'account_id': stock_out_acc_id, 'product_id': self.comp2.id, 'debit': 20.0, 'credit': 0.0},
# Delivery com2 to subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.comp1.id, 'debit': 0.0, 'credit': 10.0},
{'account_id': stock_out_acc_id, 'product_id': self.comp1.id, 'debit': 10.0, 'credit': 0.0},
])
def test_subcontracting_account_backorder(self):
""" This test uses tracked (serial and lot) component and tracked (serial) finished product
The original subcontracting production order will be split into 4 backorders. This test
@@ -83,8 +83,9 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 20.0},
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 20.0, 'credit': 0.0},
# Receipt from subcontractor
{'account_id': stock_in_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 220.0},
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 220.0, 'credit': 0.0},
{'account_id': stock_in_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 200.0},
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 20.0},
# Delivery to subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.product_b.id, 'debit': 0.0, 'credit': 20.0},
{'account_id': stock_out_acc_id, 'product_id': self.product_b.id, 'debit': 20.0, 'credit': 0.0},