Browse Source

Add basic transaction mapper

pull/373/head
James Cole 2 years ago
parent
commit
ce1ea04612
  1. 3
      .ci/php-cs-fixer/.php-cs-fixer.php
  2. 48
      .ci/php-cs-fixer/composer.lock
  3. 28
      app/Services/CSV/Roles/RoleService.php
  4. 82
      app/Services/Camt/Conversion/TransactionMapper.php
  5. 8
      app/Services/Camt/Transaction.php

3
.ci/php-cs-fixer/.php-cs-fixer.php

@ -38,5 +38,4 @@ return $config->setRules([
'strict_param' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder)
;
->setFinder($finder);

48
.ci/php-cs-fixer/composer.lock

@ -677,16 +677,16 @@
},
{
"name": "sebastian/diff",
"version": "5.0.1",
"version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02"
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"shasum": ""
},
"require": {
@ -732,7 +732,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.1"
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3"
},
"funding": [
{
@ -740,20 +740,20 @@
"type": "github"
}
],
"time": "2023-03-23T05:12:41+00:00"
"time": "2023-05-01T07:48:21+00:00"
},
{
"name": "symfony/console",
"version": "v6.2.8",
"version": "v6.2.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b"
"reference": "12288d9f4500f84a4d02254d4aa968b15488476f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b",
"reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b",
"url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f",
"reference": "12288d9f4500f84a4d02254d4aa968b15488476f",
"shasum": ""
},
"require": {
@ -820,7 +820,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.2.8"
"source": "https://github.com/symfony/console/tree/v6.2.10"
},
"funding": [
{
@ -836,7 +836,7 @@
"type": "tidelift"
}
],
"time": "2023-03-29T21:42:15+00:00"
"time": "2023-04-28T13:37:43+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -1069,16 +1069,16 @@
},
{
"name": "symfony/filesystem",
"version": "v6.2.7",
"version": "v6.2.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "82b6c62b959f642d000456f08c6d219d749215b3"
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3",
"reference": "82b6c62b959f642d000456f08c6d219d749215b3",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"shasum": ""
},
"require": {
@ -1112,7 +1112,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.2.7"
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
},
"funding": [
{
@ -1128,7 +1128,7 @@
"type": "tidelift"
}
],
"time": "2023-02-14T08:44:56+00:00"
"time": "2023-04-18T13:46:08+00:00"
},
{
"name": "symfony/finder",
@ -1755,16 +1755,16 @@
},
{
"name": "symfony/process",
"version": "v6.2.8",
"version": "v6.2.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "75ed64103df4f6615e15a7fe38b8111099f47416"
"reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416",
"reference": "75ed64103df4f6615e15a7fe38b8111099f47416",
"url": "https://api.github.com/repos/symfony/process/zipball/b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e",
"reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e",
"shasum": ""
},
"require": {
@ -1796,7 +1796,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v6.2.8"
"source": "https://github.com/symfony/process/tree/v6.2.10"
},
"funding": [
{
@ -1812,7 +1812,7 @@
"type": "tidelift"
}
],
"time": "2023-03-09T16:20:02+00:00"
"time": "2023-04-18T13:56:57+00:00"
},
{
"name": "symfony/service-contracts",

28
app/Services/CSV/Roles/RoleService.php

@ -227,24 +227,24 @@ class RoleService
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;
}
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) {
for($index = 0; $index < $splits; $index++) {
$value = $transaction->getFieldByIndex($name, $index);
if(null !== $value && '' !== $value) {
$examples[$name][] = $value;
}
}
for($index = 0; $index < $splits; $index++) {
$value = $transaction->getFieldByIndex($name, $index);
if(null !== $value && '' !== $value) {
$examples[$name][] = $value;
}
}
} else {
$value = $transaction->getFieldByIndex($name, 0);
if(null !== $value && '' !== $value) {
$examples[$name][] = $value;
}
$value = $transaction->getFieldByIndex($name, 0);
if(null !== $value && '' !== $value) {
$examples[$name][] = $value;
}
}
}
$count++;

82
app/Services/Camt/Conversion/TransactionMapper.php

