Coverage for adhoc-cicd-odoo-odoo / odoo / tools / barcode.py: 19%

48 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-09 18:15 +0000

1# Part of Odoo. See LICENSE file for full copyright and licensing details. 

2import functools 

3import re 

4from threading import RLock 

5 

6__all__ = ['check_barcode_encoding', 'createBarcodeDrawing', 'get_barcode_font'] 

7_barcode_init_lock = RLock() 

8 

9 

10# A lock occurs when the user wants to print a report having multiple barcode while the server is 

11# started in threaded-mode. The reason is that reportlab has to build a cache of the T1 fonts 

12# before rendering a barcode (done in a C extension) and this part is not thread safe. 

13# This cached functions allows to lazily initialize the T1 fonts cache need for rendering of 

14# barcodes in a thread-safe way. 

15@functools.lru_cache(1) 

16def _init_barcode(): 

17 with _barcode_init_lock: 

18 try: 

19 from reportlab.graphics import barcode # noqa: PLC0415 

20 from reportlab.pdfbase.pdfmetrics import TypeFace, getFont # noqa: PLC0415 

21 font_name = 'Courier' 

22 available = TypeFace(font_name).findT1File() 

23 if not available: 

24 substitution_font = 'NimbusMonoPS-Regular' 

25 fnt = getFont(substitution_font) 

26 if fnt: 

27 font_name = substitution_font 

28 fnt.ascent = 629 

29 fnt.descent = -157 

30 barcode.createBarcodeDrawing('Code128', value='foo', format='png', width=100, height=100, humanReadable=1, fontName=font_name).asString('png') 

31 except ImportError: 

32 raise 

33 except Exception: # noqa: BLE001 

34 font_name = 'Courier' 

35 return barcode, font_name 

36 

37 

38def createBarcodeDrawing(codeName: str, **options): 

39 barcode, _font = _init_barcode() 

40 return barcode.createBarcodeDrawing(codeName, **options) 

41 

42 

43def get_barcode_font(): 

44 """Get the barcode font for rendering.""" 

45 _barcode, font = _init_barcode() 

46 return font 

47 

48 

49def get_barcode_check_digit(numeric_barcode: str) -> int: 

50 """ Computes and returns the barcode check digit. The used algorithm 

51 follows the GTIN specifications and can be used by all compatible 

52 barcode nomenclature, like as EAN-8, EAN-12 (UPC-A) or EAN-13. 

53 https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf 

54 https://www.gs1.org/services/how-calculate-check-digit-manually 

55 :param numeric_barcode: the barcode to verify/recompute the check digit 

56 :return: the number corresponding to the right check digit 

57 """ 

58 # Multiply value of each position by 

59 # N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 N13 N14 N15 N16 N17 N18 

60 # x3 X1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 CHECKSUM 

61 oddsum = evensum = 0 

62 code = numeric_barcode[-2::-1] # Remove the check digit and reverse the barcode. 

63 # The CHECKSUM digit is removed because it will be recomputed and it must not interfer with 

64 # the computation. Also, the barcode is inverted, so the barcode length doesn't matter. 

65 # Otherwise, the digits' group (even or odd) could be different according to the barcode length. 

66 for i, digit in enumerate(code): 

67 if i % 2 == 0: 

68 evensum += int(digit) 

69 else: 

70 oddsum += int(digit) 

71 total = evensum * 3 + oddsum 

72 return (10 - total % 10) % 10 

73 

74 

75def check_barcode_encoding(barcode: str, encoding: str) -> bool: 

76 """ Checks if the given barcode is correctly encoded. 

77 :return: True if the barcode string is encoded with the provided encoding. 

78 """ 

79 encoding = encoding.lower() 

80 if encoding == "any": 

81 return True 

82 barcode_sizes = { 

83 'ean8': 8, 

84 'ean13': 13, 

85 'gtin14': 14, 

86 'upca': 12, 

87 'sscc': 18, 

88 } 

89 barcode_size = barcode_sizes[encoding] 

90 return (encoding != 'ean13' or barcode[0] != '0') \ 

91 and len(barcode) == barcode_size \ 

92 and re.match(r"^\d+$", barcode) \ 

93 and get_barcode_check_digit(barcode) == int(barcode[-1])