__init__.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import re
  2. import string
  3. from math import ceil
  4. from string import ascii_uppercase
  5. from typing import Dict, Optional
  6. from .. import BaseProvider
  7. localized = True
  8. default_locale = "en_GB"
  9. class Provider(BaseProvider):
  10. """Implement default bank provider for Faker.
  11. .. important::
  12. Bank codes, account numbers, and other ID's generated by this provider
  13. are only valid in form, i.e. they conform to some standard/format, are
  14. of the expected lengths, and have valid checksums (where applicable).
  15. Results generated that turn out to be valid in real life are purely
  16. coincidental.
  17. Sources:
  18. - https://en.wikipedia.org/wiki/International_Bank_Account_Number
  19. - https://www.theswiftcodes.com/swift-code-checker/
  20. """
  21. ALPHA: Dict[str, str] = {c: str(ord(c) % 55) for c in string.ascii_uppercase}
  22. bban_format: str = "????#############"
  23. country_code: str = "GB"
  24. def aba(self) -> str:
  25. """Generate an ABA routing transit number."""
  26. fed_num = self.random_int(min=1, max=12)
  27. rand = self.numerify("######")
  28. aba = f"{fed_num:02}{rand}"
  29. # calculate check digit
  30. d = [int(n) for n in aba]
  31. chk_digit = 3 * (d[0] + d[3] + d[6]) + 7 * (d[1] + d[4] + d[7]) + d[2] + d[5]
  32. chk_digit = ceil(chk_digit / 10) * 10 - chk_digit
  33. return f"{aba}{chk_digit}"
  34. def bank_country(self) -> str:
  35. """Generate the bank provider's ISO 3166-1 alpha-2 country code."""
  36. return self.country_code
  37. def bban(self) -> str:
  38. """Generate a Basic Bank Account Number (BBAN)."""
  39. temp = re.sub(r"\?", lambda x: self.random_element(ascii_uppercase), self.bban_format)
  40. return self.numerify(temp)
  41. def iban(self) -> str:
  42. """Generate an International Bank Account Number (IBAN)."""
  43. bban = self.bban()
  44. check = bban + self.country_code + "00"
  45. check_ = int("".join(self.ALPHA.get(c, c) for c in check))
  46. check_ = 98 - (check_ % 97)
  47. check = str(check_).zfill(2)
  48. return self.country_code + check + bban
  49. def swift8(self, use_dataset: bool = False) -> str:
  50. """Generate an 8-digit SWIFT code.
  51. This method uses |swift| under the hood with the ``length`` argument set
  52. to ``8`` and with the ``primary`` argument omitted. All 8-digit SWIFT
  53. codes already refer to the primary branch/office.
  54. :sample:
  55. :sample: use_dataset=True
  56. """
  57. return self.swift(length=8, use_dataset=use_dataset)
  58. def swift11(self, primary: bool = False, use_dataset: bool = False) -> str:
  59. """Generate an 11-digit SWIFT code.
  60. This method uses |swift| under the hood with the ``length`` argument set
  61. to ``11``. If ``primary`` is set to ``True``, the SWIFT code will always
  62. end with ``'XXX'``. All 11-digit SWIFT codes use this convention to
  63. refer to the primary branch/office.
  64. :sample:
  65. :sample: use_dataset=True
  66. """
  67. return self.swift(length=11, primary=primary, use_dataset=use_dataset)
  68. def swift(
  69. self,
  70. length: Optional[int] = None,
  71. primary: bool = False,
  72. use_dataset: bool = False,
  73. ) -> str:
  74. """Generate a SWIFT code.
  75. SWIFT codes, reading from left to right, are composed of a 4 alphabet
  76. character bank code, a 2 alphabet character country code, a 2
  77. alphanumeric location code, and an optional 3 alphanumeric branch code.
  78. This means SWIFT codes can only have 8 or 11 characters, so the value of
  79. ``length`` can only be ``None`` or the integers ``8`` or ``11``. If the
  80. value is ``None``, then a value of ``8`` or ``11`` will randomly be
  81. assigned.
  82. Because all 8-digit SWIFT codes already refer to the primary branch or
  83. office, the ``primary`` argument only has an effect if the value of
  84. ``length`` is ``11``. If ``primary`` is ``True`` and ``length`` is
  85. ``11``, the 11-digit SWIFT codes generated will always end in ``'XXX'``
  86. to denote that they belong to primary branches/offices.
  87. For extra authenticity, localized providers may opt to include SWIFT
  88. bank codes, location codes, and branch codes used in their respective
  89. locales. If ``use_dataset`` is ``True``, this method will generate SWIFT
  90. codes based on those locale-specific codes if included. If those codes
  91. were not included, then it will behave as if ``use_dataset`` were
  92. ``False``, and in that mode, all those codes will just be randomly
  93. generated as per the specification.
  94. :sample:
  95. :sample: length=8
  96. :sample: length=8, use_dataset=True
  97. :sample: length=11
  98. :sample: length=11, primary=True
  99. :sample: length=11, use_dataset=True
  100. :sample: length=11, primary=True, use_dataset=True
  101. """
  102. if length is None:
  103. length = self.random_element((8, 11))
  104. if length not in (8, 11):
  105. raise AssertionError("length can only be 8 or 11")
  106. if use_dataset and hasattr(self, "swift_bank_codes"):
  107. bank_code: str = self.random_element(self.swift_bank_codes) # type: ignore[attr-defined]
  108. else:
  109. bank_code = self.lexify("????", letters=string.ascii_uppercase)
  110. if use_dataset and hasattr(self, "swift_location_codes"):
  111. location_code: str = self.random_element(self.swift_location_codes) # type: ignore[attr-defined]
  112. else:
  113. location_code = self.lexify("??", letters=string.ascii_uppercase + string.digits)
  114. if length == 8:
  115. return bank_code + self.country_code + location_code
  116. if primary:
  117. branch_code = "XXX"
  118. elif use_dataset and hasattr(self, "swift_branch_codes"):
  119. branch_code = self.random_element(self.swift_branch_codes) # type: ignore[attr-defined]
  120. else:
  121. branch_code = self.lexify("???", letters=string.ascii_uppercase + string.digits)
  122. return bank_code + self.country_code + location_code + branch_code