@ -3,6 +3,7 @@
namespace App\Services\Camt\Conversion;
use App\Exceptions\ImporterErrorException;
use App\Services\CSV\Mapper\GetAccounts;
use App\Services\Shared\Configuration\Configuration;
use Carbon\Carbon;
@ -11,6 +12,7 @@ use Carbon\Carbon;
*/
class TransactionMapper
{
use GetAccounts;
private Configuration $configuration;
/**
@ -28,6 +30,8 @@ class TransactionMapper
*/
public function map(array $transactions): array
{
// TODO download all accounts from Firefly III, we may need them for verification.
app('log')->debug(sprintf('Now mapping %d transaction(s)', count($transactions)));
$result = [];
/** @var array $transaction */
@ -132,6 +136,10 @@ class TransactionMapper
}
}
$current = $this->sanityCheck($current);
if(null === $current) {
// give warning, skip transaction.
}
// TODO loop over $current and clean up if necessary.
$result['transactions'][] = $current;
@ -192,5 +200,79 @@ class TransactionMapper
return $current;
}
/**
* A transaction has a bunch of minimal requirements. This method checks if they are met.
*
* It will also correct the transaction type (if possible).
*
* @param array $current
*
* @return array|null
*/
private function sanityCheck(array $current): ?array
{
// at this point the source and destination could be set according to the content of the XML.
// but they could be reversed: in the case of incoming money the "source" is actually the
// relatedParty / opposing party and not the normal account. So both accounts (if present in the array)
// need to be validated to see what types they are. This also depends on the amount (positive or negative).
// not set source_id, iban or name? Then add the backup account
if(
!array_key_exists('source_id', $current) &&
!array_key_exists('source_name', $current) &&
!array_key_exists('source_iban', $current) &&
!array_key_exists('source_number', $current)) {
// TODO add backup account
}
$sourceIsNew = false;
// not set source_id, but others are present? Make sure the account mentioned actually exists.
// if it does not exist (it is "new"), do nothing for the time being just mark it as such.
if(
!array_key_exists('source_id', $current) &&
(array_key_exists('source_name', $current) ||
array_key_exists('source_iban', $current) ||
array_key_exists('source_number', $current))) {
// the reverse is true: if the info is valid, the source account is not "new".
$sourceIsNew = !$this->validAccountInfo('source', $current);
}
// not set destination? Then add a fake one
if(
!array_key_exists('destination_id', $current) &&
!array_key_exists('destination_name', $current) &&
!array_key_exists('destination_iban', $current) &&
!array_key_exists('destination_number', $current)) {
// TODO add backup account
}
// if the source is asset account AND the destination is expense or new AND amount is neg = withdrawal
// if the source is asset account AND the destination is revenue or new AND amount is pos = deposit
// if both are transfer AND amount is pos = transfer from dest to source
// if both are transfer AND amount is neg = transfer from source to dest
// any other combination is "illegal" and needs a warning.
// no description?
// no amount?
// no date?
return $current;
}
/**
* @param string $direction
* @param array $current
*
* @return bool
*/
private function validAccountInfo(string $direction, array $current): bool
{
// search for existing IBAN
// search for existing number
// search for existing name, TODO under which types?
return false;
}
}

8
app/Services/Camt/Transaction.php

@ -100,12 +100,12 @@ class Transaction
// end temporary debug message
throw new ImporterErrorException(sprintf('Unknown field "%s" in getFieldByIndex(%d)', $field, $index));
// LEVEL A
// LEVEL A
case 'messageId':
// always the same, since its level A.
return (string)$this->levelA->getGroupHeader()->getMessageId();
// LEVEL B
// LEVEL B
case 'statementId':
// always the same, since its level B.
return (string)$this->levelB->getId();
@ -129,7 +129,7 @@ class Transaction
$ret = $this->levelB->getAccount()->getIdentification();
}
// LEVEL C
// LEVEL C
return $ret;
case 'entryAccountServicerReference':
// always the same, since its level C.
@ -162,7 +162,7 @@ class Transaction
// always the same, since its level C.
return (string)$this->levelC->getBankTransactionCode()->getDomain()->getFamily()->getSubFamilyCode();
// LEVEL D
// LEVEL D
case 'entryDetailAccountServicerReference':
if (0 === count($this->levelD) || !array_key_exists($index, $this->levelD)) {
return '';

Loading…
Cancel
Save