test_sale_mrp_anglo_saxon_valuation.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo.tests import Form, tagged
  4. from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
  5. @tagged('post_install', '-at_install')
  6. class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
  7. @classmethod
  8. def setUpClass(cls, chart_template_ref=None):
  9. super().setUpClass(chart_template_ref=chart_template_ref)
  10. cls.env.user.company_id.anglo_saxon_accounting = True
  11. cls.uom_unit = cls.env.ref('uom.product_uom_unit')
  12. def _create_product(self, name, product_type, price):
  13. return self.env['product.product'].create({
  14. 'name': name,
  15. 'type': product_type,
  16. 'standard_price': price,
  17. 'categ_id': self.stock_account_product_categ.id if product_type == 'product' else self.env.ref('product.product_category_all').id,
  18. })
  19. def test_sale_mrp_kit_bom_cogs(self):
  20. """Check invoice COGS aml after selling and delivering a product
  21. with Kit BoM having another product with Kit BoM as component"""
  22. # ----------------------------------------------
  23. # BoM of Kit A:
  24. # - BoM Type: Kit
  25. # - Quantity: 3
  26. # - Components:
  27. # * 2 x Kit B
  28. # * 1 x Component A (Cost: $3, Storable)
  29. #
  30. # BoM of Kit B:
  31. # - BoM Type: Kit
  32. # - Quantity: 10
  33. # - Components:
  34. # * 2 x Component B (Cost: $4, Storable)
  35. # * 3 x Component BB (Cost: $5, Consumable)
  36. # ----------------------------------------------
  37. self.component_a = self._create_product('Component A', 'product', 3.00)
  38. self.component_b = self._create_product('Component B', 'product', 4.00)
  39. self.component_bb = self._create_product('Component BB', 'consu', 5.00)
  40. self.kit_a = self._create_product('Kit A', 'product', 0.00)
  41. self.kit_b = self._create_product('Kit B', 'consu', 0.00)
  42. self.kit_a.write({
  43. 'property_account_expense_id': self.company_data['default_account_expense'].id,
  44. 'property_account_income_id': self.company_data['default_account_revenue'].id,
  45. })
  46. # Create BoM for Kit A
  47. bom_product_form = Form(self.env['mrp.bom'])
  48. bom_product_form.product_id = self.kit_a
  49. bom_product_form.product_tmpl_id = self.kit_a.product_tmpl_id
  50. bom_product_form.product_qty = 3.0
  51. bom_product_form.type = 'phantom'
  52. with bom_product_form.bom_line_ids.new() as bom_line:
  53. bom_line.product_id = self.kit_b
  54. bom_line.product_qty = 2.0
  55. with bom_product_form.bom_line_ids.new() as bom_line:
  56. bom_line.product_id = self.component_a
  57. bom_line.product_qty = 1.0
  58. self.bom_a = bom_product_form.save()
  59. # Create BoM for Kit B
  60. bom_product_form = Form(self.env['mrp.bom'])
  61. bom_product_form.product_id = self.kit_b
  62. bom_product_form.product_tmpl_id = self.kit_b.product_tmpl_id
  63. bom_product_form.product_qty = 10.0
  64. bom_product_form.type = 'phantom'
  65. with bom_product_form.bom_line_ids.new() as bom_line:
  66. bom_line.product_id = self.component_b
  67. bom_line.product_qty = 2.0
  68. with bom_product_form.bom_line_ids.new() as bom_line:
  69. bom_line.product_id = self.component_bb
  70. bom_line.product_qty = 3.0
  71. self.bom_b = bom_product_form.save()
  72. so = self.env['sale.order'].create({
  73. 'partner_id': self.partner_a.id,
  74. 'order_line': [
  75. (0, 0, {
  76. 'name': self.kit_a.name,
  77. 'product_id': self.kit_a.id,
  78. 'product_uom_qty': 1.0,
  79. 'product_uom': self.kit_a.uom_id.id,
  80. 'price_unit': 1,
  81. 'tax_id': False,
  82. })],
  83. })
  84. so.action_confirm()
  85. so.picking_ids.move_ids.quantity_done = 1
  86. so.picking_ids.button_validate()
  87. invoice = so.with_context(default_journal_id=self.company_data['default_journal_sale'].id)._create_invoices()
  88. invoice.action_post()
  89. # Check the resulting accounting entries
  90. amls = invoice.line_ids
  91. self.assertEqual(len(amls), 4)
  92. stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
  93. self.assertEqual(stock_out_aml.debit, 0)
  94. self.assertAlmostEqual(stock_out_aml.credit, 1.53, "Should not include the value of consumable component")
  95. cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
  96. self.assertAlmostEqual(cogs_aml.debit, 1.53, "Should not include the value of consumable component")
  97. self.assertEqual(cogs_aml.credit, 0)
  98. def test_sale_mrp_anglo_saxon_variant(self):
  99. """Test the price unit of kit with variants"""
  100. # Check that the correct bom are selected when computing price_unit for COGS
  101. self.env.company.currency_id = self.env.ref('base.USD')
  102. # Create variant attributes
  103. self.prod_att_1 = self.env['product.attribute'].create({'name': 'Color'})
  104. self.prod_attr1_v1 = self.env['product.attribute.value'].create({'name': 'red', 'attribute_id': self.prod_att_1.id, 'sequence': 1})
  105. self.prod_attr1_v2 = self.env['product.attribute.value'].create({'name': 'blue', 'attribute_id': self.prod_att_1.id, 'sequence': 2})
  106. # Create Product template with variants
  107. self.product_template = self.env['product.template'].create({
  108. 'name': 'Product Template',
  109. 'type': 'product',
  110. 'uom_id': self.uom_unit.id,
  111. 'invoice_policy': 'delivery',
  112. 'categ_id': self.stock_account_product_categ.id,
  113. 'attribute_line_ids': [(0, 0, {
  114. 'attribute_id': self.prod_att_1.id,
  115. 'value_ids': [(6, 0, [self.prod_attr1_v1.id, self.prod_attr1_v2.id])]
  116. })]
  117. })
  118. # Get product variant
  119. self.pt_attr1_v1 = self.product_template.attribute_line_ids[0].product_template_value_ids[0]
  120. self.pt_attr1_v2 = self.product_template.attribute_line_ids[0].product_template_value_ids[1]
  121. self.variant_1 = self.product_template._get_variant_for_combination(self.pt_attr1_v1)
  122. self.variant_2 = self.product_template._get_variant_for_combination(self.pt_attr1_v2)
  123. def create_simple_bom_for_product(product, name, price):
  124. component = self.env['product.product'].create({
  125. 'name': 'Component ' + name,
  126. 'type': 'product',
  127. 'uom_id': self.uom_unit.id,
  128. 'categ_id': self.stock_account_product_categ.id,
  129. 'standard_price': price
  130. })
  131. self.env['stock.quant'].sudo().create({
  132. 'product_id': component.id,
  133. 'location_id': self.company_data['default_warehouse'].lot_stock_id.id,
  134. 'quantity': 10.0,
  135. })
  136. bom = self.env['mrp.bom'].create({
  137. 'product_tmpl_id': self.product_template.id,
  138. 'product_id': product.id,
  139. 'product_qty': 1.0,
  140. 'type': 'phantom'
  141. })
  142. self.env['mrp.bom.line'].create({
  143. 'product_id': component.id,
  144. 'product_qty': 1.0,
  145. 'bom_id': bom.id
  146. })
  147. create_simple_bom_for_product(self.variant_1, "V1", 20)
  148. create_simple_bom_for_product(self.variant_2, "V2", 10)
  149. def create_post_sale_order(product):
  150. so_vals = {
  151. 'partner_id': self.partner_a.id,
  152. 'partner_invoice_id': self.partner_a.id,
  153. 'partner_shipping_id': self.partner_a.id,
  154. 'order_line': [(0, 0, {
  155. 'name': product.name,
  156. 'product_id': product.id,
  157. 'product_uom_qty': 2,
  158. 'product_uom': product.uom_id.id,
  159. 'price_unit': product.list_price
  160. })],
  161. 'pricelist_id': self.env.ref('product.list0').id,
  162. 'company_id': self.company_data['company'].id,
  163. }
  164. so = self.env['sale.order'].create(so_vals)
  165. # Validate the SO
  166. so.action_confirm()
  167. # Deliver the three finished products
  168. pick = so.picking_ids
  169. # To check the products on the picking
  170. wiz_act = pick.button_validate()
  171. wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
  172. wiz.process()
  173. # Create the invoice
  174. so._create_invoices()
  175. invoice = so.invoice_ids
  176. invoice.action_post()
  177. return invoice
  178. # Create a SO for variant 1
  179. self.invoice_1 = create_post_sale_order(self.variant_1)
  180. self.invoice_2 = create_post_sale_order(self.variant_2)
  181. def check_cogs_entry_values(invoice, expected_value):
  182. aml = invoice.line_ids
  183. aml_expense = aml.filtered(lambda l: l.display_type == 'cogs' and l.debit > 0)
  184. aml_output = aml.filtered(lambda l: l.display_type == 'cogs' and l.credit > 0)
  185. self.assertEqual(aml_expense.debit, expected_value, "Cost of Good Sold entry missing or mismatching for variant")
  186. self.assertEqual(aml_output.credit, expected_value, "Cost of Good Sold entry missing or mismatching for variant")
  187. # Check that the cost of Good Sold entries for variant 1 are equal to 2 * 20 = 40
  188. check_cogs_entry_values(self.invoice_1, 40)
  189. # Check that the cost of Good Sold entries for variant 2 are equal to 2 * 10 = 20
  190. check_cogs_entry_values(self.invoice_2, 20)
  191. def test_anglo_saxo_return_and_credit_note(self):
  192. """
  193. When posting a credit note for a returned kit, the value of the anglo-saxo lines
  194. should be based on the returned component's value
  195. """
  196. self.stock_account_product_categ.property_cost_method = 'fifo'
  197. kit = self._create_product('Simple Kit', 'product', 0)
  198. component = self._create_product('Compo A', 'product', 0)
  199. kit.property_account_expense_id = self.company_data['default_account_expense']
  200. self.env['mrp.bom'].create({
  201. 'product_tmpl_id': kit.product_tmpl_id.id,
  202. 'product_qty': 1.0,
  203. 'type': 'phantom',
  204. 'bom_line_ids': [(0, 0, {'product_id': component.id, 'product_qty': 1.0})]
  205. })
  206. # Receive 3 components: one @10, one @20 and one @60
  207. in_moves = self.env['stock.move'].create([{
  208. 'name': 'IN move @%s' % p,
  209. 'product_id': component.id,
  210. 'location_id': self.env.ref('stock.stock_location_suppliers').id,
  211. 'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
  212. 'product_uom': component.uom_id.id,
  213. 'product_uom_qty': 1,
  214. 'price_unit': p,
  215. } for p in [10, 20, 60]])
  216. in_moves._action_confirm()
  217. in_moves.quantity_done = 1
  218. in_moves._action_done()
  219. # Sell 3 kits
  220. so = self.env['sale.order'].create({
  221. 'partner_id': self.env.ref('base.res_partner_1').id,
  222. 'order_line': [
  223. (0, 0, {
  224. 'name': kit.name,
  225. 'product_id': kit.id,
  226. 'product_uom_qty': 3.0,
  227. 'product_uom': kit.uom_id.id,
  228. 'price_unit': 100,
  229. 'tax_id': False,
  230. })],
  231. })
  232. so.action_confirm()
  233. # Deliver the components: 1@10, then 1@20 and then 1@60
  234. pickings = []
  235. picking = so.picking_ids
  236. while picking:
  237. pickings.append(picking)
  238. picking.move_ids.quantity_done = 1
  239. action = picking.button_validate()
  240. if isinstance(action, dict):
  241. wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
  242. wizard.process()
  243. picking = picking.backorder_ids
  244. invoice = so._create_invoices()
  245. invoice.action_post()
  246. # Receive one @100
  247. in_moves = self.env['stock.move'].create({
  248. 'name': 'IN move @100',
  249. 'product_id': component.id,
  250. 'location_id': self.env.ref('stock.stock_location_suppliers').id,
  251. 'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
  252. 'product_uom': component.uom_id.id,
  253. 'product_uom_qty': 1,
  254. 'price_unit': 100,
  255. })
  256. in_moves._action_confirm()
  257. in_moves.quantity_done = 1
  258. in_moves._action_done()
  259. # Return the second picking (i.e. one component @20)
  260. ctx = {'active_id': pickings[1].id, 'active_model': 'stock.picking'}
  261. return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
  262. return_picking_id, dummy = return_wizard._create_returns()
  263. return_picking = self.env['stock.picking'].browse(return_picking_id)
  264. return_picking.move_ids.quantity_done = 1
  265. return_picking.button_validate()
  266. # Add a credit note for the returned kit
  267. ctx = {'active_model': 'account.move', 'active_ids': invoice.ids}
  268. refund_wizard = self.env['account.move.reversal'].with_context(ctx).create({
  269. 'refund_method': 'refund',
  270. 'journal_id': invoice.journal_id.id,
  271. })
  272. action = refund_wizard.reverse_moves()
  273. reverse_invoice = self.env['account.move'].browse(action['res_id'])
  274. with Form(reverse_invoice) as reverse_invoice_form:
  275. with reverse_invoice_form.invoice_line_ids.edit(0) as line:
  276. line.quantity = 1
  277. reverse_invoice.action_post()
  278. amls = reverse_invoice.line_ids
  279. stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
  280. self.assertEqual(stock_out_aml.debit, 20, 'Should be to the value of the returned component')
  281. self.assertEqual(stock_out_aml.credit, 0)
  282. cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
  283. self.assertEqual(cogs_aml.debit, 0)
  284. self.assertEqual(cogs_aml.credit, 20, 'Should be to the value of the returned component')
  285. def test_anglo_saxo_return_and_create_invoice(self):
  286. """
  287. When creating an invoice for a returned kit, the value of the anglo-saxo lines
  288. should be based on the returned component's value
  289. """
  290. self.stock_account_product_categ.property_cost_method = 'fifo'
  291. kit = self._create_product('Simple Kit', 'product', 0)
  292. component = self._create_product('Compo A', 'product', 0)
  293. (kit + component).invoice_policy = 'delivery'
  294. kit.property_account_expense_id = self.company_data['default_account_expense']
  295. self.env['mrp.bom'].create({
  296. 'product_tmpl_id': kit.product_tmpl_id.id,
  297. 'product_qty': 1.0,
  298. 'type': 'phantom',
  299. 'bom_line_ids': [(0, 0, {'product_id': component.id, 'product_qty': 1.0})]
  300. })
  301. # Receive 3 components: one @10, one @20 and one @60
  302. in_moves = self.env['stock.move'].create([{
  303. 'name': 'IN move @%s' % p,
  304. 'product_id': component.id,
  305. 'location_id': self.env.ref('stock.stock_location_suppliers').id,
  306. 'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
  307. 'product_uom': component.uom_id.id,
  308. 'product_uom_qty': 1,
  309. 'price_unit': p,
  310. } for p in [10, 20, 60]])
  311. in_moves._action_confirm()
  312. in_moves.quantity_done = 1
  313. in_moves._action_done()
  314. # Sell 3 kits
  315. so = self.env['sale.order'].create({
  316. 'partner_id': self.env.ref('base.res_partner_1').id,
  317. 'order_line': [
  318. (0, 0, {
  319. 'name': kit.name,
  320. 'product_id': kit.id,
  321. 'product_uom_qty': 3.0,
  322. 'product_uom': kit.uom_id.id,
  323. 'price_unit': 100,
  324. 'tax_id': False,
  325. })],
  326. })
  327. so.action_confirm()
  328. # Deliver the components: 1@10, then 1@20 and then 1@60
  329. pickings = []
  330. picking = so.picking_ids
  331. while picking:
  332. pickings.append(picking)
  333. picking.move_ids.quantity_done = 1
  334. action = picking.button_validate()
  335. if isinstance(action, dict):
  336. wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
  337. wizard.process()
  338. picking = picking.backorder_ids
  339. invoice = so._create_invoices()
  340. invoice.action_post()
  341. # Receive one @100
  342. in_moves = self.env['stock.move'].create({
  343. 'name': 'IN move @100',
  344. 'product_id': component.id,
  345. 'location_id': self.env.ref('stock.stock_location_suppliers').id,
  346. 'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
  347. 'product_uom': component.uom_id.id,
  348. 'product_uom_qty': 1,
  349. 'price_unit': 100,
  350. })
  351. in_moves._action_confirm()
  352. in_moves.quantity_done = 1
  353. in_moves._action_done()
  354. # Return the second picking (i.e. one component @20)
  355. ctx = {'active_id': pickings[1].id, 'active_model': 'stock.picking'}
  356. return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
  357. return_picking_id, dummy = return_wizard._create_returns()
  358. return_picking = self.env['stock.picking'].browse(return_picking_id)
  359. return_picking.move_ids.quantity_done = 1
  360. return_picking.button_validate()
  361. # Create a new invoice for the returned kit
  362. ctx = {'active_model': 'sale.order', 'active_ids': so.ids}
  363. create_invoice_wizard = self.env['sale.advance.payment.inv'].with_context(ctx).create(
  364. {'advance_payment_method': 'delivered'})
  365. create_invoice_wizard.create_invoices()
  366. reverse_invoice = so.invoice_ids[-1]
  367. with Form(reverse_invoice) as reverse_invoice_form:
  368. with reverse_invoice_form.invoice_line_ids.edit(0) as line:
  369. line.quantity = 1
  370. reverse_invoice.action_post()
  371. amls = reverse_invoice.line_ids
  372. stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out'])
  373. self.assertEqual(stock_out_aml.debit, 20, 'Should be to the value of the returned component')
  374. self.assertEqual(stock_out_aml.credit, 0)
  375. cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense'])
  376. self.assertEqual(cogs_aml.debit, 0)
  377. self.assertEqual(cogs_aml.credit, 20, 'Should be to the value of the returned component')
  378. def test_kit_avco_fully_owned_and_delivered_invoice_post_delivery(self):
  379. self.stock_account_product_categ.property_cost_method = 'average'
  380. compo01 = self._create_product('Compo 01', 'product', 10)
  381. compo02 = self._create_product('Compo 02', 'product', 20)
  382. kit = self._create_product('Kit', 'product', 0)
  383. (compo01 + compo02 + kit).invoice_policy = 'delivery'
  384. self.env['stock.quant']._update_available_quantity(compo01, self.company_data['default_warehouse'].lot_stock_id, 1, owner_id=self.partner_b)
  385. self.env['stock.quant']._update_available_quantity(compo02, self.company_data['default_warehouse'].lot_stock_id, 1, owner_id=self.partner_b)
  386. self.env['mrp.bom'].create({
  387. 'product_id': kit.id,
  388. 'product_tmpl_id': kit.product_tmpl_id.id,
  389. 'product_uom_id': kit.uom_id.id,
  390. 'product_qty': 1.0,
  391. 'type': 'phantom',
  392. 'bom_line_ids': [
  393. (0, 0, {'product_id': compo01.id, 'product_qty': 1.0}),
  394. (0, 0, {'product_id': compo02.id, 'product_qty': 1.0}),
  395. ],
  396. })
  397. so = self.env['sale.order'].create({
  398. 'partner_id': self.partner_a.id,
  399. 'order_line': [
  400. (0, 0, {
  401. 'name': kit.name,
  402. 'product_id': kit.id,
  403. 'product_uom_qty': 1.0,
  404. 'product_uom': kit.uom_id.id,
  405. 'price_unit': 5,
  406. 'tax_id': False,
  407. })],
  408. })
  409. so.action_confirm()
  410. so.picking_ids.move_ids.quantity_done = 1
  411. so.picking_ids.button_validate()
  412. invoice = so._create_invoices()
  413. invoice.action_post()
  414. # COGS should not exist because the products are owned by an external partner
  415. amls = invoice.line_ids
  416. self.assertRecordValues(amls, [
  417. # pylint: disable=bad-whitespace
  418. {'account_id': self.company_data['default_account_revenue'].id, 'debit': 0, 'credit': 5},
  419. {'account_id': self.company_data['default_account_receivable'].id, 'debit': 5, 'credit': 0},
  420. ])
  421. def test_kit_avco_partially_owned_and_delivered_invoice_post_delivery(self):
  422. self.stock_account_product_categ.property_cost_method = 'average'
  423. compo01 = self._create_product('Compo 01', 'product', 10)
  424. compo02 = self._create_product('Compo 02', 'product', 20)
  425. kit = self._create_product('Kit', 'product', 0)
  426. (compo01 + compo02 + kit).invoice_policy = 'delivery'
  427. self.env['stock.quant']._update_available_quantity(compo01, self.company_data['default_warehouse'].lot_stock_id, 1, owner_id=self.partner_b)
  428. self.env['stock.quant']._update_available_quantity(compo01, self.company_data['default_warehouse'].lot_stock_id, 1)
  429. self.env['stock.quant']._update_available_quantity(compo02, self.company_data['default_warehouse'].lot_stock_id, 1, owner_id=self.partner_b)
  430. self.env['stock.quant']._update_available_quantity(compo02, self.company_data['default_warehouse'].lot_stock_id, 1)
  431. self.env['mrp.bom'].create({
  432. 'product_id': kit.id,
  433. 'product_tmpl_id': kit.product_tmpl_id.id,
  434. 'product_uom_id': kit.uom_id.id,
  435. 'product_qty': 1.0,
  436. 'type': 'phantom',
  437. 'bom_line_ids': [
  438. (0, 0, {'product_id': compo01.id, 'product_qty': 1.0}),
  439. (0, 0, {'product_id': compo02.id, 'product_qty': 1.0}),
  440. ],
  441. })
  442. so = self.env['sale.order'].create({
  443. 'partner_id': self.partner_a.id,
  444. 'order_line': [
  445. (0, 0, {
  446. 'name': kit.name,
  447. 'product_id': kit.id,
  448. 'product_uom_qty': 2.0,
  449. 'product_uom': kit.uom_id.id,
  450. 'price_unit': 5,
  451. 'tax_id': False,
  452. })],
  453. })
  454. so.action_confirm()
  455. so.picking_ids.move_line_ids.qty_done = 1
  456. so.picking_ids.button_validate()
  457. invoice = so._create_invoices()
  458. invoice.action_post()
  459. # COGS should not exist because the products are owned by an external partner
  460. amls = invoice.line_ids
  461. self.assertRecordValues(amls, [
  462. # pylint: disable=bad-whitespace
  463. {'account_id': self.company_data['default_account_revenue'].id, 'debit': 0, 'credit': 10},
  464. {'account_id': self.company_data['default_account_receivable'].id, 'debit': 10, 'credit': 0},
  465. {'account_id': self.company_data['default_account_stock_out'].id, 'debit': 0, 'credit': 30},
  466. {'account_id': self.company_data['default_account_expense'].id, 'debit': 30, 'credit': 0},
  467. ])