Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.75% covered (success)
91.75%
89 / 97
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
MembersUtils
91.75% covered (success)
91.75%
89 / 97
77.78% covered (warning)
77.78%
7 / 9
23.30
0.00% covered (danger)
0.00%
0 / 1
 parseCsv
82.14% covered (warning)
82.14%
23 / 28
0.00% covered (danger)
0.00%
0 / 1
6.20
 convertMemberCsvEncoding
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getMemberIdent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMemberUsername
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMemberFirstName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMemberLastName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMemberCompany
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 update
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
7.23
 getUserData
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Olz\Apps\Members\Utils;
4
5use Olz\Entity\Members\Member;
6use Olz\Entity\Users\User;
7use Olz\Utils\WithUtilsTrait;
8
9class MembersUtils {
10    use WithUtilsTrait;
11
12    public int $first_data_row = 1;
13    public string $member_ident_key = '[Id]';
14    public string $member_username_key = 'Benutzer-Id';
15    public string $member_first_name_key = 'Vorname';
16    public string $member_last_name_key = 'Nachname';
17    public string $member_company_key = 'Firma';
18
19    /** @return array<array<string, string>> */
20    public function parseCsv(string $csv_content): array {
21        $utf8_csv_content = $this->convertMemberCsvEncoding($csv_content);
22        $stream = new \SplTempFileObject();
23        $stream->fwrite($utf8_csv_content);
24        $stream->rewind();
25        $stream->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
26        $stream->setCsvControl(";", "\"", "\\");
27        $header = $stream->current();
28        if (!is_array($header)) {
29            throw new \Exception("Invalid CSV header: {$header}");
30        }
31        $num_columns = count($header);
32        $stream->next();
33
34        $rows = [];
35        $i = $this->first_data_row;
36        while ($raw_row = $stream->current()) {
37            if (!is_array($raw_row)) {
38                throw new \Exception("Invalid CSV row[{$i}]: {$raw_row}");
39            }
40            $raw_row_count = count($raw_row);
41            if ($raw_row_count !== $num_columns) {
42                $enc_raw_row = json_encode($raw_row);
43                $this->log()->notice("Member CSV parse: Row[{$i}] = '{$enc_raw_row}' length ({$raw_row_count}) is not {$num_columns}");
44                continue;
45            }
46            $row = [];
47            for ($j = 0; $j < $num_columns; $j++) {
48                $row[$header[$j]] = "{$raw_row[$j]}";
49            }
50            $rows[] = $row;
51            $i++;
52            $stream->next();
53        }
54        return $rows;
55    }
56
57    protected function convertMemberCsvEncoding(string $csv_content): string {
58        if (strpos($csv_content, "Ge\x8andert") !== false) {
59            $converted = @iconv('macintosh', 'UTF-8', $csv_content);
60            if ($converted) {
61                return $converted;
62            }
63        }
64        $encoding = mb_detect_encoding($csv_content, ['UTF-8', 'ISO-8859-1', 'Windows-1252'], true);
65        $utf8_csv_content = mb_convert_encoding($csv_content, 'UTF-8', $encoding ?: 'ISO-8859-1');
66        $this->generalUtils()->checkNotFalse($utf8_csv_content, 'Could not convert to UTF-8');
67        return $utf8_csv_content;
68    }
69
70    /** @param array<string, string> $member */
71    public function getMemberIdent(array $member): ?string {
72        return $member[$this->member_ident_key] ?? null;
73    }
74
75    /** @param array<string, string> $member */
76    public function getMemberUsername(array $member): ?string {
77        return $member[$this->member_username_key] ?? null;
78    }
79
80    /** @param array<string, string> $member */
81    public function getMemberFirstName(array $member): ?string {
82        return $member[$this->member_first_name_key] ?? null;
83    }
84
85    /** @param array<string, string> $member */
86    public function getMemberLastName(array $member): ?string {
87        return $member[$this->member_last_name_key] ?? null;
88    }
89
90    /** @param array<string, string> $member */
91    public function getMemberCompany(array $member): ?string {
92        return $member[$this->member_company_key] ?? null;
93    }
94
95    public function update(Member $member, ?User $user): void {
96        if (!$user) {
97            return;
98        }
99        if ($member->getUser()?->getId() !== $user->getId()) {
100            $this->log()->error("Update requested for member {$member->getIdent()} ({$member->getUser()?->getId()} !== {$user->getId()})");
101            return;
102        }
103        $member_data = json_decode($member->getData(), true);
104        $user_data = $this->getUserData($user);
105        $update = [];
106        foreach ($user_data as $key => $user_value) {
107            $member_value = $member_data[$key] ?? '';
108            if ($member_value !== $user_value && $user_value !== '') {
109                $this->log()->info("Field {$key} was updated.");
110                $update[$key] = $user_value;
111            }
112        }
113        if ($update) {
114            $enc_update = json_encode($update);
115            $this->generalUtils()->checkNotFalse($enc_update, 'JSON encode failed');
116            $member->setUpdates($enc_update);
117        } else {
118            $member->setUpdates(null);
119        }
120    }
121
122    /** @return array<string, string> */
123    protected function getUserData(User $user): array {
124        return [
125            'Nachname' => $user->getLastName(),
126            'Vorname' => $user->getFirstName(),
127            'Adresse' => $user->getStreet() ?? '',
128            'PLZ' => $user->getPostalCode() ?? '',
129            'Ort' => $user->getCity() ?? '',
130            'Benutzer-Id' => $user->getUsername(),
131            // 'Land' => $user->getCountryCode() ?? '',
132            // 'E-Mail' => $user->getEmail() ?? '',
133            // 'Geschlecht' => $user->getGender() ?? '',
134            // 'Geburtsdatum' => $user->getBirthdate()?->format('Y-m-d') ?? '',
135            // 'SOLV NR' => $user->getSolvNumber() ?? '',
136            // 'Badge Nummer' => $user->getSiCardNumber() ?? '',
137            // 'Rechnungsversand' => 'E-Mail', // All online accounts get invoices by email
138
139            // TODO: Think about the other fields:
140            // "Telefon Privat" => $user->getPhone(),
141            // "Telefon Mobil"
142            // "Anrede"
143            // "Titel"
144            // "Briefanrede"
145            // "Adress-Zusatz"
146            // "Nationalität"
147            // "[Gruppen]"
148            // "Status"
149            // "[Rolle]"
150            // "Eintritt"
151            // "Mitgliedsjahre"
152            // "Austritt"
153            // "Zivilstand"
154            // "Jahrgang"
155            // "Alter"
156            // "Nie mahnen"
157            // "IBAN"
158            // "Kontoinhaber"
159            // "Mail-MV"
160            // "Werbegrund"
161            // "Geburtsjahr"
162        ];
163    }
164}