Skip to content

Commit

Permalink
Lint
Browse files Browse the repository at this point in the history
  • Loading branch information
yvaucher committed Jun 25, 2020
1 parent a1bd6e8 commit c255782
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 95 deletions.
Empty file added l10n_ch_qr_bill_scan/README.rst
Empty file.
24 changes: 11 additions & 13 deletions l10n_ch_qr_bill_scan/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
# 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,
"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,
}
2 changes: 1 addition & 1 deletion l10n_ch_qr_bill_scan/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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,pdf2image,cv2,numpy
pip install pyzbar,numpy,pdf2image,opencv-python


If you are looking to generate QR-bill, this feature is already available
Expand Down
2 changes: 1 addition & 1 deletion l10n_ch_qr_bill_scan/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Launch 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
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.


Expand Down
74 changes: 37 additions & 37 deletions l10n_ch_qr_bill_scan/tests/test_scan_qrbill.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
# Copyright 2020 Camptocamp (http://www.camptocamp.com/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import base64
import time

from odoo.addons.account.tests.account_test_classes import AccountingTestCase

from odoo.modules.module import get_resource_path
from odoo import tools
from odoo.exceptions import UserError
from odoo.addons.l10n_ch_qr_bill_scan.tools import QR
from odoo.modules.module import get_resource_path
from odoo.tests import common

from mock import MagicMock
from odoo.addons.l10n_ch_qr_bill_scan.tools import QR

# QR Reference
QRR = "210000000003139471430009017"
Expand Down Expand Up @@ -59,15 +56,13 @@
"",
]

from odoo.tests import common

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(
cls.supplier = cls.env["res.partner"].create(
{
"name": "Camptocamp",
"street": "EPFL Innovation Park",
Expand All @@ -86,16 +81,18 @@ def setUpClass(cls):
"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,
})
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:
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
Expand All @@ -109,8 +106,8 @@ def import_invoice_file(self, file_path, file_name):
"""
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'])
if res.get("res_model") == "account.move":
invoice = self.env["account.move"].browse(res["res_id"])
return invoice
return None

Expand All @@ -127,8 +124,8 @@ def import_invoice_scan(self, invoice_scan):
"""
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'])
if res.get("res_model") == "account.move":
invoice = self.env["account.move"].browse(res["res_id"])
return invoice
return None

Expand Down Expand Up @@ -176,12 +173,12 @@ def test_scan_QR_new_partner(self):
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'))
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)
Expand Down Expand Up @@ -230,7 +227,7 @@ def test_scan_QR_bad_amount(self):
self.import_invoice_scan(scan_data)

def test_import_QR_pdf(self):
partner = self.env['res.partner'].create(
partner = self.env["res.partner"].create(
{
"name": "My Company", # this was generated from Odoo
"street": "addr 1",
Expand All @@ -241,16 +238,19 @@ def test_import_QR_pdf(self):
}
)
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')
self.env['account.invoice.import']
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.)
self.assertEqual(invoice.amount_total, 1.0)
38 changes: 16 additions & 22 deletions l10n_ch_qr_bill_scan/wizard/account_invoice_import.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
# Copyright 2020 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import base64
import logging
import mimetypes

import json

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.modules.module import get_resource_path

from ..tools import QR


logger = logging.getLogger(__name__)

try:
Expand All @@ -38,17 +33,16 @@ class AccountInvoiceImport(models.TransientModel):
invoice_file = fields.Binary(string="PDF, PNG or XML Invoice", required=False)

state = fields.Selection(
selection_add=[
("select-partner", "Select partner"),
],
default="import",
selection_add=[("select-partner", "Select partner")], default="import",
)

partner_name = fields.Char("Name", readonly=True)
partner_street = fields.Char("Street", readonly=True)
partner_zip = fields.Char("ZIP", readonly=True)
partner_city = fields.Char("City", readonly=True)
partner_country_id = fields.Many2one(string="Country", comodel_name="res.country", readonly=True)
partner_country_id = fields.Many2one(
string="Country", comodel_name="res.country", readonly=True
)

def get_parsed_invoice(self):
if self.invoice_scan:
Expand Down Expand Up @@ -82,11 +76,10 @@ def _parse_billing_info(self, bill_info):
def parse_qrbill(self, qr_data):
qr_match = QR.valid_re.search(qr_data)
if not qr_match:
raise UserError(
_("The data doesn't match with a QR-Code from a QR-bill"))
raise UserError(_("The data doesn't match with a QR-Code from a QR-bill"))
# Ignore everything before "SPC"
if qr_match.start() > 0:
qr_data = qr_data[qr_match.start():]
qr_data = qr_data[qr_match.start() :]

qr_data = qr_data.split("\n")

Expand All @@ -96,11 +89,11 @@ def parse_qrbill(self, qr_data):
raise UserError(_("The {} "))
parsed_inv = {
"iban": qr_data[QR.IBAN],
"partner": self._get_qr_address(qr_data[QR.CREDITOR:QR.CREDITOR + QR.ADR_LEN]),
"partner": self._get_qr_address(
qr_data[QR.CREDITOR : QR.CREDITOR + QR.ADR_LEN]
),
"amount_total": amount,
"currency": {
"iso": qr_data[QR.CURRENCY],
},
"currency": {"iso": qr_data[QR.CURRENCY]},
"invoice_number": qr_data[QR.REF],
"description": qr_data[QR.MSG],
}
Expand All @@ -117,7 +110,7 @@ def _patch_swiss_qr_code(self, bill_img):
# find an replace Swiss Cross as it prevent ZBar from detecting the
# QR Code
ch_cross_img = cv2.imread(
get_resource_path('l10n_ch_qr_bill_scan', 'data', 'CH-Cross_7mm.png')
get_resource_path("l10n_ch_qr_bill_scan", "data", "CH-Cross_7mm.png")
)
ch_cross_img = cv2.resize(ch_cross_img, (55, 55))
res = cv2.matchTemplate(ch_cross_img, bill_img, cv2.TM_CCOEFF_NORMED)
Expand All @@ -127,16 +120,16 @@ def _patch_swiss_qr_code(self, bill_img):
y = locations[0][0]
x = locations[1][0]
patch_img = cv2.imread(
get_resource_path('l10n_ch_qr_bill_scan', 'data', 'QR-patch.png')
get_resource_path("l10n_ch_qr_bill_scan", "data", "QR-patch.png")
)
bill_img[y:y + patch_img.shape[0], x:x + patch_img.shape[1]] = patch_img
bill_img[y : y + patch_img.shape[0], x : x + patch_img.shape[1]] = patch_img
return bill_img

