Coverage Summary for Class: IbanRuGenerator (ru.eda.plgn.bizgen.core.generator.impl)

Class Method, % Branch, % Line, % Instruction, %
IbanRuGenerator 100% (2/2) 100% (4/4) 100% (20/20)
IbanRuGenerator$IbanGenerator 100% (4/4) 62.5% (5/8) 100% (32/32) 98.3% (228/232)
Total 100% (6/6) 62.5% (5/8) 100% (36/36) 98.4% (248/252)


 package ru.eda.plgn.bizgen.core.generator.impl
 
 import ru.eda.plgn.bizgen.core.generator.GeneratorResult
 import ru.eda.plgn.bizgen.core.generator.GeneratorResultWithEscape
 import ru.eda.plgn.bizgen.core.generator.GeneratorStr
 import ru.eda.plgn.bizgen.core.generator.impl.IbanRuGenerator.IbanGenerator.generateRussianIBAN
 import java.math.BigInteger
 
 /**
  * IBAN (International Bank Account Number) - международный номер банковского счёта, используемый для международных переводов. Формат
  * регулируется стандартом ISO 13616.
  *
  * **Формат российского IBAN (33 символа):** *RU КК XXXX XXXX XXXX XXXX XXXX XXXX X*
  * - *2 буквы — код страны (RU)*
  * - *2 цифры — контрольное число (рассчитывается)*
  * - *29 цифр — BBAN (базовый номер счёта)*
  *
  * **Структура BBAN (29 цифр):** *KK BBBBB SSSS C NNNNNNNNNNNNNNNNNNNN*
  * - *KK — контрольные цифры (2 цифры)*
  * - *BBBBB — БИК банка (5 цифр)*
  * - *SSSS — код филиала (4 цифры, часто 0000)*
  * - *C — признак счёта (1 цифра, обычно 0)*
  * - *NN...N — номер счёта (20 цифр)*
  *
  * **See Also:** [IBAN](https://ru.wikipedia.org/wiki/IBAN)
  *
  * @author Dmitry_Emelyanenko
  */
 class IbanRuGenerator : GeneratorStr {
   override val uniqueDistance: Int = 130
 
   override fun generate(): GeneratorResult<String> = GeneratorResultWithEscape(
     data = generateRussianIBAN(accountNumber = BankAccountGenerator.randomCorrespondentAccount(bik = BikGenerator.randomBik()))
   )
 
   /**
    * IBAN (International Bank Account Number) - международный номер банковского счёта, используемый для международных переводов. Формат
    * регулируется стандартом ISO 13616.
    *
    * **See Also:** [IBAN](https://ru.wikipedia.org/wiki/IBAN)
    *
    * @author Dmitry_Emelyanenko
    */
   private object IbanGenerator {
     // База данных российских банков (БИК или аналогичные коды)
     private val bankCodes = mapOf(
       "Газпромбанк" to "044525823",
       "Сбербанк" to "044525225",
       "ВТБ" to "044525187",
       "Альфа-Банк" to "044525593",
       "Тинькофф" to "044525444",
       "Райффайзенбанк" to "044525202",
       "Открытие" to "044525111",
       "Промсвязьбанк" to "044525060",
       "Россельхозбанк" to "044525402",
       "Совкомбанк" to "044525417"
     )
 
     /**
      * Генерирует российский IBAN.
      *
      * @param accountNumber Номер счёта (если не указан, генерируется случайный).
      * @param bankCode Код банка (если не указан, выбирается случайный из базы).
      * @return Строка IBAN (формат RUXXXXXXXXXXXXXXXXXXXXXXXXX).
      */
     fun generateRussianIBAN(
       accountNumber: String,
       bankCode: String? = null,
     ): String {
       // 1. Выбираем код банка (5 цифр)
       val selectedBankCode = (bankCode ?: bankCodes.values.random()).take(5)
 
       // 2. Формируем BBAN (29 цифр)
       val bban = buildString {
         append("00")                    // Временные контрольные цифры
         append(selectedBankCode)        // Код банка (5 цифр)
         append("0000")                  // Код филиала
         append("0")                     // Признак счета
         append(accountNumber)           // Номер счета (20 цифр)
       }.take(29)
 
       // 3. Вычисляем контрольное число для IBAN
       val controlNumber = calculateIBANControlNumber("RU", bban)
 
       // 4. Собираем итоговый IBAN (33 символа)
       return "RU$controlNumber$bban".also {
         require(it.length == 33) { "IBAN must be 33 but was ${it.length}" }
       }
     }
 
     /** Вычисляет контрольное число IBAN (алгоритм mod-97). */
     @Suppress("SameParameterValue")
     private fun calculateIBANControlNumber(countryCode: String, bban: String): String {
       val tempIban = "$countryCode${"00"}$bban" // RU + 00 + BBAN
       val moved = tempIban.substring(4) + tempIban.substring(0, 4) // Перемещаем первые 4 символа в конец
       val numericIban = moved.map { char ->
         if (char.isLetter()) (char.uppercaseChar() - 'A' + 10).toString()
         else char.toString()
       }.joinToString("")
       val mod97 = BigInteger(numericIban).mod(BigInteger("97")).toInt()
       return "%02d".format(98 - mod97)
     }
   }
 }