Passed
Pull Request — main (#1)
by Daniel
02:57
created

setLocalization()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 8
rs 10
1
<?php
2
3
/*
4
 * Copyright (c) 2024, Daniel Popiniuc and its licensors.
5
 *
6
 * All rights reserved. This program and the accompanying materials
7
 * are made available under the terms of the Eclipse Public License v1.0
8
 * which accompanies this distribution, and is available at
9
 * http://www.eclipse.org/legal/epl-v20.html
10
 *
11
 * Contributors:
12
 *    Daniel Popiniuc
13
 */
14
15
namespace danielgp\efactura;
16
17
class ClassElectronicInvoiceUserInterface
18
{
19
    use \danielgp\io_operations\InputOutputFiles;
20
21
    private array $arrayConfiguration;
22
    private \SebastianBergmann\Timer\Timer $classTimer;
23
    private $translation;
24
25
    public function __construct()
26
    {
27
        $this->classTimer         = new \SebastianBergmann\Timer\Timer();
28
        $this->classTimer->start();
29
        $this->arrayConfiguration = $this->getArrayFromJsonFile(__DIR__
30
            . DIRECTORY_SEPARATOR . 'config', 'BasicConfiguration.json');
31
        $this->setLocalization();
32
    }
33
34
    public function actionAnalyzeZIPfromANAFfromLocalFolder(string $strFilePath): array
35
    {
36
        $arrayFiles    = new \RecursiveDirectoryIterator($strFilePath, \FilesystemIterator::SKIP_DOTS);
37
        $arrayInvoices = [];
38
        $intFileNo     = 0;
39
        foreach ($arrayFiles as $strFile) {
40
            if ($strFile->isFile() && ($strFile->getExtension() === 'zip')) {
41
                $arrayInvoices[$intFileNo] = $this->setArchiveFromAnaf($strFile->getRealPath());
42
                $intFileNo++;
43
            }
44
        }
45
        return $arrayInvoices;
46
    }
47
48
    private function getButtonForLocalisation(string $strLanguageCountry): string
49
    {
50
        $arrayMapFlags = [
51
            'ro_RO' => 'ro',
52
            'it_IT' => 'it',
53
            'en_US' => 'us',
54
        ];
55
        return vsprintf('<a href="?language_COUNTRY=%s" style="float:left;margin-left:10px;">'
56
            . '<span class="fi fi-%s" style="%s">&nbsp;</span>'
57
            . '</a>', [
58
            $strLanguageCountry . (array_key_exists('action', $_GET) ? '&action=' . $_GET['action'] : ''),
59
            $arrayMapFlags[$strLanguageCountry],
60
            ($strLanguageCountry === $_GET['language_COUNTRY'] ? 'width:40px;height:30px;' : 'width:20px;height:15px;'),
61
        ]);
62
    }
63
64
    private function getButtonToActionSomething(array $arrayButtonFeatures): string
65
    {
66
        $arrayButtonStyle = [
67
            'font:bold 14pt Arial',
68
            'margin:2px',
69
            'padding:4px 10px',
70
        ];
71
        $arrayStylePieces = $arrayButtonStyle;
72
        if (array_key_exists('AdditionalStyle', $arrayButtonFeatures)) {
73
            $arrayStylePieces = array_merge($arrayButtonStyle, $arrayButtonFeatures['AdditionalStyle']);
74
        }
75
        return vsprintf('<a href="%s" class="btn btn-outline-primary" style="%s">%s</a>', [
76
            $arrayButtonFeatures['URL']
77
            . (array_key_exists('language_COUNTRY', $_GET) ? '&language_COUNTRY=' . $_GET['language_COUNTRY'] : ''),
78
            implode(';', $arrayStylePieces),
79
            $arrayButtonFeatures['Text'],
80
        ]);
81
    }
82
83
    private function setArchiveFromAnaf(string $strFile)
84
    {
85
        $classZip      = new \ZipArchive();
86
        $arrayToReturn = [];
87
        $res           = $classZip->open($strFile, \ZipArchive::RDONLY);
88
        if ($res === true) {
89
            $intFilesArchived = $classZip->numFiles;
90
            for ($intArchivedFile = 0; $intArchivedFile < $intFilesArchived; $intArchivedFile++) {
91
                $strArchivedFile = $classZip->getNameIndex($intArchivedFile);
92
                $strFileStats    = $classZip->statIndex($intArchivedFile);
93
                $matches         = [];
94
                preg_match('/^[0-9]{5,20}\.xml$/', $strArchivedFile, $matches, PREG_OFFSET_CAPTURE);
95
                $matches2        = [];
96
                preg_match('/^semnatura_[0-9]{5,20}\.xml$/', $strArchivedFile, $matches2, PREG_OFFSET_CAPTURE);
97
                if ($matches !== []) {
98
                    $resInvoice        = $classZip->getStream($strArchivedFile);
99
                    $strInvoiceContent = stream_get_contents($resInvoice);
100
                    fclose($resInvoice);
101
                    $arrayToReturn     = $this->setStandardizedFeedbackArray([
102
                        'Response_Index'      => pathinfo($strFile)['filename'],
103
                        'Size'                => $strFileStats['size'],
104
                        'FileDate'            => date('Y-m-d H:i:s', $strFileStats['mtime']),
105
                        'Matches'             => $matches,
106
                        'strArchivedFileName' => $strArchivedFile,
107
                        'strInvoiceContent'   => $strInvoiceContent,
108
                    ]);
109
                } elseif ($matches2 === []) {
110
                    echo vsprintf('<div>' . $this->arrayConfiguration['Feedback']['DifferentFile'] . '</div>', [
111
                        $strArchivedFile,
112
                        $strFile->getBasename(),
113
                    ]);
114
                }
115
            }
116
        } else {
117
            // @codeCoverageIgnoreStart
118
            throw new \RuntimeException(sprintf('Archive %s could not be opened!', $strFile));
119
            // @codeCoverageIgnoreEnd
120
        }
121
        return $arrayToReturn;
122
    }
123
124
    private function getHeaderColumnMapping(array $arrayColumns): array
125
    {
126
        $arrayMap = [];
127
        foreach ($arrayColumns as $strColumnName) {
128
            $arrayMap[$strColumnName] = $strColumnName;
129
            $strRelevant              = $this->translation->find(null, 'i18n_Clmn_' . $strColumnName);
130
            if (!is_null($strRelevant)) {
131
                $arrayMap[$strColumnName] = $strRelevant->getTranslation();
132
            }
133
        }
134
        return $arrayMap;
135
    }
136
137
    /**
138
     * Archived document interpretation requires a temporary files to be stored
139
     * and upon processing file is removed immediately
140
     *
141
     * @param array $arrayData
142
     * @return array
143
     */
144
    private function getDocumentDetails(array $arrayData): array
145
    {
146
        file_put_contents($arrayData['strArchivedFileName'], $arrayData['strInvoiceContent']);
147
        $appR               = new \danielgp\efactura\ClassElectronicInvoiceRead();
148
        $arrayElectronicInv = $appR->readElectronicInvoice($arrayData['strArchivedFileName']);
149
        $arrayBasic         = $arrayElectronicInv['Header']['CommonBasicComponents-2'];
150
        $arrayAggregate     = $arrayElectronicInv['Header']['CommonAggregateComponents-2'];
151
        $arrayStandardized  = [
152
            'Customer'    => $arrayAggregate['AccountingCustomerParty']['Party'],
153
            'ID'          => $arrayBasic['ID'],
154
            'IssueDate'   => $arrayBasic['IssueDate'],
155
            'No_of_Lines' => count($arrayElectronicInv['Lines']),
156
            'Supplier'    => $arrayAggregate['AccountingSupplierParty']['Party'],
157
            'TOTAL'       => (float) $arrayAggregate['LegalMonetaryTotal']['TaxInclusiveAmount']['value'],
158
            'wo_VAT'      => (float) $arrayAggregate['LegalMonetaryTotal']['TaxExclusiveAmount']['value'],
159
        ];
160
        unlink($arrayData['strArchivedFileName']);
161
        return $arrayStandardized;
162
    }
163
164
    public function setActionToDo(): void
165
    {
166
        echo '<main>';
167
        $arrayOptions = [
168
            'action' => FILTER_SANITIZE_SPECIAL_CHARS,
169
        ];
170
        $arrayInputs  = filter_input_array(INPUT_GET, $arrayOptions);
171
        if (array_key_exists('action', $arrayInputs)) {
172
            switch ($arrayInputs['action']) {
173
                case 'AnalyzeZIPfromANAFfromLocalFolder':
174
                    $strRelevantFolder = 'P:/eFactura_Responses/Luna_Anterioara_NeDeclarata_Inca/';
175
                    $arrayInvoices     = $this->actionAnalyzeZIPfromANAFfromLocalFolder($strRelevantFolder);
176
                    if (count($arrayInvoices) === 0) {
177
                        echo sprintf('<p style="color:red;">'
178
                            . $this->translation->find(null, 'i18n_Msg_NoZip')->getTranslation()
179
                            . '</p>', $strRelevantFolder);
180
                    } else {
181
                        echo $this->setHtmlTable($arrayInvoices);
182
                    }
183
                    break;
184
            }
185
        }
186
        echo '</main>';
187
    }
188
189
    private function setDataSupplierOrCustomer(array $arrayData)
190
    {
191
        $strCustomerCui = '';
192
        if (isset($arrayData['PartyTaxScheme']['01']['CompanyID'])) {
193
            $strCustomerCui = $arrayData['PartyTaxScheme']['01']['CompanyID'];
194
        } else {
195
            $strCustomerCui = $arrayData['PartyLegalEntity']['CompanyID'];
196
        }
197
        if (is_numeric($strCustomerCui)) {
198
            $strCustomerCui = 'RO' . $strCustomerCui;
199
        }
200
        return $strCustomerCui;
201
    }
202
203
    private function setDaysElapsed(string $strFirstDate, string $strLaterDate): string
204
    {
205
        $origin   = new \DateTimeImmutable($strFirstDate);
206
        $target   = new \DateTimeImmutable($strLaterDate);
207
        $interval = $origin->diff($target);
208
        return $interval->format('%R%a');
209
    }
210
211
    public function setHtmlFooter(): void
212
    {
213
        $strHtmlContent = implode('', $this->getFileEntireContent(implode(DIRECTORY_SEPARATOR, [
214
                __DIR__,
215
                'HTML',
216
                'footer.html',
217
        ])));
218
        echo vsprintf($strHtmlContent, [
219
            (new \SebastianBergmann\Timer\ResourceUsageFormatter())->resourceUsage($this->classTimer->stop()),
220
            date('Y'),
221
            $this->arrayConfiguration['Application']['Developer'],
222
        ]);
223
    }
224
225
    public function setHtmlHeader(): void
226
    {
227
        $strHtmlContent = implode('', $this->getFileEntireContent(implode(DIRECTORY_SEPARATOR, [
228
                __DIR__,
229
                'HTML',
230
                'header.html',
231
        ])));
232
        echo vsprintf($strHtmlContent, [
233
            $this->arrayConfiguration['Application']['Name'],
234
            $this->arrayConfiguration['Application']['Developer'],
235
        ]);
236
    }
237
238
    public function setHtmlTable(array $arrayData): string
239
    {
240
        $strReturn = '<table style="margin-left:auto;margin-right:auto;">';
241
        foreach ($arrayData as $intLineNo => $arrayContent) {
242
            ksort($arrayContent);
243
            if ($intLineNo === 0) {
244
                $strReturn .= $this->setHtmlTableHeader(array_keys($arrayContent))
245
                    . '<tbody>';
246
            }
247
            $strReturn .= $this->setHtmlTableLine(($intLineNo + 1), $arrayContent);
248
        }
249
        return ($strReturn . '</tbody>' . '</table>');
250
    }
251
252
    private function setHtmlTableHeader(array $arrayData): string
253
    {
254
        $arrayMap    = $this->getHeaderColumnMapping(array_values($arrayData));
255
        $strToReturn = '<th>#</th>';
256
        foreach ($arrayData as $key) {
257
            $strToReturn .= sprintf('<th>%s</th>', (array_key_exists($key, $arrayMap) ? $arrayMap[$key] : $key));
258
        }
259
        return '<thead><tr>' . $strToReturn . '</tr></thead>';
260
    }
261
262
    private function setHtmlTableLine(int $intLineNo, array $arrayLine): string
263
    {
264
        $arrayContent = [];
265
        foreach ($arrayLine as $strColumn => $strValue) {
266
            if (str_starts_with($strColumn, 'Amount_')) {
267
                $arrayContent[] = sprintf('<td style="text-align:right;">%s</td>', $this->setNumbers($strValue, 2, 2));
268
            } elseif (str_starts_with($strColumn, 'Size')) {
269
                $arrayContent[] = sprintf('<td style="text-align:right;">%s</td>', $this->setNumbers($strValue, 0, 0));
270
            } else {
271
                $arrayContent[] = sprintf('<td>%s</td>', $strValue);
272
            }
273
        }
274
        return '<tr' . ($arrayLine['Error'] === '' ? '' : ' style="color:red;"') . '>'
275
            . '<td>' . $intLineNo . '</td>'
276
            . implode('', $arrayContent)
277
            . '</tr>';
278
    }
279
280
    private function setLocalization(): void
281
    {
282
        if (!array_key_exists('language_COUNTRY', $_GET)) {
283
            $_GET['language_COUNTRY'] = 'ro_RO';
284
        }
285
        $loader            = new \Gettext\Loader\PoLoader();
286
        $this->translation = $loader->loadFile(__DIR__ . '/locale/' . $_GET['language_COUNTRY']
287
            . '/LC_MESSAGES/eFactura.po');
288
    }
289
290
    private function setNumbers(float $floatNumber, int $intMinDigits, int $intMaxDigits): string
291
    {
292
        $classFormat = new \NumberFormatter($_GET['language_COUNTRY'], \NumberFormatter::DECIMAL);
293
        $classFormat->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $intMinDigits);
294
        $classFormat->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $intMaxDigits);
295
        return $classFormat->format($floatNumber);
296
    }
