-
-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A new module to scan QR-bill or to import PDF files
- Loading branch information
Showing
23 changed files
with
642 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import wizard | ||
from . import tools |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright 2020 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
{ | ||
"name": "Switzerland - QR-bill scan", | ||
"summary": "Scan QR-bill to create vendor bills", | ||
"version": "13.0.1.0.0", | ||
"author": "Camptocamp,Odoo Community Association (OCA)", | ||
"category": "Localization", | ||
"website": "https://github.com/OCA/l10n-switzerland", | ||
"license": "AGPL-3", | ||
"depends": ["l10n_ch", "account_invoice_import"], | ||
"data": ["wizard/account_invoice_import_view.xml"], | ||
"auto_install": False, | ||
"installable": True, | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Copyright 2020 Camptocamp | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
from odoo import api, models | ||
|
||
|
||
class BusinessDocumentImport(models.AbstractModel): | ||
_name = "business.document.import" | ||
_description = "Common methods to import business documents" | ||
|
||
@api.model | ||
def _hook_match_partner( | ||
self, partner_dict, chatter_msg, domain, partner_type_label | ||
): | ||
# TODO search by iban | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
In order to configure the type of invoice created per partner. | ||
|
||
Got to: | ||
- `Accounting` or `Invoicing` | ||
- `Configuration` | ||
- `Import Vendor Bill` | ||
- `Import Bills` | ||
|
||
you will find the configuration of each partner. | ||
|
||
|
||
In order to automatically create bank accounts on QR-bill import: | ||
|
||
Go to: | ||
|
||
- `Accounting` or `Invoicing` | ||
- `Configuration` | ||
- `Settings` | ||
- `Invoice Import` | ||
|
||
And activate `Auto-create Bank` | ||
|
||
.. figure:: ../static/description/auto-create-bank.png | ||
:alt: auto create bank | ||
:width: 600 px |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Yannick Vaucher <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Import Vendor bills with a PayEye scanner. | ||
|
||
The QR bills are introduced in Switzerland since the 30th of June 2020. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
This module depends on `account_invoice_import` which is located in `edi` and has dependencies on `community-data-files`. | ||
So the repository `edi` and `community-data-files` must be present on the system. | ||
|
||
Optionally you can also import Vendor bills directly from a PDF. | ||
To enable this feature those additional dependencies must be | ||
installed on your Odoo server: | ||
|
||
pip install pyzbar,numpy,pdf2image,opencv-python | ||
|
||
If you are looking to generate QR-bill, this feature is already available | ||
in Odoo in the Switzerland localization module (`l10n_ch`). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
TODO: | ||
|
||
- Read the Swico standard from Billing info | ||
- Read Alternative parameters |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
To import your invoices Launch the wizard `Invoicing` -> `Vendors` -> `Import Vendor Bill`. | ||
|
||
In there you can either directly the QR code of the QR-bill or you can import a PDF/image file. | ||
|
||
If it's the first time you import a Bill from a Vendor the wizard will ask you to create the partner and to set a import configuration for that partner | ||
A configuration let you set which expense account you want the invoice to be imported with. | ||
|
||
|
||
To quickly import a serie of invoice, use the button `Import Next Invoice` from the Vendor bill form. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import test_scan_qrbill |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
# Copyright 2020 Camptocamp (http://www.camptocamp.com/) | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
import base64 | ||
|
||
from odoo import tools | ||
from odoo.exceptions import UserError | ||
from odoo.modules.module import get_resource_path | ||
from odoo.tests import common | ||
|
||
from odoo.addons.l10n_ch_qr_bill_scan.tools import QR | ||
|
||
# QR Reference | ||
QRR = "210000000003139471430009017" | ||
|
||
# Creditor Reference | ||
CF = "RF18539007547034" | ||
|
||
CH_IBAN = "CH15 3881 5158 3845 3843 7" | ||
QR_IBAN = "CH21 3080 8001 2345 6782 7" | ||
|
||
|
||
SCAN_DATA = [ | ||
"SPC", | ||
"0200", | ||
"1", | ||
CH_IBAN.replace(" ", ""), | ||
"S", | ||
"Camptocamp", | ||
"EPFL Innovation Park", | ||
"Bldg A", | ||
"1015", | ||
"Lausanne", | ||
"CH", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"1949.75", | ||
"CHF", | ||
"S", | ||
"Your company", | ||
"Bahnhofplatz", | ||
"10", | ||
"3011", | ||
"Bern", | ||
"CH", | ||
"{ref_type}", | ||
"{ref}", | ||
"Instruction of 15.09.2019", | ||
"EPD", | ||
"", | ||
"", | ||
"", | ||
] | ||
|
||
|
||
class TestScanQRBill(common.SavepointCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
super().setUpClass() | ||
cls.env.user.company_id.invoice_import_create_bank_account = True | ||
cls.supplier = cls.env["res.partner"].create( | ||
{ | ||
"name": "Camptocamp", | ||
"street": "EPFL Innovation Park", | ||
"street2": "Bldg A", | ||
"zip": "1015", | ||
"city": "Lausanne", | ||
"country_id": cls.env.ref("base.ch").id, | ||
} | ||
) | ||
cls.supplier.supplier_rank = 1 | ||
|
||
cls.expense_account = cls.env["account.account"].create( | ||
{ | ||
"code": "612AII", | ||
"name": "expense account invoice import", | ||
"user_type_id": cls.env.ref("account.data_account_type_expenses").id, | ||
} | ||
) | ||
cls.env["account.invoice.import.config"].create( | ||
{ | ||
"name": "Camptocamp - one line no product", | ||
"partner_id": cls.supplier.id, | ||
"invoice_line_method": "1line_no_product", | ||
"account_id": cls.expense_account.id, | ||
} | ||
) | ||
|
||
def wiz_import_invoice_file(self, file_path, file_name): | ||
""" Import a file of a vendor bill """ | ||
with tools.file_open(file_path, "rb") as f: | ||
invoice_file = base64.b64encode(f.read()) | ||
wiz = self.env["account.invoice.import"].create({}) | ||
wiz.invoice_file = invoice_file | ||
wiz.invoice_filename = file_name | ||
return wiz | ||
|
||
def import_invoice_file(self, file_path, file_name): | ||
""" Import scanned data from a vendor bill | ||
And return the created invoice | ||
""" | ||
wiz = self.wiz_import_invoice_file(file_path, file_name) | ||
res = wiz.import_invoice() | ||
if res.get("res_model") == "account.move": | ||
invoice = self.env["account.move"].browse(res["res_id"]) | ||
return invoice | ||
return None | ||
|
||
def wiz_import_invoice_scan(self, invoice_scan): | ||
""" Import scanned data from a vendor bill """ | ||
wiz = self.env["account.invoice.import"].create({}) | ||
wiz.invoice_scan = invoice_scan | ||
return wiz | ||
|
||
def import_invoice_scan(self, invoice_scan): | ||
""" Import scanned data from a vendor bill | ||
And return the created invoice | ||
""" | ||
wiz = self.wiz_import_invoice_scan(invoice_scan) | ||
res = wiz.import_invoice() | ||
if res.get("res_model") == "account.move": | ||
invoice = self.env["account.move"].browse(res["res_id"]) | ||
return invoice | ||
return None | ||
|
||
def test_scan_QR_free_ref(self): | ||
scan_data = SCAN_DATA[:] | ||
scan_data = "\n".join(scan_data).format(ref_type="NON", ref="") | ||
invoice = self.import_invoice_scan(scan_data) | ||
|
||
self.assertEqual(invoice.partner_id, self.supplier) | ||
self.assertFalse(invoice.invoice_payment_ref) | ||
self.assertEqual(invoice.state, "draft") | ||
iban = invoice.invoice_partner_bank_id.acc_number | ||
self.assertEqual(iban, CH_IBAN) | ||
self.assertEqual(invoice.amount_total, 1949.75) | ||
|
||
def test_scan_QR_QRR(self): | ||
scan_data = SCAN_DATA[:] | ||
scan_data[QR.IBAN] = QR_IBAN.replace(" ", "") | ||
scan_data = "\n".join(scan_data).format(ref_type="QRR", ref=QRR) | ||
invoice = self.import_invoice_scan(scan_data) | ||
|
||
self.assertEqual(invoice.partner_id, self.supplier) | ||
self.assertEqual(invoice.invoice_payment_ref, QRR) | ||
self.assertEqual(invoice.state, "draft") | ||
iban = invoice.invoice_partner_bank_id.acc_number | ||
self.assertEqual(iban, QR_IBAN) | ||
self.assertEqual(invoice.amount_total, 1949.75) | ||
|
||
def test_scan_QR_CF(self): | ||
scan_data = SCAN_DATA[:] | ||
scan_data = "\n".join(scan_data).format(ref_type="SCOR", ref=CF) | ||
invoice = self.import_invoice_scan(scan_data) | ||
|
||
self.assertEqual(invoice.partner_id, self.supplier) | ||
self.assertEqual(invoice.invoice_payment_ref, CF) | ||
self.assertEqual(invoice.state, "draft") | ||
iban = invoice.invoice_partner_bank_id.acc_number | ||
self.assertEqual(iban, CH_IBAN) | ||
self.assertEqual(invoice.amount_total, 1949.75) | ||
|
||
def test_scan_QR_new_partner(self): | ||
scan_data = SCAN_DATA | ||
scan_data[QR.CREDITOR_NAME] = "New Vendor" | ||
scan_data = "\n".join(scan_data).format(ref_type="NON", ref="") | ||
wiz = self.wiz_import_invoice_scan(scan_data) | ||
wiz.import_invoice() | ||
|
||
self.assertEqual(wiz.state, "select-partner") | ||
self.assertEqual(wiz.partner_name, "New Vendor") | ||
self.assertEqual(wiz.partner_street, "EPFL Innovation Park Bldg A") | ||
self.assertEqual(wiz.partner_zip, "1015") | ||
self.assertEqual(wiz.partner_city, "Lausanne") | ||
self.assertEqual(wiz.partner_country_id, self.env.ref("base.ch")) | ||
|
||
def test_scan_QR_swico(self): | ||
self.assertTrue(False) | ||
|
||
def test_scan_QR_wrong_swico(self): | ||
self.assertTrue(False) | ||
|
||
def test_scan_QR_extra_first_lines(self): | ||
scan_data = ["", ""] + SCAN_DATA[:] | ||
scan_data = "\n".join(scan_data).format(ref_type="NON", ref="") | ||
|
||
invoice = self.import_invoice_scan(scan_data) | ||
self.assertEqual(invoice.partner_id, self.supplier) | ||
self.assertFalse(invoice.invoice_payment_ref) | ||
self.assertEqual(invoice.state, "draft") | ||
iban = invoice.invoice_partner_bank_id.acc_number | ||
self.assertEqual(iban, CH_IBAN) | ||
self.assertEqual(invoice.amount_total, 1949.75) | ||
|
||
def test_scan_QR_missing_lines(self): | ||
scan_data = SCAN_DATA[:-10] | ||
scan_data = "\n".join(scan_data).format(ref_type="NON", ref="") | ||
|
||
with self.assertRaises(UserError): | ||
self.import_invoice_scan(scan_data) | ||
|
||
def test_scan_QR_wrong_size(self): | ||
scan_data = SCAN_DATA[:15] + SCAN_DATA[20:] | ||
scan_data = "\n".join(scan_data).format(ref_type="NON", ref="") | ||
|
||
with self.assertRaises(UserError): | ||
self.import_invoice_scan(scan_data) | ||
|
||
def test_scan_QR_gibberish(self): | ||
scan_data = "saoeutheSPCoauhoess" + "\n" * 30 | ||
|
||
with self.assertRaises(UserError): | ||
self.import_invoice_scan(scan_data) | ||
|
||
def test_scan_QR_bad_amount(self): | ||
scan_data = SCAN_DATA[:] | ||
scan_data[QR.AMOUNT] = "not a number" | ||
scan_data = "\n".join(scan_data).format(ref_type="NON", ref="") | ||
|
||
with self.assertRaises(UserError): | ||
self.import_invoice_scan(scan_data) | ||
|
||
def test_import_QR_pdf(self): | ||
partner = self.env["res.partner"].create( | ||
{ | ||
"name": "My Company", # this was generated from Odoo | ||
"street": "addr 1", | ||
"street2": "", | ||
"zip": "2074", | ||
"city": "Marin", | ||
"country_id": self.env.ref("base.ch").id, | ||
} | ||
) | ||
partner.supplier_rank = 1 | ||
self.env["account.invoice.import.config"].create( | ||
{ | ||
"name": "Camptocamp - one line no product", | ||
"partner_id": partner.id, | ||
"invoice_line_method": "1line_no_product", | ||
"account_id": self.expense_account.id, | ||
} | ||
) | ||
|
||
invoice_fp = get_resource_path( | ||
"l10n_ch_qr_bill_scan", "tests", "data", "qr-bill.pdf" | ||
) | ||
invoice = self.import_invoice_file(invoice_fp, "qr-bill.pdf") | ||
self.assertTrue(invoice) | ||
self.assertEqual(invoice.invoice_payment_ref, "000000000000000000202000058") | ||
self.assertEqual(invoice.amount_total, 1.0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import re | ||
|
||
# Positions | ||
SPC = 0 | ||
VERSION = 1 | ||
IBAN = 3 | ||
CREDITOR = 4 # Till 10 | ||
CREDITOR_NAME = 5 | ||
AMOUNT = 18 | ||
CURRENCY = 19 | ||
REF = 28 | ||
MSG = 29 | ||
EPD = 30 | ||
BILL_INFO = 31 | ||
|
||
ADR_LEN = 7 | ||
|
||
valid_re = re.compile(r"SPC\n(.*\n){29}EPD") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import QR |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import account_invoice_import |
Oops, something went wrong.