[IMP] stock_account_anglo_saxon_cogs_kit: correct account in vendor bills for purchase of consumable kit

This commit is contained in:
JordiMForgeFlow
2024-08-08 13:28:16 +02:00
parent 61a2f81426
commit 46f214092c
4 changed files with 141 additions and 82 deletions

View File

@@ -4,7 +4,7 @@
"name": "Stock Account Anglo Saxon COGS Kit",
"category": "Accounting",
"version": "16.0.1.0.0",
"depends": ["sale_mrp"],
"depends": ["sale_mrp", "purchase_mrp"],
"data": [],
"author": "ForgeFlow, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-financial-tools",

View File

@@ -12,3 +12,9 @@ class AccountMoveLine(models.Model):
p.type == "product" and p.valuation == "real_time"
for p in self.sale_line_ids.mapped("move_ids.product_id")
)
def _can_use_stock_accounts(self):
return super()._can_use_stock_accounts() or any(
p.type == "product" and p.valuation == "real_time"
for p in self.purchase_line_id.mapped("move_ids.product_id")
)

View File

@@ -1,20 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Stock Account Anglo Saxon COGS Kit</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
@@ -369,7 +368,7 @@ ul.auto-toc {
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:95246b5c8140145c330025892f1165ea1839841376ce1796732d29fb5b4c1962
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/16.0/stock_account_anglo_saxon_cogs_kit"><img alt="OCA/account-financial-tools" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/account-financial-tools-16-0/account-financial-tools-16-0-stock_account_anglo_saxon_cogs_kit"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-financial-tools/tree/16.0/stock_account_anglo_saxon_cogs_kit"><img alt="OCA/account-financial-tools" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-financial-tools-16-0/account-financial-tools-16-0-stock_account_anglo_saxon_cogs_kit"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>The Odoo Official documentation recommends to use consumable for kit products and only to be
storable in case Anglosaxon accounting:</p>
<p><a class="reference external" href="https://www.odoo.com/documentation/16.0/applications/inventory_and_mrp/manufacturing/management/kit_shipping.html">https://www.odoo.com/documentation/16.0/applications/inventory_and_mrp/manufacturing/management/kit_shipping.html</a></p>
@@ -382,17 +381,17 @@ storable if they dont want to track quantities for the kit.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#bug-tracker" id="id1">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id5">Maintainers</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-1">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-5">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id1">Bug Tracker</a></h1>
<h1><a class="toc-backref" href="#toc-entry-1">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-financial-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
@@ -400,15 +399,15 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id2">Credits</a></h1>
<h1><a class="toc-backref" href="#toc-entry-2">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id3">Authors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-3">Authors</a></h2>
<ul class="simple">
<li>ForgeFlow</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id4">Contributors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-4">Contributors</a></h2>
<ul class="simple">
<li>ForgeFlow, S.L. (<a class="reference external" href="https://www.forgeflow.com">https://www.forgeflow.com</a>)
* Marina Alapont &lt;<a class="reference external" href="mailto:marina.alapont&#64;forgeflow.com">marina.alapont&#64;forgeflow.com</a>&gt;
@@ -416,14 +415,14 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id5">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-5">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
<p><a class="reference external" href="https://github.com/MarinaAForgeFlow"><img alt="MarinaAForgeFlow" src="https://github.com/MarinaAForgeFlow.png?size=40px" /></a> <a class="reference external" href="https://github.com/AaronHForgeFlow"><img alt="AaronHForgeFlow" src="https://github.com/AaronHForgeFlow.png?size=40px" /></a></p>
<p><a class="reference external image-reference" href="https://github.com/MarinaAForgeFlow"><img alt="MarinaAForgeFlow" src="https://github.com/MarinaAForgeFlow.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/AaronHForgeFlow"><img alt="AaronHForgeFlow" src="https://github.com/AaronHForgeFlow.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/16.0/stock_account_anglo_saxon_cogs_kit">OCA/account-financial-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>

View File

@@ -17,31 +17,21 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
cls.ProductCategory = cls.env["product.category"]
cls.categ_unit = cls.env.ref("uom.product_uom_categ_unit")
def test_01_sale_mrp_anglo_saxon(self):
"""Test sale order for kit, deliver and invoice and ensure
COGS is hit
"""
self.env.company.currency_id = self.env.ref("base.USD")
self.uom_unit = self.UoM.create(
cls.env.company.currency_id = cls.env.ref("base.USD")
cls.uom_unit = cls.UoM.create(
{
"name": "Test-Unit",
"category_id": self.categ_unit.id,
"category_id": cls.categ_unit.id,
"factor": 1,
"uom_type": "bigger",
"rounding": 1.0,
}
)
self.company = self.env.ref("base.main_company")
self.company.anglo_saxon_accounting = True
self.partner = self.env.ref("base.res_partner_1")
self.category = self.env.ref("product.product_category_1").copy(
{
"name": "Test category",
"property_valuation": "real_time",
"property_cost_method": "fifo",
}
)
account_receiv = self.env["account.account"].create(
# Configure company
cls.company = cls.env.ref("base.main_company")
cls.company.anglo_saxon_accounting = True
# Create accounts
cls.account_receiv = cls.env["account.account"].create(
{
"name": "Receivable",
"code": "RCV00",
@@ -49,7 +39,7 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
"reconcile": True,
}
)
account_expense = self.env["account.account"].create(
cls.account_expense = cls.env["account.account"].create(
{
"name": "Expense",
"code": "EXP00",
@@ -57,7 +47,7 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
"reconcile": True,
}
)
account_input = self.env["account.account"].create(
cls.account_input = cls.env["account.account"].create(
{
"name": "Input",
"code": "IN00",
@@ -65,7 +55,7 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
"reconcile": True,
}
)
account_output = self.env["account.account"].create(
cls.account_output = cls.env["account.account"].create(
{
"name": "Output",
"code": "OUT00",
@@ -73,7 +63,7 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
"reconcile": True,
}
)
account_valuation = self.env["account.account"].create(
cls.account_valuation = cls.env["account.account"].create(
{
"name": "Valuation",
"code": "STV00",
@@ -81,43 +71,78 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
"reconcile": True,
}
)
self.partner.property_account_receivable_id = account_receiv
self.category.property_stock_account_input_categ_id = account_input
self.category.property_stock_account_output_categ_id = account_output
self.category.property_stock_valuation_account_id = account_valuation
self.category.property_account_expense_categ_id = account_expense
self.category.property_stock_journal = self.env["account.journal"].create(
# Configure partner and products
cls.partner = cls.env.ref("base.res_partner_1")
cls.category = cls.env.ref("product.product_category_1").copy(
{
"name": "Test category",
"property_valuation": "real_time",
"property_cost_method": "fifo",
}
)
cls.partner.property_account_receivable_id = cls.account_receiv
cls.category.property_stock_account_input_categ_id = cls.account_input
cls.category.property_stock_account_output_categ_id = cls.account_output
cls.category.property_stock_valuation_account_id = cls.account_valuation
cls.category.property_account_expense_categ_id = cls.account_expense
cls.category.property_stock_journal = cls.env["account.journal"].create(
{"name": "Stock journal", "type": "sale", "code": "STK00"}
)
Product = self.env["product.product"]
self.finished_product = Product.create(
Product = cls.env["product.product"]
cls.finished_product = Product.create(
{
"name": "KIT product",
"type": "consu",
"uom_id": self.uom_unit.id,
"uom_id": cls.uom_unit.id,
"invoice_policy": "delivery",
"categ_id": self.category.id,
"categ_id": cls.category.id,
}
)
self.component1 = Product.create(
cls.component1 = Product.create(
{
"name": "Component 1",
"type": "product",
"uom_id": self.uom_unit.id,
"categ_id": self.category.id,
"uom_id": cls.uom_unit.id,
"categ_id": cls.category.id,
"standard_price": 20,
}
)
self.component2 = Product.create(
cls.component2 = Product.create(
{
"name": "Component 2",
"type": "product",
"uom_id": self.uom_unit.id,
"categ_id": self.category.id,
"uom_id": cls.uom_unit.id,
"categ_id": cls.category.id,
"standard_price": 10,
}
)
kit = cls.env["mrp.bom"].create(
{
"product_tmpl_id": cls.finished_product.product_tmpl_id.id,
"product_qty": 1.0,
"type": "phantom",
}
)
BomLine = cls.env["mrp.bom.line"]
BomLine.create(
{
"product_id": cls.component1.id,
"product_qty": 2.0,
"bom_id": kit.id,
}
)
BomLine.create(
{
"product_id": cls.component2.id,
"product_qty": 1.0,
"bom_id": kit.id,
}
)
def test_01_sale_mrp_anglo_saxon(self):
"""Test sale order for kit, deliver and invoice and ensure
COGS is hit
"""
self.env["stock.quant"].create(
{
"product_id": self.component1.id,
@@ -132,28 +157,6 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
"quantity": 3.0,
}
)
kit = self.env["mrp.bom"].create(
{
"product_tmpl_id": self.finished_product.product_tmpl_id.id,
"product_qty": 1.0,
"type": "phantom",
}
)
BomLine = self.env["mrp.bom.line"]
BomLine.create(
{
"product_id": self.component1.id,
"product_qty": 2.0,
"bom_id": kit.id,
}
)
BomLine.create(
{
"product_id": self.component2.id,
"product_qty": 1.0,
"bom_id": kit.id,
}
)
# Create a SO for a specific partner for three units of the
# finished product
@@ -200,8 +203,8 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
self.invoice = move_form.save()
self.invoice.action_post()
aml = self.invoice.line_ids
aml_expense = aml.filtered(lambda l: l.account_id == account_expense)
aml_output = aml.filtered(lambda l: l.account_id == account_output)
aml_expense = aml.filtered(lambda l: l.account_id == self.account_expense)
aml_output = aml.filtered(lambda l: l.account_id == self.account_output)
# Check that the cost of Good Sold entries are equal to:
# 2* (2 * 20 + 1 * 10) = 100
self.assertEqual(
@@ -217,14 +220,65 @@ class TestStockAccountAngloSaxonCogsKit(common.TransactionCase):
self.assertNotEqual(
aml.filtered(
lambda ml: ml.product_id == self.finished_product
and ml.account_id == account_expense
and ml.account_id == self.account_expense
),
self.env["account.move.line"],
)
self.assertEqual(
aml.filtered(
lambda ml: ml.product_id == self.component1
and ml.account_id == account_expense
and ml.account_id == self.account_expense
),
self.env["account.move.line"],
)
def test_02_purchase_mrp_anglo_saxon(self):
"""Test purchase order for kit, receive and invoice and ensure
stock input account is hit
"""
# Create a PO for a specific partner for three units of the
# finished product
po_vals = {
"partner_id": self.partner.id,
"order_line": [
(
0,
0,
{
"name": self.finished_product.name,
"product_id": self.finished_product.id,
"product_qty": 3,
"product_uom": self.finished_product.uom_id.id,
"price_unit": 100,
},
)
],
"company_id": self.company.id,
}
self.po = self.env["purchase.order"].create(po_vals)
# Validate the PO
self.po.button_confirm()
# Receive the three finished products
pick = self.po.picking_ids
# To check the products on the picking
self.assertEqual(
pick.move_line_ids.mapped("product_id"), self.component1 | self.component2
)
for ml in pick.move_line_ids:
ml.qty_done = ml.reserved_uom_qty
pick._action_done()
# Create the invoice
action = self.po.action_create_invoice()
self.invoice = self.env["account.move"].browse(action["res_id"])
self.invoice.invoice_date = self.invoice.date
self.invoice.action_post()
aml = self.invoice.line_ids
aml_expense = aml.filtered(lambda l: l.account_id == self.account_expense)
aml_input = aml.filtered(lambda l: l.account_id == self.account_input)
# No line with expense account
self.assertFalse(aml_expense)
# Check that there is line with stock input account and amount:
# 3*100 = 300
self.assertEqual(
sum(aml_input.mapped("debit")), 300, "GDNI missing or mismatching"
)