297
298
    private function setStandardizedFeedbackArray(array $arrayData): array
299
    {
300
        $arrayToReturn = [
301
            'Response_Date'   => '',
302
            'Response_Index'  => $arrayData['Response_Index'],
303
            'Loading_Index'   => '',
304
            'Size'            => '',
305
            'Document_No'     => '',
306
            'Issue_Date'      => '',
307
            'Issue_YearMonth' => '',
308
            'Amount_wo_VAT'   => '',
309
            'Amount_TOTAL'    => '',
310
            'Amount_VAT'      => '',
311
            'Supplier_CUI'    => '',
312
            'Supplier_Name'   => '',
313
            'Customer_CUI'    => '',
314
            'Customer_Name'   => '',
315
            'No_of_Lines'     => '',
316
            'Error'           => '',
317
            'Days_Between'    => '',
318
        ];
319
        if ($arrayData['Size'] > 1000) {
320
            $arrayAttr                        = $this->getDocumentDetails($arrayData);
321
            $arrayToReturn['Loading_Index']   = substr($arrayData['Matches'][0][0], 0, -4);
322
            $arrayToReturn['Size']            = $arrayData['Size'];
323
            $arrayToReturn['Document_No']     = $arrayAttr['ID'];
324
            $arrayToReturn['Issue_Date']      = $arrayAttr['IssueDate'];
325
            $arrayToReturn['Issue_YearMonth'] = (new \IntlDateFormatter(
326
                    $_GET['language_COUNTRY'],
327
                    \IntlDateFormatter::FULL,
328
                    \IntlDateFormatter::FULL,
329
                    $this->translation->find(null, 'i18n_TimeZone')->getTranslation(),
330
                    \IntlDateFormatter::GREGORIAN,
331
                    'r-MM__MMMM'
332
                ))->format(new \DateTime($arrayAttr['IssueDate']));
333
            $arrayToReturn['Response_Date']   = $arrayData['FileDate'];
334
            $arrayToReturn['Amount_wo_VAT']   = $arrayAttr['wo_VAT'];
335
            $arrayToReturn['Amount_TOTAL']    = $arrayAttr['TOTAL'];
336
            $arrayToReturn['Amount_VAT']      = round(($arrayAttr['TOTAL'] - $arrayAttr['wo_VAT']), 2);
337
            $arrayToReturn['Supplier_CUI']    = $this->setDataSupplierOrCustomer($arrayAttr['Supplier']);
338
            $arrayToReturn['Supplier_Name']   = $arrayAttr['Supplier']['PartyLegalEntity']['RegistrationName'];
339
            $arrayToReturn['Customer_CUI']    = $this->setDataSupplierOrCustomer($arrayAttr['Customer']);
340
            $arrayToReturn['Customer_Name']   = $arrayAttr['Customer']['PartyLegalEntity']['RegistrationName'];
341
            $arrayToReturn['No_of_Lines']     = $arrayAttr['No_of_Lines'];
342
            $arrayToReturn['Days_Between']    = $this->setDaysElapsed($arrayAttr['IssueDate'], $arrayData['FileDate']);
343
        } elseif ($arrayData['Size'] > 0) {
344
            $objErrors                      = new \SimpleXMLElement($arrayData['strInvoiceContent']);
345
            $arrayToReturn['Loading_Index'] = $objErrors->attributes()->Index_incarcare->__toString();
346
            $arrayToReturn['Size']          = $arrayData['Size'];
347
            $arrayToReturn['Response_Date'] = $arrayData['FileDate'];
348
            $arrayToReturn['Supplier_CUI']  = 'RO' . $objErrors->attributes()->Cif_emitent->__toString();
349
            $arrayToReturn['Supplier_Name'] = '??????????';
350
            $arrayToReturn['Error']         = '<div style="max-width:200px;font-size:0.8rem;">'
351
                . $objErrors->Error->attributes()->errorMessage->__toString() . '</div>';
352
        }
353
        return $arrayToReturn;
354
    }
355
356
    public function setUserInterface(): void
357
    {
358
        echo '<header class="border-bottom">'
359
        . $this->getButtonForLocalisation('en_US')
360
        . $this->getButtonForLocalisation('it_IT')
361
        . $this->getButtonForLocalisation('ro_RO')
362
        . $this->getButtonToActionSomething([
363
            'Text' => $this->translation->find(null, 'i18n_Btn_AnalyzeZIP')->getTranslation(),
364
            'URL'  => '?action=AnalyzeZIPfromANAFfromLocalFolder',
365
        ])
366
        . ' </header>';
367
    }
368
}
369