Lines
100.00%
67 / 67
Functions and Methods
100.00%
3 / 3
Classes and Traits
100.00%
1 / 1
| Name | Lines | Functions and Methods | CRAP | Classes and Traits | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| TatraBankaMailParser | 100.00% | 67 / 67 | 100.00% | 3 / 3 | 21 | 100.00% | 1 / 1 | |||
| bankDomains | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | |||||
| supports | 100.00% | 8 / 8 | 100.00% | 1 / 1 | 6 | |||||
| parse | 100.00% | 58 / 58 | 100.00% | 1 / 1 | 14 | |||||
| 1 | <?php | |
| 2 | ||
| 3 | declare(strict_types=1); | |
| 4 | ||
| 5 | namespace Tomaj\BankMailsParser\Parser\TatraBanka; | |
| 6 | ||
| 7 | use Tomaj\BankMailsParser\Exception\InvalidEmailFormatException; | |
| 8 | use Tomaj\BankMailsParser\MailContent; | |
| 9 | use Tomaj\BankMailsParser\Parser\BankDate; | |
| 10 | use Tomaj\BankMailsParser\Parser\MailWhitespace; | |
| 11 | use Tomaj\BankMailsParser\Parser\ParserInterface; | |
| 12 | use Tomaj\BankMailsParser\Parser\ParsesBankMail; | |
| 13 | ||
| 14 | final readonly class TatraBankaMailParser implements ParserInterface | |
| 15 | { | |
| 16 | use ParsesBankMail; | |
| 17 | ||
| 18 | private const string TRANSACTION = '/^' . MailWhitespace::WS . '(\d{1,2}\.[ \t\x{00A0}\x{2009}\x{202F}]*\d{1,2}\.[ \t\x{00A0}\x{2009}\x{202F}]*\d{2,4}(?:[ \t\x{00A0}\x{2009}\x{202F}]+\d{1,2}:\d{2}(?::\d{2})?)?|\d{4}-\d{2}-\d{2}(?:[ \t\x{00A0}\x{2009}\x{202F}]+\d{1,2}:\d{2}(?::\d{2})?)?|\d{1,2}[ \t\x{00A0}\x{2009}\x{202F}]+[A-Za-z]+[ \t\x{00A0}\x{2009}\x{202F}]+\d{4}(?:[ \t\x{00A0}\x{2009}\x{202F}]+\d{1,2}:\d{2}(?::\d{2})?)?)[ \t\x{00A0}\x{2009}\x{202F}]+bol zostatok Vasho uctu ([A-Za-z0-9]+) (zvyseny|znizeny) o ([0-9 \x{00A0}\x{2009}\x{202F}\t]+,[0-9]+) ([A-Za-z]+)/mu'; | |
| 19 | ||
| 20 | /** | |
| 21 | * @return list<string> | |
| 22 | */ | |
| 23 | #[\Override] | |
| 24 | public function bankDomains(): array | |
| 25 | { | |
| 26 | return ['tatrabanka.sk']; | |
| 27 | } | |
| 28 | ||
| 29 | #[\Override] | |
| 30 | public function supports(string $content, ?string $sender = null, ?string $subject = null): bool | |
| 31 | { | |
| 32 | return $this->supportsQuietly(function () use ($content, $sender, $subject): bool { | |
| 33 | $content = $this->withoutFreeText($this->scrubUtf8($this->withoutByteOrderMark($content))); | |
| 34 | if ($this->matched(self::TRANSACTION, $content)) { | |
| 35 | return true; | |
| 36 | } | |
| 37 | ||
| 38 | $senderMatches = $sender !== null && $this->senderDomain($sender, 'tatrabanka.sk'); | |
| 39 | $subjectMatches = $subject !== null && str_contains(strtolower($subject), 'tatra'); | |
| 40 | ||
| 41 | return ($senderMatches || $subjectMatches) && str_contains($content, 'zostatok Vasho uctu'); | |
| 42 | }); | |
| 43 | } | |
| 44 | ||
| 45 | public function parse(string $content): MailContent | |
| 46 | { | |
| 47 | $original = $this->scrubUtf8($this->requireUniformLineBreaks($content)); | |
| 48 | $description = $this->lineValue($original, 'Popis transakcie'); | |
| 49 | $receiverMessage = $this->lineValue($original, 'Informacia pre prijemcu'); | |
| 50 | $sourceAccountNumber = null; | |
| 51 | if ($description !== null) { | |
| 52 | $descriptionParts = explode(' ', $description, 2); | |
| 53 | $sourceAccountNumber = count($descriptionParts) === 2 ? $descriptionParts[1] : $descriptionParts[0]; | |
| 54 | } | |
| 55 | ||
| 56 | $content = $this->withoutFreeText($original); | |
| 57 | if (!$this->matched(self::TRANSACTION, $content, $matches)) { | |
| 58 | throw InvalidEmailFormatException::malformedContent( | |
| 59 | self::class, | |
| 60 | 'Could not find required transaction pattern', | |
| 61 | ); | |
| 62 | } | |
| 63 | ||
| 64 | $rawDate = trim($this->group($matches, 1)); | |
| 65 | $transactionDate = BankDate::parseFirst($rawDate, [ | |
| 66 | '!j. n. Y G:i:s', | |
| 67 | '!j.n.Y G:i:s', | |
| 68 | '!j. n. Y G:i', | |
| 69 | '!j.n.Y G:i', | |
| 70 | '!Y-m-d G:i:s', | |
| 71 | '!Y-m-d H:i:s', | |
| 72 | '!Y-m-d G:i', | |
| 73 | '!Y-m-d H:i', | |
| 74 | '!j. n. Y', | |
| 75 | '!j.n.Y', | |
| 76 | '!Y-m-d', | |
| 77 | ], BankDate::bratislava()); | |
| 78 | if ($transactionDate === null && preg_match('/[A-Za-z]/', $rawDate) === 1) { | |
| 79 | $transactionDate = BankDate::parseEnglish($rawDate, BankDate::bratislava()); | |
| 80 | } | |
| 81 | ||
| 82 | $accountNumber = $this->group($matches, 2); | |
| 83 | $amount = $this->group($matches, 4); | |
| 84 | if ($this->group($matches, 3) === 'znizeny') { | |
| 85 | $amount = '-' . $amount; | |
| 86 | } | |
| 87 | ||
| 88 | $money = $this->money($amount, $this->group($matches, 5), ','); | |
| 89 | ||
| 90 | $vs = null; | |
| 91 | $ss = null; | |
| 92 | $ks = null; | |
| 93 | if ($this->matched($this->field('Referencia platitela:', '\/VS(\d*)\/SS(\d*)\/KS(\d*)'), $original, $refMatches)) { | |
| 94 | $vs = $this->paymentSymbol($this->group($refMatches, 1), null); | |
| 95 | $ss = $this->paymentSymbol($this->group($refMatches, 2), null); | |
| 96 | $ks = $this->paymentSymbol($this->group($refMatches, 3), null); | |
| 97 | } | |
| 98 | ||
| 99 | // 4.1.0 fallbacks. They read payer text and never the amount, account, or date. | |
| 100 | if ($vs === null && $this->matched('/vs([0-9]{1,10})(?!\d)/i', $original, $prefixed)) { | |
| 101 | $vs = $this->group($prefixed, 1); | |
| 102 | } | |
| 103 | if ($vs === null && $this->matched('/Informacia pre prijemcu:.*\b([0-9]{1,10})\b.*/i', $original, $fromMessage)) { | |
| 104 | $vs = $this->group($fromMessage, 1); | |
| 105 | } | |
| 106 | if ($vs === null && $this->matched('/Referencia platitela:.*\b([0-9]{1,10})\b.*/i', $original, $fromReference)) { | |
| 107 | $vs = $this->group($fromReference, 1); | |
| 108 | } | |
| 109 | ||
| 110 | return new MailContent( | |
| 111 | money: $money, | |
| 112 | transactionDate: $transactionDate, | |
| 113 | accountNumber: $accountNumber, | |
| 114 | sourceAccountNumber: $sourceAccountNumber, | |
| 115 | vs: $vs, | |
| 116 | ss: $ss, | |
| 117 | ks: $ks, | |
| 118 | receiverMessage: $receiverMessage, | |
| 119 | description: $description, | |
| 120 | ); | |
| 121 | } | |
| 122 | } |