You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

254 lines
8.5 KiB

<?php
/*
* RoleService.php
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of the Firefly III Data Importer
* (https://github.com/firefly-iii/data-importer).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\CSV\Roles;
use App\Services\Camt\Transaction;
use App\Services\Session\Constants;
use App\Services\Shared\Configuration\Configuration;
use App\Services\Storage\StorageService;
use Genkgo\Camt\Camt053\DTO\Statement as CamtStatement;
use Genkgo\Camt\Config;
use Genkgo\Camt\Reader as CamtReader;
use Illuminate\Support\Facades\Log;
use League\Csv\Exception;
use League\Csv\InvalidArgument;
use League\Csv\Reader;
use League\Csv\Statement;
use League\Csv\UnableToProcessCsv;
/**
* Class RoleService
*/
class RoleService
{
public const EXAMPLE_COUNT = 7;
public const EXAMPLE_LENGTH = 26;
/**
* @throws InvalidArgument
* @throws UnableToProcessCsv
*/
public static function getColumns(string $content, Configuration $configuration): array
{
$reader = Reader::createFromString($content);
// configure reader:
$delimiter = $configuration->getDelimiter();
switch ($delimiter) {
default:
case 'comma':
$reader->setDelimiter(',');
break;
case 'semicolon':
$reader->setDelimiter(';');
break;
case 'tab':
$reader->setDelimiter("\t");
break;
}
$headers = [];
if (true === $configuration->isHeaders()) {
try {
$stmt = new Statement()->limit(1)->offset(0);
$records = $stmt->process($reader);
$headers = $records->fetchOne();
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::error($e->getMessage());
throw new \InvalidArgumentException($e->getMessage());
}
// @codeCoverageIgnoreEnd
Log::debug('Detected file headers:', $headers);
}
if (false === $configuration->isHeaders()) {
Log::debug('Role service: file has no headers');
try {
$stmt = new Statement()->limit(1)->offset(0);
$records = $stmt->process($reader);
$count = count($records->fetchOne());
Log::debug(sprintf('Role service: first row has %d columns', $count));
for ($i = 0; $i < $count; ++$i) {
$headers[] = sprintf('Column #%d', $i + 1);
}
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::error($e->getMessage());
throw new \InvalidArgumentException($e->getMessage());
}
}
return $headers;
}
/**
* @throws Exception
*/
public static function getExampleData(string $content, Configuration $configuration): array
{
$reader = Reader::createFromString($content);
// configure reader:
$delimiter = $configuration->getDelimiter();
switch ($delimiter) {
default:
case 'comma':
$reader->setDelimiter(',');
break;
case 'semicolon':
$reader->setDelimiter(';');
break;
case 'tab':
$reader->setDelimiter("\t");
break;
}
$offset = $configuration->isHeaders() ? 1 : 0;
$examples = [];
// make statement.
try {
$stmt = new Statement()->limit(self::EXAMPLE_COUNT)->offset($offset);
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::error($e->getMessage());
throw new \InvalidArgumentException($e->getMessage());
}
/** @codeCoverageIgnoreEnd */
// grab the records:
$records = $stmt->process($reader);
/** @var array $line */
foreach ($records as $line) {
$line = array_values($line);
// $line = SpecificService::runSpecifics($line, $configuration->getSpecifics());
foreach ($line as $index => $cell) {
if (strlen((string) $cell) > self::EXAMPLE_LENGTH) {
$cell = sprintf('%s...', substr((string) $cell, 0, self::EXAMPLE_LENGTH));
}
$examples[$index][] = $cell;
$examples[$index] = array_unique($examples[$index]);
}
}
foreach ($examples as $line => $entries) {
asort($entries);
$examples[$line] = $entries;
}
return $examples;
}
public static function getExampleDataFromCamt(string $content, Configuration $configuration): array
{
$camtReader = new CamtReader(Config::getDefault());
$camtMessage = $camtReader->readString(StorageService::getContent(session()->get(Constants::UPLOAD_DATA_FILE))); // -> Level A
$transactions = [];
$examples = [];
$fieldNames = array_keys(config('camt.fields'));
foreach ($fieldNames as $name) {
$examples[$name] = [];
}
/**
* This code creates separate Transaction objects for transaction details,
* even when the user indicates these details should be splits or ignored entirely.
* This is because we still need to extract possible example data from these transaction details.
*/
$statements = $camtMessage->getRecords();
/** @var CamtStatement $statement */
foreach ($statements as $statement) { // -> Level B
$entries = $statement->getEntries();
foreach ($entries as $entry) { // -> Level C
$count = count($entry->getTransactionDetails()); // count level D entries.
if (0 === $count) {
// TODO Create a single transaction, I guess?
$transactions[] = new Transaction($configuration, $camtMessage, $statement, $entry, []);
}
if (0 !== $count) {
foreach ($entry->getTransactionDetails() as $detail) {
$transactions[] = new Transaction($configuration, $camtMessage, $statement, $entry, [$detail]);
}
}
}
}
$count = 0;
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
if (15 === $count) { // do not check more than 15 transactions to fill the example-data
break;
}
foreach ($fieldNames as $name) {
if (array_key_exists($name, $examples)) { // there is at least one example, so we can check how many
if (count($examples[$name]) > 5) { // there are already five examples, so jump to next field
continue;
}
} // otherwise, try to fetch data
$splits = $transaction->countSplits();
if (0 === $splits) {
$value = $transaction->getFieldByIndex($name, 0);
if ('' !== $value) {
$examples[$name][] = $value;
}
}
if ($splits > 0) {
for ($index = 0; $index < $splits; ++$index) {
$value = $transaction->getFieldByIndex($name, $index);
if ('' !== $value) {
$examples[$name][] = $value;
}
}
}
}
++$count;
}
foreach ($examples as $key => $list) {
$examples[$key] = array_unique($list);
$examples[$key] = array_filter($examples[$key], fn (string $value) => '' !== $value);
}
return $examples;
}
}