Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.65% covered (success)
95.65%
66 / 69
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
CsobMailParser
95.65% covered (success)
95.65%
66 / 69
50.00% covered (danger)
50.00%
1 / 2
22
0.00% covered (danger)
0.00%
0 / 1
 parseMulti
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 parse
95.16% covered (success)
95.16%
59 / 62
0.00% covered (danger)
0.00%
0 / 1
19
1<?php
2declare(strict_types=1);
3
4namespace Tomaj\BankMailsParser\Parser\Csob;
5
6use Tomaj\BankMailsParser\MailContent;
7use Tomaj\BankMailsParser\Parser\ParserInterface;
8
9class CsobMailParser implements ParserInterface
10{
11    /**
12     * @return MailContent[]
13     */
14    public function parseMulti(string $content): array
15    {
16        $transactions = array_slice(explode("Dne ", $content), 1);
17
18        $mailContents = [];
19        foreach ($transactions as $transaction) {
20            $mailContent = $this->parse($transaction);
21            if ($mailContent !== null) {
22                $mailContents[] = $mailContent;
23            }
24        }
25
26        return $mailContents;
27    }
28
29    public function parse(string $content): ?MailContent
30    {
31        $mailContent = new MailContent();
32
33        $pattern1 = '/(.*) byl(?:a)? na účtu ([a-zA-Z0-9]+) (?:zaúčtovaná|zaúčtována|zaúčtovaný) (?:zahraniční transakce\.|hotovostní transakce\.|(?:transakce typu: |transakce )?(Došlá platba|Příchozí úhrada|Došlá úhrada|SEPA převod|platební kartou))/mu';
34        $res = preg_match($pattern1, $content, $result);
35        if (!$res) {
36            return null;
37        }
38
39        $mailContent->setTransactionDate(strtotime($result[1]));
40        $mailContent->setAccountNumber($result[2]);
41
42        $pattern3 = '/Účet protistrany(?:\/IBAN)*: (.*)/mu';
43        $res = preg_match($pattern3, $content, $result);
44        if ($res) {
45            $mailContent->setSourceAccountNumber(trim($result[1]));
46        }
47
48        $pattern2 = '/Částka: ([+-])(.*?) ([A-Z]+)/mu';
49        $res = preg_match($pattern2, $content, $result);
50        if ($res) {
51            // there's unicode non-breaking space (u00A0) in mime encoded version of email, unicode regex switched is necessary
52            $amount = floatval(str_replace(',', '.', preg_replace('/\s+/u', '', $result[2])));
53            $currency = $result[3];
54            if ($result[1] === '-') {
55                $amount = -$amount;
56            }
57            $mailContent->setAmount($amount);
58            $mailContent->setCurrency($currency);
59        }
60
61        $pattern3 = '/Zpráva příjemci: (.*)/mu';
62        $res = preg_match($pattern3, $content, $result);
63        if ($res) {
64            $mailContent->setReceiverMessage(trim($result[1]));
65        }
66
67        $pattern4 = '/Variabilní symbol: ([0-9]{1,10})/m';
68        $res = preg_match($pattern4, $content, $result);
69        if ($res) {
70            // check if variable symbol field is not filled with zeros (0000000000)
71            // (this can happen for foreign transfers which use receiver message for VS)
72            if ((int) $result[1] !== 0) {
73                $mailContent->setVs($result[1]);
74            }
75        }
76        $pattern4 = '/Identifikace: ([0-9]{1,10})/m';
77        $res = preg_match($pattern4, $content, $result);
78        if ($res) {
79            $mailContent->setVs($result[1]);
80        }
81
82        // search whole email for number with `vs` / `VS` prefix
83        if ($mailContent->getVs() === null) {
84            $pattern = '/vs([0-9]{1,10})/i';
85            $res = preg_match($pattern, $content, $result);
86            if ($res) {
87                $mailContent->setVs($result[1]);
88            }
89        }
90
91        // search whole email for number with `v.s.` / `V.S.` prefix
92        if ($mailContent->getVs() === null) {
93            $pattern = '/v\.s\.([0-9]{1,10})/i';
94            $res = preg_match($pattern, $content, $result);
95            if ($res) {
96                $mailContent->setVs($result[1]);
97            }
98        }
99
100        // if still no number (VS) found, check receiver message
101        // - some payers incorrectly set this field with VS number but without "VS" prefix
102        // - some banks send here variable symbol in Creditor Reference Information - SEPA XML format
103        // loads VS provided in formats:
104        // - Informacia pre prijemcu: 1234056789
105        // - Informacia pre prijemcu: (CdtrRefInf)(Tp)(CdOrPrtry)(Cd)SCOR(/Cd)(/CdOrPrtry)(/Tp)(Ref)1234056789(/Ref)(/CdtrRefInf)
106        if ($mailContent->getVs() === null) {
107            $pattern = '/Zpráva příjemci:.*\b([0-9]{1,10})\b.*/i';
108            $res = preg_match($pattern, $content, $result);
109            if ($res) {
110                $mailContent->setVs($result[1]);
111            }
112        }
113
114        // if still no number (VS) found, check payment purpose (some foreign payments use this field for variable symbol)
115        if ($mailContent->getVs() === null) {
116            $pattern4 = '/Účel platby: ([0-9]{1,10})/m';
117            $res = preg_match($pattern4, $content, $result);
118            if ($res) {
119                $mailContent->setVs($result[1]);
120            } else {
121                // Transaction description/purpose (Účel platby) is sometimes multiline element.
122                // For now it is always last entry before balance element. So check all lines until "Zůstatek".
123                // Use modifier `s` - single-line (dot matches newline).
124                $pattern4 = '/Účel platby:.*\b([0-9]{1,10})\b.*Zůstatek/s';
125                $res = preg_match($pattern4, $content, $result);
126                if ($res) {
127                    $mailContent->setVs($result[1]);
128                }
129            }
130        }
131
132        $pattern5 = '/Konstantní symbol: ([0-9]{1,10})/mu';
133        $res = preg_match($pattern5, $content, $result);
134        if ($res) {
135            $mailContent->setKs($result[1]);
136        }
137
138        return $mailContent;
139    }
140}