def parse_pdf_invoice(self, file_data):
if decode:
logger.debug("Search for Swiss QR-Code in PDF file")
# TODO support multiple pages
pdf_img = pdf2image.convert_from_bytes(file_data)[0].convert('RGB')
pdf_img = pdf2image.convert_from_bytes(file_data)[0].convert("RGB")
pdf_img = np.array(pdf_img)
# Convert RGB to BGR
pdf_img = pdf_img[:, :, ::-1].copy()
Expand All @@ -154,7 +147,8 @@ def _hook_no_partner_found(self, partner_dict):
"""Switch wizard to partner creation.
"""
country = self.env["res.country"].search(
[("code", "=", partner_dict["country_code"])], limit=1)
[("code", "=", partner_dict["country_code"])], limit=1
)
wiz_vals = {
"state": "select-partner",
"partner_name": partner_dict["name"],
Expand Down
62 changes: 41 additions & 21 deletions l10n_ch_qr_bill_scan/wizard/account_invoice_import_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,72 @@
<field name="domain">[]</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'res_partner_search_mode': 'supplier', 'default_is_company': True}</field>
<field name="filter" eval="True"/>
<field
name="context"
>{'res_partner_search_mode': 'supplier', 'default_is_company': True}</field>
<field name="filter" eval="True" />
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new vendor in your address book
</p><p>
</p>
<p>
Odoo helps you easily track all activities related to a vendor.
</p>
</field>
</record>
<record id="account_invoice_import_form" model="ir.ui.view">
<field name="model">account.invoice.import</field>
<field name="inherit_id" ref="account_invoice_import.account_invoice_import_form"/>
<field
name="inherit_id"
ref="account_invoice_import.account_invoice_import_form"
/>
<field name="arch" type="xml">
<xpath expr="//div[@name='help-import']" position="before">
<div name="help-import-scan" states="import">
<p><big><big><i class="fa fa-qrcode"/></big></big> Scan a QR-bill code from a supplier invoice</p>
<p><big>
<big>
<i class="fa fa-qrcode" />
</big>
</big> Scan a QR-bill code from a supplier invoice</p>
</div>
<group name="main">
<field
name="invoice_scan"
states="import,update-from-invoice"
/>
<field name="invoice_scan" states="import,update-from-invoice" />
</group>
<div name="help-or" states="import">
<p><b>OR</b></p>
<p>
<b>OR</b>
</p>
</div>
<div name="help-import-partner-select" states="select-partner">
<p>Partner was not found or is not set as a supplier. Please create or select the partner before continuing</p>
<p
>Partner was not found or is not set as a supplier. Please create or select the partner before continuing</p>
</div>
</xpath>
<field name="partner_id" position="attributes">
<attribute name="context">{'default_name': partner_name, 'default_street': partner_street, 'default_zip': partner_zip, 'default_city': partner_city, 'default_country_id': partner_country_id}</attribute>
<attribute name="states">config,update,update-from-invoice,select-partner</attribute>
<attribute name="attrs">{'readonly': [('state', '!=', 'select-partner')], 'required': [('state', '=', 'select-partner')]}</attribute>
<attribute
name="context"
>{'default_name': partner_name, 'default_street': partner_street, 'default_zip': partner_zip, 'default_city': partner_city, 'default_country_id': partner_country_id}</attribute>
<attribute
name="states"
>config,update,update-from-invoice,select-partner</attribute>
<attribute
name="attrs"
>{'readonly': [('state', '!=', 'select-partner')], 'required': [('state', '=', 'select-partner')]}</attribute>
</field>
<group name="main">
<field name="partner_name" states="select-partner"/>
<field name="partner_street" states="select-partner"/>
<field name="partner_zip" states="select-partner"/>
<field name="partner_city" states="select-partner"/>
<field name="partner_country_id" states="select-partner"/>
<field name="partner_name" states="select-partner" />
<field name="partner_street" states="select-partner" />
<field name="partner_zip" states="select-partner" />
<field name="partner_city" states="select-partner" />
<field name="partner_country_id" states="select-partner" />
</group>
<field name="import_config_id" position="attributes">
<attribute name="states">config,update-from-invoice,update,select-partner</attribute>
<attribute name="attrs">{'required': [('state', 'in', ('config', 'update-from-invoice', 'update', 'select-partner'))]}</attribute>
<attribute
name="states"
>config,update-from-invoice,update,select-partner</attribute>
<attribute
name="attrs"
>{'required': [('state', 'in', ('config', 'update-from-invoice', 'update', 'select-partner'))]}</attribute>
<attribute name="context">{'default_partner_id': partner_id}</attribute>
</field>
<button name="import_invoice" position="after">
Expand Down

0 comments on commit c255782

Please sign in to comment.