[FIX] web, l10n_ch: Error Correction Level QR Bill
l10n_ch QR bill report needs level 'M' (15% red.) instead of default level 'L' (7% red.) as per: https://en.wikipedia.org/wiki/QR_code#Error_correction See specifications at: https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-en.pdf The layout of higher level of Error Correction are made of a thicker grid with more squares (more information) at the cost of them being smaller (a little harder to be read by the scanner). opw-2584899 closes odoo/odoo#82932 X-original-commit: 0f374b61a6f3b25a1f986afe455c0e42797a5769 Signed-off-by: William André (wan) <wan@odoo.com> Signed-off-by: Paolo Gatti (pgi) <pgi@odoo.com>
This commit is contained in:
@@ -307,6 +307,8 @@ class ResPartnerBank(models.Model):
|
||||
'quiet': 1,
|
||||
'mask': 'ch_cross',
|
||||
'value': '\n'.join(self._get_qr_vals(qr_method, amount, currency, debtor_partner, free_communication, structured_communication)),
|
||||
# Swiss QR code requires Error Correction Level = 'M' by specification
|
||||
'barLevel': 'M',
|
||||
}
|
||||
return super()._get_qr_code_generation_params(qr_method, amount, currency, debtor_partner, free_communication, structured_communication)
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ class TestSwissQR(AccountTestInvoicingCommon):
|
||||
|
||||
expected_params = {
|
||||
'barcode_type': 'QR',
|
||||
'barLevel': 'M',
|
||||
'width': 256,
|
||||
'height': 256,
|
||||
'quiet': 1,
|
||||
|
||||
@@ -1975,17 +1975,20 @@ class ReportController(http.Controller):
|
||||
#------------------------------------------------------
|
||||
# Misc. route utils
|
||||
#------------------------------------------------------
|
||||
@http.route(['/report/barcode', '/report/barcode/<type>/<path:value>'], type='http', auth="public")
|
||||
def report_barcode(self, type, value, width=600, height=100, humanreadable=0, quiet=1, mask=None):
|
||||
@http.route(['/report/barcode', '/report/barcode/<barcode_type>/<path:value>'], type='http', auth="public")
|
||||
def report_barcode(self, barcode_type, value, **kwargs):
|
||||
"""Contoller able to render barcode images thanks to reportlab.
|
||||
Samples:
|
||||
Samples::
|
||||
|
||||
<img t-att-src="'/report/barcode/QR/%s' % o.name"/>
|
||||
<img t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' %
|
||||
<img t-att-src="'/report/barcode/?barcode_type=%s&value=%s&width=%s&height=%s' %
|
||||
('QR', o.name, 200, 200)"/>
|
||||
|
||||
:param type: Accepted types: 'Codabar', 'Code11', 'Code128', 'EAN13', 'EAN8', 'Extended39',
|
||||
'Extended93', 'FIM', 'I2of5', 'MSI', 'POSTNET', 'QR', 'Standard39', 'Standard93',
|
||||
'UPCA', 'USPS_4State'
|
||||
:param barcode_type: Accepted types: 'Codabar', 'Code11', 'Code128', 'EAN13', 'EAN8',
|
||||
'Extended39', 'Extended93', 'FIM', 'I2of5', 'MSI', 'POSTNET', 'QR', 'Standard39',
|
||||
'Standard93', 'UPCA', 'USPS_4State'
|
||||
:param width: Pixel width of the barcode
|
||||
:param height: Pixel height of the barcode
|
||||
:param humanreadable: Accepted values: 0 (default) or 1. 1 will insert the readable value
|
||||
at the bottom of the output image
|
||||
:param quiet: Accepted values: 0 (default) or 1. 1 will display white
|
||||
@@ -1993,10 +1996,11 @@ class ReportController(http.Controller):
|
||||
:param mask: The mask code to be used when rendering this QR-code.
|
||||
Masks allow adding elements on top of the generated image,
|
||||
such as the Swiss cross in the center of QR-bill codes.
|
||||
:param barLevel: QR code Error Correction Levels. Default is 'L'.
|
||||
ref: https://hg.reportlab.com/hg-public/reportlab/file/830157489e00/src/reportlab/graphics/barcode/qr.py#l101
|
||||
"""
|
||||
try:
|
||||
barcode = request.env['ir.actions.report'].barcode(type, value, width=width,
|
||||
height=height, humanreadable=humanreadable, quiet=quiet, mask=mask)
|
||||
barcode = request.env['ir.actions.report'].barcode(barcode_type, value, **kwargs)
|
||||
except (ValueError, AttributeError):
|
||||
raise werkzeug.exceptions.HTTPException(description='Cannot convert into barcode.')
|
||||
|
||||
|
||||
@@ -533,7 +533,25 @@ class IrActionsReport(models.Model):
|
||||
return report_obj.with_context(context).sudo().search(conditions, limit=1)
|
||||
|
||||
@api.model
|
||||
def barcode(self, barcode_type, value, width=600, height=100, humanreadable=0, quiet=1, mask=None):
|
||||
def barcode(self, barcode_type, value, **kwargs):
|
||||
defaults = {
|
||||
'width': (600, int),
|
||||
'height': (100, int),
|
||||
'humanreadable': (False, lambda x: bool(int(x))),
|
||||
'quiet': (True, lambda x: bool(int(x))),
|
||||
'mask': (None, lambda x: x),
|
||||
'barBorder': (4, int),
|
||||
# The QR code can have different layouts depending on the Error Correction Level
|
||||
# See: https://en.wikipedia.org/wiki/QR_code#Error_correction
|
||||
# Level 'L' – up to 7% damage (default)
|
||||
# Level 'M' – up to 15% damage (i.e. required by l10n_ch QR bill)
|
||||
# Level 'Q' – up to 25% damage
|
||||
# Level 'H' – up to 30% damage
|
||||
'barLevel': ('L', lambda x: x in ('L', 'M', 'Q', 'H') and x or 'L'),
|
||||
}
|
||||
kwargs = {k: validator(kwargs.get(k, v)) for k, (v, validator) in defaults.items()}
|
||||
kwargs['humanReadable'] = kwargs.pop('humanreadable')
|
||||
|
||||
if barcode_type == 'UPCA' and len(value) in (11, 12, 13):
|
||||
barcode_type = 'EAN13'
|
||||
if len(value) in (11, 12):
|
||||
@@ -544,34 +562,31 @@ class IrActionsReport(models.Model):
|
||||
elif barcode_type == 'DataMatrix' and not self.datamatrix_available():
|
||||
# fallback to avoid stacktrack because reportlab won't recognize the type and error message isn't useful/will be blocking
|
||||
barcode_type = 'Code128'
|
||||
try:
|
||||
width, height, humanreadable, quiet = int(width), int(height), bool(int(humanreadable)), bool(int(quiet))
|
||||
elif barcode_type == 'QR':
|
||||
# for `QR` type, `quiet` is not supported. And is simply ignored.
|
||||
# But we can use `barBorder` to get a similar behaviour.
|
||||
bar_border = 4
|
||||
if barcode_type == 'QR' and quiet:
|
||||
bar_border = 0
|
||||
if kwargs['quiet']:
|
||||
kwargs['barBorder'] = 0
|
||||
|
||||
barcode = createBarcodeDrawing(
|
||||
barcode_type, value=value, format='png', width=width, height=height,
|
||||
humanReadable=humanreadable, quiet=quiet, barBorder=bar_border
|
||||
)
|
||||
try:
|
||||
barcode = createBarcodeDrawing(barcode_type, value=value, format='png', **kwargs)
|
||||
|
||||
# If a mask is asked and it is available, call its function to
|
||||
# post-process the generated QR-code image
|
||||
if mask:
|
||||
if kwargs['mask']:
|
||||
available_masks = self.get_available_barcode_masks()
|
||||
mask_to_apply = available_masks.get(mask)
|
||||
mask_to_apply = available_masks.get(kwargs['mask'])
|
||||
if mask_to_apply:
|
||||
mask_to_apply(width, height, barcode)
|
||||
mask_to_apply(kwargs['width'], kwargs['height'], barcode)
|
||||
|
||||
return barcode.asString('png')
|
||||
except (ValueError, AttributeError):
|
||||
if barcode_type == 'Code128':
|
||||
raise ValueError("Cannot convert into barcode.")
|
||||
elif barcode_type == 'QR':
|
||||
raise ValueError("Cannot convert into QR code.")
|
||||
else:
|
||||
return self.barcode('Code128', value, width=width, height=height,
|
||||
humanreadable=humanreadable, quiet=quiet)
|
||||
return self.barcode('Code128', value, **kwargs)
|
||||
|
||||
@api.model
|
||||
def get_available_barcode_masks(self):
|
||||
|
||||
Reference in New Issue
Block a user