Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions account_invoice_import/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

======================
Account Invoice Import
======================
Expand All @@ -17,7 +13,7 @@ Account Invoice Import
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github
Expand Down Expand Up @@ -123,6 +119,7 @@ Contributors
* Yannick Vaucher <[email protected]>
* Ronald Portier <[email protected]>
* Simone Orsi <[email protected]>
* Marwan BEHILLIL <[email protected]>

Maintainers
~~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions account_invoice_import/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
* Yannick Vaucher <[email protected]>
* Ronald Portier <[email protected]>
* Simone Orsi <[email protected]>
* Marwan BEHILLIL <[email protected]>
31 changes: 13 additions & 18 deletions account_invoice_import/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Account Invoice Import</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="account-invoice-import">
<h1 class="title">Account Invoice Import</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="account-invoice-import">
<h1>Account Invoice Import</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5508553be45215c419ec94d2c75642b4aa77758f9d6e14e705f98693a3621225
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/edi/tree/16.0/account_invoice_import"><img alt="OCA/edi" src="https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/edi-16-0/edi-16-0-account_invoice_import"><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/edi&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/edi/tree/16.0/account_invoice_import"><img alt="OCA/edi" src="https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/edi-16-0/edi-16-0-account_invoice_import"><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/edi&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>This module has been started by lazy accounting users who hate enter they vendor bills manually in Odoo. Almost all companies have several vendor bills to enter regularly in the system from the same vendors: phone bill, electricity bill, Internet access, train tickets, etc. Most of these invoices are available as PDF. If we are able to automatically extract from the PDF the required information to enter the invoice as vendor bill in Odoo, then this module will create it automatically. To know the full story behind the development of this module, read this <a class="reference external" href="http://www.akretion.com/blog/akretions-christmas-present-for-the-odoo-community">blog post</a>.</p>
<p>In order to reliably extract the required information from the invoice, two international standards exists to describe an Invoice in XML:</p>
<ul class="simple">
Expand Down Expand Up @@ -409,7 +404,7 @@ <h1>Account Invoice Import</h1>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>Go to the form view of the suppliers and configure it with the following parameters:</p>
<ul class="simple">
<li>Individual/Company: <em>Company</em></li>
Expand All @@ -424,7 +419,7 @@ <h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>Go to the menu <em>Invoicing &gt; Vendors &gt; Import Vendor Bill</em> and follow the instructions of the wizard. You can also start the wizard from the <em>Accounting Dashboard</em>: on the purchase journal, click on the <em>Upload</em> button.</p>
<p>This module also supports the scenario where you have a draft vendor bill (generated from a purchase order for instance) and you have to update it to comply with the real invoice sent by the vendor: on the form view of the draft vendor bill, click on the button <em>Import Invoice File</em> and follow the instructions of the wizard.</p>
<p>If you have a large volume of invoices to import, you may be interested by the script <strong>mass_invoice_import.py</strong> which is available in the <em>scripts</em> subdirectory of this module. If you run:</p>
Expand All @@ -435,40 +430,41 @@ <h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<p>A particular use case of this script is to have a directory where all the invoices saved are automatically uploaded in Odoo. For that, have a look at the sample script <strong>inotify-sample.sh</strong> available in the same subdirectory. Edit this sample script to adapt it to your needs.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Remove dependency on <em>base_iban</em> and develop a separate glue module between this module and <em>base_iban</em></li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/edi/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
<a class="reference external" href="https://github.com/OCA/edi/issues/new?body=module:%20account_invoice_import%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-5">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-6">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-7">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-7">Contributors</a></h2>
<ul class="simple">
<li>Alexis de Lattre &lt;<a class="reference external" href="mailto:alexis.delattre&#64;akretion.com">alexis.delattre&#64;akretion.com</a>&gt;</li>
<li>Andrea Stirpe &lt;<a class="reference external" href="mailto:a.stirpe&#64;onestein.nl">a.stirpe&#64;onestein.nl</a>&gt;</li>
<li>Nicolas JEUDY &lt;<a class="reference external" href="https://github.com/njeudy">https://github.com/njeudy</a>&gt;</li>
<li>Yannick Vaucher &lt;<a class="reference external" href="mailto:yannick.vaucher&#64;camptocamp.com">yannick.vaucher&#64;camptocamp.com</a>&gt;</li>
<li>Ronald Portier &lt;<a class="reference external" href="mailto:ronald&#64;therp.nl">ronald&#64;therp.nl</a>&gt;</li>
<li>Simone Orsi &lt;<a class="reference external" href="mailto:simone.orsi&#64;camptocamp.com">simone.orsi&#64;camptocamp.com</a>&gt;</li>
<li>Marwan BEHILLIL &lt;<a class="reference external" href="mailto:marwan.behillil&#64;gmail.com">marwan.behillil&#64;gmail.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-8">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" />
Expand All @@ -483,6 +479,5 @@ <h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>
74 changes: 74 additions & 0 deletions account_invoice_import/tests/test_invoice_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,80 @@ def test_import_out_invoice(self):
fields.Date.to_string(inv.invoice_date), parsed_inv["date"]
)

def test_import_in_invoice_special_display_types(self):
parsed_inv = {
"type": "in_invoice",
"journal": {"code": "XXXP2"},
"amount_untaxed": 0.0,
"amount_total": 0.0,
"date": "2017-08-16",
"partner": {"name": "Wood Corner"},
"lines": [
{"line_note": "This is a line note"},
{"sectionheader": "SECTION 1"},
],
}
import_config = {"company": self.company}
parsed_inv["invoice_number"] = "INV-%s" % randint(100000, 999999)
inv = self.env["account.invoice.import"].create_invoice(
parsed_inv, import_config
)
# We expect two created invoice lines corresponding to the note and section
self.assertEqual(len(inv.invoice_line_ids), 2)
types = [line.display_type for line in inv.invoice_line_ids]
self.assertIn("line_note", types)
self.assertIn("line_section", types)
# Special display lines should not have a product linked
for line in inv.invoice_line_ids:
self.assertFalse(line.product_id)

def test_import_in_invoice_mixed_display_types_with_product(self):
parsed_inv = {
"type": "in_invoice",
"journal": {"code": "XXXP2"},
"amount_untaxed": 100.0,
"amount_total": 101.0,
"date": "2017-08-16",
"partner": {"name": "Wood Corner"},
"lines": [
{"sectionheader": "SECTION 1"},
{"line_note": "This is a line note"},
{
"product": {"code": "AII-TEST-PRODUCT"},
"name": "Super test product",
"qty": 2,
"price_unit": 50,
"taxes": [
{
"amount_type": "percent",
"amount": 1.0,
"unece_type_code": "VAT",
"unece_categ_code": "S",
}
],
},
],
}
import_config = {"company": self.company}
parsed_inv["invoice_number"] = "INV-%s" % randint(100000, 999999)
inv = self.env["account.invoice.import"].create_invoice(
parsed_inv, import_config
)
# Expect three lines: section, note, and product
self.assertEqual(len(inv.invoice_line_ids), 3)
types = [line.display_type for line in inv.invoice_line_ids]
self.assertIn("line_section", types)
self.assertIn("line_note", types)
self.assertIn("product", types)
# Check product line exists and has correct product and qty
prod_lines = [
line for line in inv.invoice_line_ids if line.display_type == "product"
]
self.assertEqual(len(prod_lines), 1)
prod_line = prod_lines[0]
self.assertEqual(prod_line.product_id.id, self.product.id)
self.assertEqual(prod_line.quantity, 2)

_fake_email = """
Received: by [email protected]
Message-Id: <[email protected]>
Expand Down
33 changes: 30 additions & 3 deletions account_invoice_import/wizard/account_invoice_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def fallback_parse_pdf_invoice(self, file_data, company):
# 'note': 'Note embedded in the document',
# 'origin': 'Origin note',
# 'lines': [{
# # Regular product line:
# 'product': {
# 'barcode': '4123456000021',
# 'code': 'GZ250',
Expand All @@ -129,9 +130,16 @@ def fallback_parse_pdf_invoice(self, file_data, company):
# 'date_start': '2015-10-01',
# 'date_end': '2015-10-31',
# # date_start and date_end on lines override the global value
# },
# # Section header line (display_type='line_section'):
# {
# 'sectionheader': 'Section Title', # Creates a section header line
# },
# # Note line (display_type='line_note'):
# {
# 'line_note': 'Note text here', # Creates a note line
# }],
# }

# IMPORT CONFIG
# {
# 'company': company recordset, # required field
Expand Down Expand Up @@ -402,6 +410,23 @@ def _prepare_line_vals_nline(self, parsed_inv, import_config, vals, partner):
assert parsed_inv.get("lines")
bdio = self.env["business.document.import"]
for line in parsed_inv["lines"]:
# Handle special display types first
if line.get("line_note"):
il_vals = {
"product_id": None,
"name": line.get("line_note"),
"display_type": "line_note",
}
vals["invoice_line_ids"].append(Command.create(il_vals))
continue
if line.get("sectionheader"):
il_vals = {
"product_id": None,
"name": line.get("sectionheader"),
"display_type": "line_section",
}
vals["invoice_line_ids"].append(Command.create(il_vals))
continue
product = False
if line.get("product"):
product = bdio._match_product(
Expand Down Expand Up @@ -683,9 +708,11 @@ def _pre_process_parsed_inv_rounding(self, parsed_inv, company):
"Product Unit of Measure"
)
for line in parsed_inv.get("lines", []):
line["qty"] = float_round(line["qty"], precision_digits=prec_qty)
if line.get("sectionheader") or line.get("line_note"):
continue
line["qty"] = float_round(line.get("qty", 0), precision_digits=prec_qty)
line["price_unit"] = float_round(
line["price_unit"], precision_digits=prec_price
line.get("price_unit", 0), precision_digits=prec_price
)
line["discount"] = float_round(
line.get("discount", 0), precision_digits=prec_disc
Expand Down