mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Migrated all code to group collector.
This commit is contained in:
parent
eb6329e556
commit
e15c35de64
@ -162,166 +162,6 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $collection
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function parseArray(Collection $collection): Collection
|
|
||||||
{
|
|
||||||
$groups = [];
|
|
||||||
/** @var TransactionGroup $augmentedGroup */
|
|
||||||
foreach ($collection as $augmentedGroup) {
|
|
||||||
$groupId = $augmentedGroup->transaction_group_id;
|
|
||||||
if (!isset($groups[$groupId])) {
|
|
||||||
// make new array
|
|
||||||
$groupArray = [
|
|
||||||
'id' => $augmentedGroup->transaction_group_id,
|
|
||||||
'user_id' => $augmentedGroup->user_id,
|
|
||||||
'title' => $augmentedGroup->transaction_group_title,
|
|
||||||
'count' => 1,
|
|
||||||
'sums' => [],
|
|
||||||
'transactions' => [],
|
|
||||||
];
|
|
||||||
$journalId = (int)$augmentedGroup->transaction_journal_id;
|
|
||||||
$groupArray['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
|
|
||||||
$groups[$groupId] = $groupArray;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// or parse the rest.
|
|
||||||
$journalId = (int)$augmentedGroup->transaction_journal_id;
|
|
||||||
$groups[$groupId]['count']++;
|
|
||||||
|
|
||||||
if (isset($groups[$groupId]['transactions'][$journalId])) {
|
|
||||||
$groups[$groupId]['transactions'][$journalId] =
|
|
||||||
$this->mergeTags($groups[$groupId]['transactions'][$journalId], $augmentedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($groups[$groupId]['transactions'][$journalId])) {
|
|
||||||
$groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
$groups = $this->parseSums($groups);
|
|
||||||
|
|
||||||
return new Collection($groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param TransactionGroup $augmentedGroup
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function parseAugmentedGroup(TransactionGroup $augmentedGroup): array
|
|
||||||
{
|
|
||||||
$result = $augmentedGroup->toArray();
|
|
||||||
$result['tags'] = [];
|
|
||||||
$result['date'] = new Carbon($result['date']);
|
|
||||||
$result['created_at'] = new Carbon($result['created_at']);
|
|
||||||
$result['updated_at'] = new Carbon($result['updated_at']);
|
|
||||||
$result['reconciled'] = 1 === (int)$result['reconciled'];
|
|
||||||
if (isset($augmentedGroup['tag_id'])) { // assume the other fields are present as well.
|
|
||||||
$tagId = (int)$augmentedGroup['tag_id'];
|
|
||||||
$tagDate = null;
|
|
||||||
try {
|
|
||||||
$tagDate = Carbon::parse($augmentedGroup['tag_date']);
|
|
||||||
} catch (InvalidDateException $e) {
|
|
||||||
Log::debug(sprintf('Could not parse date: %s', $e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$result['tags'][$tagId] = [
|
|
||||||
'id' => (int)$result['tag_id'],
|
|
||||||
'name' => $result['tag_name'],
|
|
||||||
'date' => $tagDate,
|
|
||||||
'description' => $result['tag_description'],
|
|
||||||
'latitude' => $result['tag_latitude'],
|
|
||||||
'longitude' => $result['tag_longitude'],
|
|
||||||
'zoom_level' => $result['tag_zoom_level'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $existingJournal
|
|
||||||
* @param TransactionGroup $newGroup
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function mergeTags(array $existingJournal, TransactionGroup $newGroup): array
|
|
||||||
{
|
|
||||||
$newArray = $newGroup->toArray();
|
|
||||||
if (isset($newArray['tag_id'])) { // assume the other fields are present as well.
|
|
||||||
$tagId = (int)$newGroup['tag_id'];
|
|
||||||
|
|
||||||
$tagDate = null;
|
|
||||||
try {
|
|
||||||
$tagDate = Carbon::parse($newArray['tag_date']);
|
|
||||||
} catch (InvalidDateException $e) {
|
|
||||||
Log::debug(sprintf('Could not parse date: %s', $e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$existingJournal['tags'][$tagId] = [
|
|
||||||
'id' => (int)$newArray['tag_id'],
|
|
||||||
'name' => $newArray['tag_name'],
|
|
||||||
'date' => $tagDate,
|
|
||||||
'description' => $newArray['tag_description'],
|
|
||||||
'latitude' => $newArray['tag_latitude'],
|
|
||||||
'longitude' => $newArray['tag_longitude'],
|
|
||||||
'zoom_level' => $newArray['tag_zoom_level'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $existingJournal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $groups
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function parseSums(array $groups): array
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int $groudId
|
|
||||||
* @var array $group
|
|
||||||
*/
|
|
||||||
foreach ($groups as $groudId => $group) {
|
|
||||||
/** @var array $transaction */
|
|
||||||
foreach ($group['transactions'] as $transaction) {
|
|
||||||
$currencyId = (int)$transaction['currency_id'];
|
|
||||||
|
|
||||||
// set default:
|
|
||||||
if (!isset($groups[$groudId]['sums'][$currencyId])) {
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['currency_code'];
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['currency_symbol'];
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['currency_decimal_places'];
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
|
|
||||||
}
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
|
|
||||||
|
|
||||||
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
|
|
||||||
$currencyId = (int)$transaction['foreign_currency_id'];
|
|
||||||
|
|
||||||
// set default:
|
|
||||||
if (!isset($groups[$groudId]['sums'][$currencyId])) {
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['foreign_currency_code'];
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['foreign_currency_symbol'];
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['foreign_currency_decimal_places'];
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
|
|
||||||
}
|
|
||||||
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['foreign_amount']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define which accounts can be part of the source and destination transactions.
|
* Define which accounts can be part of the source and destination transactions.
|
||||||
*
|
*
|
||||||
@ -355,12 +195,46 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function setBill(Bill $bill): GroupCollectorInterface
|
public function setBill(Bill $bill): GroupCollectorInterface
|
||||||
{
|
{
|
||||||
$this->withBudgetInformation();
|
$this->withBillInformation();
|
||||||
$this->query->where('transaction_journals.bill_id', '=', $bill->id);
|
$this->query->where('transaction_journals.bill_id', '=', $bill->id);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will include bill name + ID, if any.
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function withBillInformation(): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
if (false === $this->hasBillInformation) {
|
||||||
|
// join bill table
|
||||||
|
$this->query->leftJoin('bills', 'bills.id', '=', 'transaction_journals.bill_id');
|
||||||
|
// add fields
|
||||||
|
$this->fields[] = 'bills.id as bill_id';
|
||||||
|
$this->fields[] = 'bills.name as bill_name';
|
||||||
|
$this->hasBillInformation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the search to a specific budget.
|
||||||
|
*
|
||||||
|
* @param Budget $budget
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setBudget(Budget $budget): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->withBudgetInformation();
|
||||||
|
$this->query->where('budgets.id', $budget->id);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will include budget ID + name, if any.
|
* Will include budget ID + name, if any.
|
||||||
*
|
*
|
||||||
@ -382,21 +256,6 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit the search to a specific budget.
|
|
||||||
*
|
|
||||||
* @param Budget $budget
|
|
||||||
*
|
|
||||||
* @return GroupCollectorInterface
|
|
||||||
*/
|
|
||||||
public function setBudget(Budget $budget): GroupCollectorInterface
|
|
||||||
{
|
|
||||||
$this->withBudgetInformation();
|
|
||||||
$this->query->where('budgets.id', $budget->id);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the search to a specific set of budgets.
|
* Limit the search to a specific set of budgets.
|
||||||
*
|
*
|
||||||
@ -554,26 +413,6 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Join table to get tag information.
|
|
||||||
*/
|
|
||||||
private function joinTagTables(): void
|
|
||||||
{
|
|
||||||
if (false === $this->hasJoinedTagTables) {
|
|
||||||
// join some extra tables:
|
|
||||||
$this->hasJoinedTagTables = true;
|
|
||||||
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
|
||||||
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
|
|
||||||
$this->fields[] = 'tags.id as tag_id';
|
|
||||||
$this->fields[] = 'tags.tag as tag_name';
|
|
||||||
$this->fields[] = 'tags.date as tag_date';
|
|
||||||
$this->fields[] = 'tags.description as tag_description';
|
|
||||||
$this->fields[] = 'tags.latitude as tag_latitude';
|
|
||||||
$this->fields[] = 'tags.longitude as tag_longitude';
|
|
||||||
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the search to one specific transaction group.
|
* Limit the search to one specific transaction group.
|
||||||
*
|
*
|
||||||
@ -617,44 +456,6 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the query.
|
|
||||||
*/
|
|
||||||
private function startQuery(): void
|
|
||||||
{
|
|
||||||
app('log')->debug('GroupCollector::startQuery');
|
|
||||||
$this->query = $this->user
|
|
||||||
->transactionGroups()
|
|
||||||
->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
|
|
||||||
// join source transaction.
|
|
||||||
->leftJoin(
|
|
||||||
'transactions as source', function (JoinClause $join) {
|
|
||||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
|
|
||||||
->where('source.amount', '<', 0);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// join destination transaction
|
|
||||||
->leftJoin(
|
|
||||||
'transactions as destination', function (JoinClause $join) {
|
|
||||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
|
|
||||||
->where('destination.amount', '>', 0);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// left join transaction type.
|
|
||||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
|
||||||
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
|
|
||||||
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
|
|
||||||
->whereNull('transaction_groups.deleted_at')
|
|
||||||
->whereNull('transaction_journals.deleted_at')
|
|
||||||
->whereNull('source.deleted_at')
|
|
||||||
->whereNull('destination.deleted_at')
|
|
||||||
->orderBy('transaction_journals.date', 'DESC')
|
|
||||||
->orderBy('transaction_journals.order', 'ASC')
|
|
||||||
->orderBy('transaction_journals.id', 'DESC')
|
|
||||||
->orderBy('transaction_journals.description', 'DESC')
|
|
||||||
->orderBy('source.amount', 'DESC');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically include all stuff required to make API calls work.
|
* Automatically include all stuff required to make API calls work.
|
||||||
*
|
*
|
||||||
@ -708,25 +509,6 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Will include bill name + ID, if any.
|
|
||||||
*
|
|
||||||
* @return GroupCollectorInterface
|
|
||||||
*/
|
|
||||||
public function withBillInformation(): GroupCollectorInterface
|
|
||||||
{
|
|
||||||
if (false === $this->hasBillInformation) {
|
|
||||||
// join bill table
|
|
||||||
$this->query->leftJoin('bills', 'bills.id', '=', 'transaction_journals.bill_id');
|
|
||||||
// add fields
|
|
||||||
$this->fields[] = 'bills.id as bill_id';
|
|
||||||
$this->fields[] = 'bills.name as bill_name';
|
|
||||||
$this->hasBillInformation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the search to a specific bunch of categories.
|
* Limit the search to a specific bunch of categories.
|
||||||
*
|
*
|
||||||
@ -793,6 +575,18 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function dumpQuery(): void
|
||||||
|
{
|
||||||
|
|
||||||
|
echo $this->query->toSql();
|
||||||
|
echo '<pre>';
|
||||||
|
print_r($this->query->getBindings());
|
||||||
|
echo '</pre>';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the sum of all journals.
|
* Return the sum of all journals.
|
||||||
*
|
*
|
||||||
@ -830,4 +624,370 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for words in descriptions.
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setSearchWords(array $array): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->query->where(
|
||||||
|
function (EloquentBuilder $q) use ($array) {
|
||||||
|
$q->where(
|
||||||
|
function (EloquentBuilder $q1) use ($array) {
|
||||||
|
foreach ($array as $word) {
|
||||||
|
$keyword = sprintf('%%%s%%', $word);
|
||||||
|
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$q->orWhere(
|
||||||
|
function (EloquentBuilder $q2) use ($array) {
|
||||||
|
foreach ($array as $word) {
|
||||||
|
$keyword = sprintf('%%%s%%', $word);
|
||||||
|
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the result to a specific transaction group.
|
||||||
|
*
|
||||||
|
* @param TransactionGroup $transactionGroup
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setGroup(TransactionGroup $transactionGroup): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->query->where('transaction_groups.id', $transactionGroup->id);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the search to a specific set of bills.
|
||||||
|
*
|
||||||
|
* @param Collection $bills
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setBills(Collection $bills): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->withBillInformation();
|
||||||
|
$this->query->whereIn('transaction_journals.bill_id', $bills->pluck('id')->toArray());
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transactions with a specific amount.
|
||||||
|
*
|
||||||
|
* @param string $amount
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function amountIs(string $amount): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->query->where(
|
||||||
|
function (EloquentBuilder $q) use ($amount) {
|
||||||
|
$q->where('source.amount', app('steam')->negative($amount));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transactions where the amount is less than.
|
||||||
|
*
|
||||||
|
* @param string $amount
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function amountLess(string $amount): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->query->where(
|
||||||
|
function (EloquentBuilder $q) use ($amount) {
|
||||||
|
$q->where('destination.amount', '<', app('steam')->positive($amount));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transactions where the amount is more than.
|
||||||
|
*
|
||||||
|
* @param string $amount
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function amountMore(string $amount): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$this->query->where(
|
||||||
|
function (EloquentBuilder $q) use ($amount) {
|
||||||
|
$q->where('destination.amount', '>', app('steam')->positive($amount));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect transactions before a specific date.
|
||||||
|
*
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setBefore(Carbon $date): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$beforeStr = $date->format('Y-m-d 00:00:00');
|
||||||
|
$this->query->where('transaction_journals.date', '<=', $beforeStr);
|
||||||
|
Log::debug(sprintf('GroupCollector range is now before %s (inclusive)', $beforeStr));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect transactions after a specific date.
|
||||||
|
*
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setAfter(Carbon $date): GroupCollectorInterface
|
||||||
|
{
|
||||||
|
$afterStr = $date->format('Y-m-d 00:00:00');
|
||||||
|
$this->query->where('transaction_journals.date', '>=', $afterStr);
|
||||||
|
Log::debug(sprintf('GroupCollector range is now after %s (inclusive)', $afterStr));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $collection
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function parseArray(Collection $collection): Collection
|
||||||
|
{
|
||||||
|
$groups = [];
|
||||||
|
/** @var TransactionGroup $augmentedGroup */
|
||||||
|
foreach ($collection as $augmentedGroup) {
|
||||||
|
$groupId = $augmentedGroup->transaction_group_id;
|
||||||
|
if (!isset($groups[$groupId])) {
|
||||||
|
// make new array
|
||||||
|
$groupArray = [
|
||||||
|
'id' => $augmentedGroup->transaction_group_id,
|
||||||
|
'user_id' => $augmentedGroup->user_id,
|
||||||
|
'title' => $augmentedGroup->transaction_group_title,
|
||||||
|
'count' => 1,
|
||||||
|
'sums' => [],
|
||||||
|
'transactions' => [],
|
||||||
|
];
|
||||||
|
$journalId = (int)$augmentedGroup->transaction_journal_id;
|
||||||
|
$groupArray['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
|
||||||
|
$groups[$groupId] = $groupArray;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// or parse the rest.
|
||||||
|
$journalId = (int)$augmentedGroup->transaction_journal_id;
|
||||||
|
$groups[$groupId]['count']++;
|
||||||
|
|
||||||
|
if (isset($groups[$groupId]['transactions'][$journalId])) {
|
||||||
|
$groups[$groupId]['transactions'][$journalId] =
|
||||||
|
$this->mergeTags($groups[$groupId]['transactions'][$journalId], $augmentedGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($groups[$groupId]['transactions'][$journalId])) {
|
||||||
|
$groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
$groups = $this->parseSums($groups);
|
||||||
|
|
||||||
|
return new Collection($groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionGroup $augmentedGroup
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function parseAugmentedGroup(TransactionGroup $augmentedGroup): array
|
||||||
|
{
|
||||||
|
$result = $augmentedGroup->toArray();
|
||||||
|
$result['tags'] = [];
|
||||||
|
$result['date'] = new Carbon($result['date']);
|
||||||
|
$result['created_at'] = new Carbon($result['created_at']);
|
||||||
|
$result['updated_at'] = new Carbon($result['updated_at']);
|
||||||
|
$result['reconciled'] = 1 === (int)$result['reconciled'];
|
||||||
|
if (isset($augmentedGroup['tag_id'])) { // assume the other fields are present as well.
|
||||||
|
$tagId = (int)$augmentedGroup['tag_id'];
|
||||||
|
$tagDate = null;
|
||||||
|
try {
|
||||||
|
$tagDate = Carbon::parse($augmentedGroup['tag_date']);
|
||||||
|
} catch (InvalidDateException $e) {
|
||||||
|
Log::debug(sprintf('Could not parse date: %s', $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$result['tags'][$tagId] = [
|
||||||
|
'id' => (int)$result['tag_id'],
|
||||||
|
'name' => $result['tag_name'],
|
||||||
|
'date' => $tagDate,
|
||||||
|
'description' => $result['tag_description'],
|
||||||
|
'latitude' => $result['tag_latitude'],
|
||||||
|
'longitude' => $result['tag_longitude'],
|
||||||
|
'zoom_level' => $result['tag_zoom_level'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $existingJournal
|
||||||
|
* @param TransactionGroup $newGroup
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function mergeTags(array $existingJournal, TransactionGroup $newGroup): array
|
||||||
|
{
|
||||||
|
$newArray = $newGroup->toArray();
|
||||||
|
if (isset($newArray['tag_id'])) { // assume the other fields are present as well.
|
||||||
|
$tagId = (int)$newGroup['tag_id'];
|
||||||
|
|
||||||
|
$tagDate = null;
|
||||||
|
try {
|
||||||
|
$tagDate = Carbon::parse($newArray['tag_date']);
|
||||||
|
} catch (InvalidDateException $e) {
|
||||||
|
Log::debug(sprintf('Could not parse date: %s', $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingJournal['tags'][$tagId] = [
|
||||||
|
'id' => (int)$newArray['tag_id'],
|
||||||
|
'name' => $newArray['tag_name'],
|
||||||
|
'date' => $tagDate,
|
||||||
|
'description' => $newArray['tag_description'],
|
||||||
|
'latitude' => $newArray['tag_latitude'],
|
||||||
|
'longitude' => $newArray['tag_longitude'],
|
||||||
|
'zoom_level' => $newArray['tag_zoom_level'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $existingJournal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $groups
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function parseSums(array $groups): array
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int $groudId
|
||||||
|
* @var array $group
|
||||||
|
*/
|
||||||
|
foreach ($groups as $groudId => $group) {
|
||||||
|
/** @var array $transaction */
|
||||||
|
foreach ($group['transactions'] as $transaction) {
|
||||||
|
$currencyId = (int)$transaction['currency_id'];
|
||||||
|
|
||||||
|
// set default:
|
||||||
|
if (!isset($groups[$groudId]['sums'][$currencyId])) {
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['currency_code'];
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['currency_symbol'];
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['currency_decimal_places'];
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
|
||||||
|
}
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
|
||||||
|
|
||||||
|
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
|
||||||
|
$currencyId = (int)$transaction['foreign_currency_id'];
|
||||||
|
|
||||||
|
// set default:
|
||||||
|
if (!isset($groups[$groudId]['sums'][$currencyId])) {
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['foreign_currency_code'];
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['foreign_currency_symbol'];
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['foreign_currency_decimal_places'];
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
|
||||||
|
}
|
||||||
|
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['foreign_amount']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join table to get tag information.
|
||||||
|
*/
|
||||||
|
private function joinTagTables(): void
|
||||||
|
{
|
||||||
|
if (false === $this->hasJoinedTagTables) {
|
||||||
|
// join some extra tables:
|
||||||
|
$this->hasJoinedTagTables = true;
|
||||||
|
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||||
|
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
|
||||||
|
$this->fields[] = 'tags.id as tag_id';
|
||||||
|
$this->fields[] = 'tags.tag as tag_name';
|
||||||
|
$this->fields[] = 'tags.date as tag_date';
|
||||||
|
$this->fields[] = 'tags.description as tag_description';
|
||||||
|
$this->fields[] = 'tags.latitude as tag_latitude';
|
||||||
|
$this->fields[] = 'tags.longitude as tag_longitude';
|
||||||
|
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the query.
|
||||||
|
*/
|
||||||
|
private function startQuery(): void
|
||||||
|
{
|
||||||
|
app('log')->debug('GroupCollector::startQuery');
|
||||||
|
$this->query = $this->user
|
||||||
|
->transactionGroups()
|
||||||
|
->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
|
||||||
|
// join source transaction.
|
||||||
|
->leftJoin(
|
||||||
|
'transactions as source', function (JoinClause $join) {
|
||||||
|
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
|
||||||
|
->where('source.amount', '<', 0);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// join destination transaction
|
||||||
|
->leftJoin(
|
||||||
|
'transactions as destination', function (JoinClause $join) {
|
||||||
|
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
|
||||||
|
->where('destination.amount', '>', 0);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// left join transaction type.
|
||||||
|
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||||
|
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
|
||||||
|
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
|
||||||
|
->whereNull('transaction_groups.deleted_at')
|
||||||
|
->whereNull('transaction_journals.deleted_at')
|
||||||
|
->whereNull('source.deleted_at')
|
||||||
|
->whereNull('destination.deleted_at')
|
||||||
|
->orderBy('transaction_journals.date', 'DESC')
|
||||||
|
->orderBy('transaction_journals.order', 'ASC')
|
||||||
|
->orderBy('transaction_journals.id', 'DESC')
|
||||||
|
->orderBy('transaction_journals.description', 'DESC')
|
||||||
|
->orderBy('source.amount', 'DESC');
|
||||||
|
}
|
||||||
}
|
}
|
@ -85,6 +85,42 @@ interface GroupCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function setBill(Bill $bill): GroupCollectorInterface;
|
public function setBill(Bill $bill): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the search to a specific set of bills.
|
||||||
|
*
|
||||||
|
* @param Collection $bills
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setBills(Collection $bills): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transactions with a specific amount.
|
||||||
|
*
|
||||||
|
* @param string $amount
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function amountIs(string $amount): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transactions where the amount is less than.
|
||||||
|
*
|
||||||
|
* @param string $amount
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function amountLess(string $amount): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transactions where the amount is more than.
|
||||||
|
*
|
||||||
|
* @param string $amount
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function amountMore(string $amount): GroupCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the search to a specific budget.
|
* Limit the search to a specific budget.
|
||||||
*
|
*
|
||||||
@ -123,7 +159,7 @@ interface GroupCollectorInterface
|
|||||||
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface;
|
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the result to a set of specific journals.
|
* Limit the result to a set of specific transaction journals.
|
||||||
*
|
*
|
||||||
* @param array $journalIds
|
* @param array $journalIds
|
||||||
*
|
*
|
||||||
@ -131,6 +167,24 @@ interface GroupCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function setJournalIds(array $journalIds): GroupCollectorInterface;
|
public function setJournalIds(array $journalIds): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the result to a specific transaction group.
|
||||||
|
*
|
||||||
|
* @param TransactionGroup $transactionGroup
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setGroup(TransactionGroup $transactionGroup): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for words in descriptions.
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setSearchWords(array $array): GroupCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the number of returned entries.
|
* Limit the number of returned entries.
|
||||||
*
|
*
|
||||||
@ -241,6 +295,24 @@ interface GroupCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function setCategories(Collection $categories): GroupCollectorInterface;
|
public function setCategories(Collection $categories): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect transactions before a specific date.
|
||||||
|
*
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setBefore(Carbon $date): GroupCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect transactions after a specific date.
|
||||||
|
*
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return GroupCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setAfter(Carbon $date): GroupCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include bill name + ID.
|
* Include bill name + ID.
|
||||||
*
|
*
|
||||||
|
@ -89,7 +89,7 @@ class ExpenseReportController extends Controller
|
|||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
// return response()->json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||||
@ -97,44 +97,43 @@ class ExpenseReportController extends Controller
|
|||||||
$chartData = [];
|
$chartData = [];
|
||||||
$currentStart = clone $start;
|
$currentStart = clone $start;
|
||||||
$combined = $this->combineAccounts($expense);
|
$combined = $this->combineAccounts($expense);
|
||||||
|
|
||||||
// make "all" set:
|
// make "all" set:
|
||||||
$all = new Collection;
|
$all = new Collection;
|
||||||
foreach ($combined as $name => $combi) {
|
foreach ($combined as $name => $combination) {
|
||||||
$all = $all->merge($combi);
|
$all = $all->merge($combination);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prep chart data:
|
// prep chart data:
|
||||||
/**
|
/**
|
||||||
* @var string $name
|
* @var string $name
|
||||||
* @var Collection $combi
|
* @var Collection $combination
|
||||||
*/
|
*/
|
||||||
foreach ($combined as $name => $combi) {
|
foreach ($combined as $name => $combination) {
|
||||||
// first is always expense account:
|
// first is always expense account:
|
||||||
/** @var Account $exp */
|
/** @var Account $exp */
|
||||||
$exp = $combi->first();
|
$exp = $combination->first();
|
||||||
$chartData[$exp->id . '-in'] = [
|
$chartData[$exp->id . '-in'] = [
|
||||||
'label' => $name . ' (' . strtolower((string)trans('firefly.income')) . ')',
|
'label' => sprintf('%s (%s)', $name, strtolower((string)trans('firefly.income'))),
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
'yAxisID' => 'y-axis-0',
|
'yAxisID' => 'y-axis-0',
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
$chartData[$exp->id . '-out'] = [
|
$chartData[$exp->id . '-out'] = [
|
||||||
'label' => $name . ' (' . strtolower((string)trans('firefly.expenses')) . ')',
|
'label' => sprintf('%s (%s)', $name, strtolower((string)trans('firefly.expenses'))),
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
'yAxisID' => 'y-axis-0',
|
'yAxisID' => 'y-axis-0',
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
// total in, total out:
|
// total in, total out:
|
||||||
$chartData[$exp->id . '-total-in'] = [
|
$chartData[$exp->id . '-total-in'] = [
|
||||||
'label' => $name . ' (' . strtolower((string)trans('firefly.sum_of_income')) . ')',
|
'label' => sprintf('%s (%s)', $name, strtolower((string)trans('firefly.sum_of_income'))),
|
||||||
'type' => 'line',
|
'type' => 'line',
|
||||||
'fill' => false,
|
'fill' => false,
|
||||||
'yAxisID' => 'y-axis-1',
|
'yAxisID' => 'y-axis-1',
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
$chartData[$exp->id . '-total-out'] = [
|
$chartData[$exp->id . '-total-out'] = [
|
||||||
'label' => $name . ' (' . strtolower((string)trans('firefly.sum_of_expenses')) . ')',
|
'label' => sprintf('%s (%s)', $name, strtolower((string)trans('firefly.sum_of_expenses'))),
|
||||||
'type' => 'line',
|
'type' => 'line',
|
||||||
'fill' => false,
|
'fill' => false,
|
||||||
'yAxisID' => 'y-axis-1',
|
'yAxisID' => 'y-axis-1',
|
||||||
@ -154,15 +153,15 @@ class ExpenseReportController extends Controller
|
|||||||
$income = $this->groupByName($this->getIncomeForOpposing($accounts, $all, $currentStart, $currentEnd));
|
$income = $this->groupByName($this->getIncomeForOpposing($accounts, $all, $currentStart, $currentEnd));
|
||||||
$label = $currentStart->formatLocalized($format);
|
$label = $currentStart->formatLocalized($format);
|
||||||
|
|
||||||
foreach ($combined as $name => $combi) {
|
foreach ($combined as $name => $combination) {
|
||||||
// first is always expense account:
|
// first is always expense account:
|
||||||
/** @var Account $exp */
|
/** @var Account $exp */
|
||||||
$exp = $combi->first();
|
$exp = $combination->first();
|
||||||
$labelIn = $exp->id . '-in';
|
$labelIn = $exp->id . '-in';
|
||||||
$labelOut = $exp->id . '-out';
|
$labelOut = $exp->id . '-out';
|
||||||
$labelSumIn = $exp->id . '-total-in';
|
$labelSumIn = $exp->id . '-total-in';
|
||||||
$labelSumOut = $exp->id . '-total-out';
|
$labelSumOut = $exp->id . '-total-out';
|
||||||
$currentIncome = $income[$name] ?? '0';
|
$currentIncome = bcmul($income[$name] ?? '0', '-1');
|
||||||
$currentExpense = $expenses[$name] ?? '0';
|
$currentExpense = $expenses[$name] ?? '0';
|
||||||
|
|
||||||
// add to sum:
|
// add to sum:
|
||||||
@ -180,6 +179,7 @@ class ExpenseReportController extends Controller
|
|||||||
/** @var Carbon $currentStart */
|
/** @var Carbon $currentStart */
|
||||||
$currentStart = clone $currentEnd;
|
$currentStart = clone $currentEnd;
|
||||||
$currentStart->addDay();
|
$currentStart->addDay();
|
||||||
|
$currentStart->startOfDay();
|
||||||
}
|
}
|
||||||
// remove all empty entries to prevent cluttering:
|
// remove all empty entries to prevent cluttering:
|
||||||
$newSet = [];
|
$newSet = [];
|
||||||
|
@ -32,6 +32,7 @@ use Illuminate\Http\Response as LaravelResponse;
|
|||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO make sure all import methods work.
|
||||||
*
|
*
|
||||||
* Class IndexController
|
* Class IndexController
|
||||||
*/
|
*/
|
||||||
|
@ -96,6 +96,7 @@ class ReconcileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function overview(Request $request, Account $account, Carbon $start, Carbon $end): JsonResponse
|
public function overview(Request $request, Account $account, Carbon $start, Carbon $end): JsonResponse
|
||||||
{
|
{
|
||||||
|
|
||||||
if (AccountType::ASSET !== $account->accountType->type) {
|
if (AccountType::ASSET !== $account->accountType->type) {
|
||||||
throw new FireflyException(sprintf('Account %s is not an asset account.', $account->name));
|
throw new FireflyException(sprintf('Account %s is not an asset account.', $account->name));
|
||||||
}
|
}
|
||||||
@ -107,6 +108,7 @@ class ReconcileController extends Controller
|
|||||||
$clearedAmount = '0';
|
$clearedAmount = '0';
|
||||||
$route = route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]);
|
$route = route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]);
|
||||||
// get sum of transaction amounts:
|
// get sum of transaction amounts:
|
||||||
|
// TODO these methods no longer exist:
|
||||||
$transactions = $this->repository->getTransactionsById($transactionIds);
|
$transactions = $this->repository->getTransactionsById($transactionIds);
|
||||||
$cleared = $this->repository->getTransactionsById($clearedIds);
|
$cleared = $this->repository->getTransactionsById($clearedIds);
|
||||||
$countCleared = 0;
|
$countCleared = 0;
|
||||||
|
@ -70,7 +70,7 @@ class CategoryController extends Controller
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ class CategoryController extends Controller
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
@ -167,7 +167,7 @@ class CategoryController extends Controller
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
|
@ -23,9 +23,8 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Http\Controllers\Report;
|
namespace FireflyIII\Http\Controllers\Report;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
@ -110,7 +109,7 @@ class ExpenseController extends Controller
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::budget: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::budget: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
@ -175,7 +174,7 @@ class ExpenseController extends Controller
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
@ -227,7 +226,7 @@ class ExpenseController extends Controller
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
@ -265,22 +264,23 @@ class ExpenseController extends Controller
|
|||||||
$all = $all->merge($combi);
|
$all = $all->merge($combi);
|
||||||
}
|
}
|
||||||
// get all expenses in period:
|
// get all expenses in period:
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts);
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts);
|
||||||
$collector->setOpposingAccounts($all);
|
$collector->setAccounts($all);
|
||||||
$set = $collector->getTransactions();
|
$set = $collector->getExtractedJournals();
|
||||||
$sorted = $set->sortBy(
|
|
||||||
function (Transaction $transaction) {
|
usort($set, function ($a, $b) {
|
||||||
return (float)$transaction->transaction_amount;
|
return $a['amount'] <=> $b['amount'];
|
||||||
}
|
});
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
$result = view('reports.partials.top-transactions', compact('sorted'))->render();
|
$result = view('reports.partials.top-transactions', compact('sorted'))->render();
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::topExpense: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::topExpense: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
@ -316,22 +316,24 @@ class ExpenseController extends Controller
|
|||||||
$all = $all->merge($combi);
|
$all = $all->merge($combi);
|
||||||
}
|
}
|
||||||
// get all expenses in period:
|
// get all expenses in period:
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($accounts);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setOpposingAccounts($all);
|
|
||||||
$set = $collector->getTransactions();
|
$total = $accounts->merge($all);
|
||||||
$sorted = $set->sortByDesc(
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total);
|
||||||
function (Transaction $transaction) {
|
$journals = $collector->getExtractedJournals();
|
||||||
return (float)$transaction->transaction_amount;
|
|
||||||
}
|
usort($journals, function ($a, $b) {
|
||||||
);
|
return $a['amount'] <=> $b['amount'];
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = view('reports.partials.top-transactions', compact('sorted'))->render();
|
$result = view('reports.partials.top-transactions', compact('sorted'))->render();
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error(sprintf('Could not render category::topIncome: %s', $e->getMessage()));
|
Log::error(sprintf('Could not render category::topIncome: %s', $e->getMessage()));
|
||||||
$result = 'An error prevented Firefly III from rendering. Apologies.';
|
$result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage());
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
|
@ -170,6 +170,7 @@ class RuleGroupController extends Controller
|
|||||||
* @param RuleGroup $ruleGroup
|
* @param RuleGroup $ruleGroup
|
||||||
*
|
*
|
||||||
* @return RedirectResponse
|
* @return RedirectResponse
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function execute(SelectTransactionsRequest $request, AccountRepositoryInterface $repository, RuleGroup $ruleGroup): RedirectResponse
|
public function execute(SelectTransactionsRequest $request, AccountRepositoryInterface $repository, RuleGroup $ruleGroup): RedirectResponse
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\TransactionViewFilter;
|
use FireflyIII\Helpers\Filter\TransactionViewFilter;
|
||||||
use FireflyIII\Helpers\Filter\TransferFilter;
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
@ -126,10 +126,13 @@ class MassController extends Controller
|
|||||||
* @param Collection $journals
|
* @param Collection $journals
|
||||||
*
|
*
|
||||||
* @return IlluminateView
|
* @return IlluminateView
|
||||||
|
*
|
||||||
|
* TODO rebuild this feature.
|
||||||
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
public function edit(Collection $journals): IlluminateView
|
public function edit(Collection $journals): IlluminateView
|
||||||
{
|
{
|
||||||
throw new FireflyException('Needs refactor');
|
throw new FireflyException(sprintf('The mass-editor is not available in v%s of Firefly III. Sorry about that. It will be back soon.', config('firefly.version')));
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$subTitle = (string)trans('firefly.mass_edit_journals');
|
$subTitle = (string)trans('firefly.mass_edit_journals');
|
||||||
@ -148,8 +151,8 @@ class MassController extends Controller
|
|||||||
$transformer = app(TransactionTransformer::class);
|
$transformer = app(TransactionTransformer::class);
|
||||||
$transformer->setParameters(new ParameterBag);
|
$transformer->setParameters(new ParameterBag);
|
||||||
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setUser($user);
|
$collector->setUser($user);
|
||||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
||||||
$collector->setJournals($journals);
|
$collector->setJournals($journals);
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* SplitController.php
|
|
||||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
|
||||||
*
|
|
||||||
* This file is part of Firefly III.
|
|
||||||
*
|
|
||||||
* Firefly III is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Firefly III 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers\Transaction;
|
|
||||||
|
|
||||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
|
||||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
|
||||||
use FireflyIII\Http\Requests\SplitJournalFormRequest;
|
|
||||||
use FireflyIII\Models\TransactionJournal;
|
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
|
||||||
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
|
||||||
use FireflyIII\Support\Http\Controllers\RequestInformation;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SplitController.
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
|
||||||
*/
|
|
||||||
class SplitController extends Controller
|
|
||||||
{
|
|
||||||
use ModelInformation, RequestInformation;
|
|
||||||
|
|
||||||
/** @var AttachmentHelperInterface Attachment helper */
|
|
||||||
private $attachments;
|
|
||||||
|
|
||||||
/** @var BudgetRepositoryInterface The budget repository */
|
|
||||||
private $budgets;
|
|
||||||
|
|
||||||
/** @var CurrencyRepositoryInterface The currency repository */
|
|
||||||
private $currencies;
|
|
||||||
/** @var JournalRepositoryInterface Journals and transactions overview */
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SplitController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
throw new FireflyException('Do not use me.');
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
// some useful repositories:
|
|
||||||
$this->middleware(
|
|
||||||
function ($request, $next) {
|
|
||||||
$this->budgets = app(BudgetRepositoryInterface::class);
|
|
||||||
$this->attachments = app(AttachmentHelperInterface::class);
|
|
||||||
$this->currencies = app(CurrencyRepositoryInterface::class);
|
|
||||||
$this->repository = app(JournalRepositoryInterface::class);
|
|
||||||
app('view')->share('mainTitleIcon', 'fa-share-alt');
|
|
||||||
app('view')->share('title', (string)trans('firefly.split-transactions'));
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit a split.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
|
||||||
public function edit(Request $request, TransactionJournal $journal)
|
|
||||||
{
|
|
||||||
throw new FireflyException('Needs refactoring');
|
|
||||||
if ($this->isOpeningBalance($journal)) {
|
|
||||||
return $this->redirectToAccount($journal); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
// basic fields:
|
|
||||||
$uploadSize = min(app('steam')->phpBytes(ini_get('upload_max_filesize')), app('steam')->phpBytes(ini_get('post_max_size')));
|
|
||||||
$subTitle = (string)trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
|
||||||
$subTitleIcon = 'fa-pencil';
|
|
||||||
|
|
||||||
// lists and collections
|
|
||||||
$currencies = $this->currencies->get();
|
|
||||||
$budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
|
||||||
|
|
||||||
// other fields
|
|
||||||
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
|
||||||
$preFilled = $this->arrayFromJournal($request, $journal);
|
|
||||||
|
|
||||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
|
||||||
if (true !== session('transactions.edit-split.fromUpdate')) {
|
|
||||||
$this->rememberPreviousUri('transactions.edit-split.uri');
|
|
||||||
}
|
|
||||||
session()->forget('transactions.edit-split.fromUpdate');
|
|
||||||
|
|
||||||
return view(
|
|
||||||
'transactions.split.edit', compact(
|
|
||||||
'subTitleIcon', 'currencies', 'optionalFields', 'preFilled', 'subTitle', 'uploadSize', 'budgets',
|
|
||||||
'journal'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store new split journal.
|
|
||||||
*
|
|
||||||
* @param SplitJournalFormRequest $request
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
*
|
|
||||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
public function update(SplitJournalFormRequest $request, TransactionJournal $journal)
|
|
||||||
{
|
|
||||||
throw new FireflyException('Needs refactoring.');
|
|
||||||
if ($this->isOpeningBalance($journal)) {
|
|
||||||
return $this->redirectToAccount($journal); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
$data = $request->getAll();
|
|
||||||
|
|
||||||
// keep current bill:
|
|
||||||
$data['bill_id'] = $journal->bill_id;
|
|
||||||
$journal = $this->repository->update($journal, $data);
|
|
||||||
|
|
||||||
/** @var array $files */
|
|
||||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
|
||||||
// save attachments:
|
|
||||||
$this->attachments->saveAttachmentsForModel($journal, $files);
|
|
||||||
event(new UpdatedTransactionGroup($group));
|
|
||||||
|
|
||||||
// flash messages
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
|
||||||
session()->flash('info', $this->attachments->getMessages()->get('attachments'));
|
|
||||||
}
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
||||||
$type = strtolower($this->repository->getTransactionType($journal));
|
|
||||||
session()->flash('success', (string)trans('firefly.updated_' . $type, ['description' => $journal->description]));
|
|
||||||
app('preferences')->mark();
|
|
||||||
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
if (1 === (int)$request->get('return_to_edit')) {
|
|
||||||
// set value so edit routine will not overwrite URL:
|
|
||||||
session()->put('transactions.edit-split.fromUpdate', true);
|
|
||||||
|
|
||||||
return redirect(route('transactions.split.edit', [$journal->id]))->withInput(['return_to_edit' => 1]);
|
|
||||||
}
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
||||||
// redirect to previous URL.
|
|
||||||
return redirect($this->getPreviousUri('transactions.edit-split.uri'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -26,20 +26,19 @@ namespace FireflyIII\Import\Storage;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use DB;
|
use DB;
|
||||||
|
use Exception;
|
||||||
use FireflyIII\Events\RequestedReportOnJournals;
|
use FireflyIII\Events\RequestedReportOnJournals;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|
||||||
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
|
||||||
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
|
||||||
use FireflyIII\Models\ImportJob;
|
use FireflyIII\Models\ImportJob;
|
||||||
use FireflyIII\Models\Rule;
|
use FireflyIII\Models\Rule;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||||
use FireflyIII\TransactionRules\Processor;
|
use FireflyIII\TransactionRules\Processor;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -64,8 +63,10 @@ class ImportArrayStorage
|
|||||||
private $journalRepos;
|
private $journalRepos;
|
||||||
/** @var ImportJobRepositoryInterface Import job repository */
|
/** @var ImportJobRepositoryInterface Import job repository */
|
||||||
private $repository;
|
private $repository;
|
||||||
/** @var Collection The transfers the user already has. */
|
/** @var array The transfers the user already has. */
|
||||||
private $transfers;
|
private $transfers;
|
||||||
|
/** @var TransactionGroupRepositoryInterface */
|
||||||
|
private $groupRepos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set job, count transfers in the array and create the repository.
|
* Set job, count transfers in the array and create the repository.
|
||||||
@ -83,9 +84,59 @@ class ImportArrayStorage
|
|||||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||||
$this->journalRepos->setUser($importJob->user);
|
$this->journalRepos->setUser($importJob->user);
|
||||||
|
|
||||||
|
$this->groupRepos = app(TransactionGroupRepositoryInterface::class);
|
||||||
|
$this->groupRepos->setUser($importJob->user);
|
||||||
|
|
||||||
Log::debug('Constructed ImportArrayStorage()');
|
Log::debug('Constructed ImportArrayStorage()');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of transfers in the array. If this is zero, don't bother checking for double transfers.
|
||||||
|
*/
|
||||||
|
private function countTransfers(): void
|
||||||
|
{
|
||||||
|
Log::debug('Now in countTransfers()');
|
||||||
|
/** @var array $array */
|
||||||
|
$array = $this->repository->getTransactions($this->importJob);
|
||||||
|
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
foreach ($array as $index => $group) {
|
||||||
|
foreach ($group['transactions'] as $transaction) {
|
||||||
|
if (strtolower(TransactionType::TRANSFER) === strtolower($transaction['type'])) {
|
||||||
|
$count++;
|
||||||
|
Log::debug(sprintf('Row #%d is a transfer, increase count to %d', $index + 1, $count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Count of transfers in import array is %d.', $count));
|
||||||
|
if ($count > 0) {
|
||||||
|
$this->checkForTransfers = true;
|
||||||
|
Log::debug('Will check for duplicate transfers.');
|
||||||
|
// get users transfers. Needed for comparison.
|
||||||
|
$this->getTransfers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the users transfers, so they can be compared to whatever the user is trying to import.
|
||||||
|
*/
|
||||||
|
private function getTransfers(): void
|
||||||
|
{
|
||||||
|
Log::debug('Now in getTransfers()');
|
||||||
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
|
$collector->setUser($this->importJob->user);
|
||||||
|
$collector
|
||||||
|
->setTypes([TransactionType::TRANSFER])->setLimit(10000)->setPage(1)
|
||||||
|
->withAccountInformation();
|
||||||
|
$this->transfers = $collector->getExtractedJournals();
|
||||||
|
Log::debug(sprintf('Count of getTransfers() is %d', count($this->transfers)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually does the storing. Does three things.
|
* Actually does the storing. Does three things.
|
||||||
* - Store journals
|
* - Store journals
|
||||||
@ -99,7 +150,7 @@ class ImportArrayStorage
|
|||||||
{
|
{
|
||||||
// store transactions
|
// store transactions
|
||||||
$this->setStatus('storing_data');
|
$this->setStatus('storing_data');
|
||||||
$collection = $this->storeArray();
|
$collection = $this->storeGroupArray();
|
||||||
$this->setStatus('stored_data');
|
$this->setStatus('stored_data');
|
||||||
|
|
||||||
// link tag:
|
// link tag:
|
||||||
@ -124,70 +175,104 @@ class ImportArrayStorage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the users rules to the created journals.
|
* Shorthand method to quickly set job status
|
||||||
*
|
|
||||||
* @param Collection $collection
|
|
||||||
*
|
*
|
||||||
|
* @param string $status
|
||||||
*/
|
*/
|
||||||
private function applyRules(Collection $collection): void
|
private function setStatus(string $status): void
|
||||||
{
|
{
|
||||||
$rules = $this->getRules();
|
$this->repository->setStatus($this->importJob, $status);
|
||||||
if ($rules->count() > 0) {
|
|
||||||
foreach ($collection as $journal) {
|
|
||||||
$rules->each(
|
|
||||||
function (Rule $rule) use ($journal) {
|
|
||||||
Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id));
|
|
||||||
/** @var Processor $processor */
|
|
||||||
$processor = app(Processor::class);
|
|
||||||
$processor->make($rule);
|
|
||||||
$processor->handleTransactionJournal($journal);
|
|
||||||
$journal->refresh();
|
|
||||||
if ($rule->stop_processing) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of transfers in the array. If this is zero, don't bother checking for double transfers.
|
* Store array as journals.
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
* @throws FireflyException
|
||||||
|
*
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||||
|
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||||
*/
|
*/
|
||||||
private function countTransfers(): void
|
private function storeGroupArray(): Collection
|
||||||
{
|
{
|
||||||
Log::debug('Now in countTransfers()');
|
|
||||||
/** @var array $array */
|
/** @var array $array */
|
||||||
$array = $this->repository->getTransactions($this->importJob);
|
$array = $this->repository->getTransactions($this->importJob);
|
||||||
|
$count = count($array);
|
||||||
|
|
||||||
|
Log::notice(sprintf('Will now store the groups. Count of groups is %d.', $count));
|
||||||
|
Log::notice('Going to store...');
|
||||||
|
|
||||||
$count = 0;
|
$collection = new Collection;
|
||||||
foreach ($array as $index => $transaction) {
|
foreach ($array as $index => $group) {
|
||||||
if (strtolower(TransactionType::TRANSFER) === strtolower($transaction['type'])) {
|
Log::debug(sprintf('Now store #%d', ($index + 1)));
|
||||||
$count++;
|
$result = $this->storeGroup($index, $group);
|
||||||
Log::debug(sprintf('Row #%d is a transfer, increase count to %d', $index + 1, $count));
|
if (null !== $result) {
|
||||||
|
$collection->push($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log::debug(sprintf('Count of transfers in import array is %d.', $count));
|
Log::notice(sprintf('Done storing. Firefly III has stored %d transactions.', $collection->count()));
|
||||||
if ($count > 0) {
|
|
||||||
$this->checkForTransfers = true;
|
return $collection;
|
||||||
Log::debug('Will check for duplicate transfers.');
|
|
||||||
// get users transfers. Needed for comparison.
|
|
||||||
$this->getTransfers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $index
|
* @param int $index
|
||||||
* @param array $transaction
|
* @param array $group
|
||||||
|
* @return TransactionGroup|null
|
||||||
|
*/
|
||||||
|
private function storeGroup(int $index, array $group): ?TransactionGroup
|
||||||
|
{
|
||||||
|
|
||||||
|
// do duplicate detection!
|
||||||
|
if ($this->duplicateDetected($index, $group)) {
|
||||||
|
Log::warning(sprintf('Row #%d seems to be a imported already and will be ignored.', $index));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('Going to store entry #%d', $index + 1));
|
||||||
|
|
||||||
|
// do some basic error catching.
|
||||||
|
foreach ($group['transactions'] as $index => $transaction) {
|
||||||
|
$group['transactions'][$index]['date'] = Carbon::parse($transaction['date'], config('app.timezone'));
|
||||||
|
$group['transactions'][$index]['description'] = '' === $transaction['description'] ? '(empty description)' : $transaction['description'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the group
|
||||||
|
try {
|
||||||
|
$newGroup = $this->groupRepos->store($group);
|
||||||
|
} catch (FireflyException $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
Log::error($e->getTraceAsString());
|
||||||
|
$this->repository->addErrorMessage($this->importJob, sprintf('Row #%d could not be imported. %s', $index, $e->getMessage()));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Stored as group #%d', $newGroup->id));
|
||||||
|
|
||||||
|
// add to collection of transfers, if necessary:
|
||||||
|
if ('transfer' === strtolower($group['transactions'][0]['type'])) {
|
||||||
|
$journals = $this->getTransactionFromJournal($newGroup);
|
||||||
|
Log::debug('We just stored a transfer, so add the journal to the list of transfers.');
|
||||||
|
foreach ($journals as $newJournal) {
|
||||||
|
$this->transfers[] = $newJournal;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('List length is now %d', count($this->transfers)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $index
|
||||||
|
* @param array $group
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
*/
|
||||||
private function duplicateDetected(int $index, array $transaction): bool
|
private function duplicateDetected(int $index, array $group): bool
|
||||||
{
|
{
|
||||||
|
$transactions = $group['transactions'] ?? [];
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
$hash = $this->getHash($transaction);
|
$hash = $this->getHash($transaction);
|
||||||
$existingId = $this->hashExists($hash);
|
$existingId = $this->hashExists($hash);
|
||||||
if (null !== $existingId) {
|
if (null !== $existingId) {
|
||||||
@ -206,6 +291,7 @@ class ImportArrayStorage
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -215,7 +301,6 @@ class ImportArrayStorage
|
|||||||
*
|
*
|
||||||
* @param array $transaction
|
* @param array $transaction
|
||||||
*
|
*
|
||||||
* @throws FireflyException
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function getHash(array $transaction): string
|
private function getHash(array $transaction): string
|
||||||
@ -226,7 +311,12 @@ class ImportArrayStorage
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
/** @noinspection ForgottenDebugOutputInspection */
|
/** @noinspection ForgottenDebugOutputInspection */
|
||||||
Log::error('Could not encode import array.', $transaction);
|
Log::error('Could not encode import array.', $transaction);
|
||||||
throw new FireflyException('Could not encode import array. Please see the logs.');
|
try {
|
||||||
|
$json = random_int(1, 10000);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// seriously?
|
||||||
|
Log::error(sprintf('random_int() just failed. I want a medal: %s', $e->getMessage()));
|
||||||
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
$hash = hash('sha256', $json);
|
$hash = hash('sha256', $json);
|
||||||
@ -235,72 +325,6 @@ class ImportArrayStorage
|
|||||||
return $hash;
|
return $hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the users rules.
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
private function getRules(): Collection
|
|
||||||
{
|
|
||||||
/** @var RuleRepositoryInterface $repository */
|
|
||||||
$repository = app(RuleRepositoryInterface::class);
|
|
||||||
$repository->setUser($this->importJob->user);
|
|
||||||
$set = $repository->getForImport();
|
|
||||||
|
|
||||||
Log::debug(sprintf('Found %d user rules.', $set->count()));
|
|
||||||
|
|
||||||
return $set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $journal
|
|
||||||
*
|
|
||||||
* @return Transaction
|
|
||||||
*/
|
|
||||||
private function getTransactionFromJournal($journal): Transaction
|
|
||||||
{
|
|
||||||
// collect transactions using the journal collector
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setUser($this->importJob->user);
|
|
||||||
$collector->withOpposingAccount();
|
|
||||||
// filter on specific journals.
|
|
||||||
$collector->setJournals(new Collection([$journal]));
|
|
||||||
|
|
||||||
// add filter to remove transactions:
|
|
||||||
$transactionType = $journal->transactionType->type;
|
|
||||||
if ($transactionType === TransactionType::WITHDRAWAL) {
|
|
||||||
$collector->addFilter(PositiveAmountFilter::class);
|
|
||||||
}
|
|
||||||
if (!($transactionType === TransactionType::WITHDRAWAL)) {
|
|
||||||
$collector->addFilter(NegativeAmountFilter::class);
|
|
||||||
}
|
|
||||||
/** @var Transaction $result */
|
|
||||||
$result = $collector->getTransactions()->first();
|
|
||||||
Log::debug(sprintf('Return transaction #%d with journal id #%d based on ID #%d', $result->id, $result->journal_id, $journal->id));
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the users transfers, so they can be compared to whatever the user is trying to import.
|
|
||||||
*/
|
|
||||||
private function getTransfers(): void
|
|
||||||
{
|
|
||||||
Log::debug('Now in getTransfers()');
|
|
||||||
app('preferences')->mark();
|
|
||||||
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setUser($this->importJob->user);
|
|
||||||
$collector->setAllAssetAccounts()
|
|
||||||
->ignoreCache()
|
|
||||||
->setTypes([TransactionType::TRANSFER])
|
|
||||||
->withOpposingAccount();
|
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
$this->transfers = $collector->getTransactions();
|
|
||||||
Log::debug(sprintf('Count of getTransfers() is %d', $this->transfers->count()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the hash exists for the array the user wants to import.
|
* Check if the hash exists for the array the user wants to import.
|
||||||
*
|
*
|
||||||
@ -321,6 +345,188 @@ class ImportArrayStorage
|
|||||||
return (int)$entry->transaction_journal_id;
|
return (int)$entry->transaction_journal_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log about a duplicate object (double hash).
|
||||||
|
*
|
||||||
|
* @param array $transaction
|
||||||
|
* @param int $existingId
|
||||||
|
*/
|
||||||
|
private function logDuplicateObject(array $transaction, int $existingId): void
|
||||||
|
{
|
||||||
|
Log::info(
|
||||||
|
'Transaction is a duplicate, and will not be imported (the hash exists).',
|
||||||
|
[
|
||||||
|
'existing' => $existingId,
|
||||||
|
'description' => $transaction['description'] ?? '',
|
||||||
|
'amount' => $transaction['transactions'][0]['amount'] ?? 0,
|
||||||
|
'date' => $transaction['date'] ?? '',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a transfer exists.
|
||||||
|
*
|
||||||
|
* @param array $transaction
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||||
|
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||||
|
* @SuppressWarnings(PHPMD.NPathComplexity)
|
||||||
|
*/
|
||||||
|
private function transferExists(array $transaction): bool
|
||||||
|
{
|
||||||
|
Log::debug('Check if transaction is a double transfer.');
|
||||||
|
|
||||||
|
// how many hits do we need?
|
||||||
|
Log::debug(sprintf('System has %d existing transfers', count($this->transfers)));
|
||||||
|
// loop over each split:
|
||||||
|
|
||||||
|
// check if is a transfer
|
||||||
|
if (strtolower(TransactionType::TRANSFER) !== strtolower($transaction['type'])) {
|
||||||
|
Log::debug(sprintf('Is a %s, not a transfer so no.', $transaction['type']));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log::debug(sprintf('Required hits for transfer comparison is %d', self::REQUIRED_HITS));
|
||||||
|
|
||||||
|
// get the amount:
|
||||||
|
/** @noinspection UnnecessaryCastingInspection */
|
||||||
|
$amount = (string)($transaction['amount'] ?? '0');
|
||||||
|
if (bccomp($amount, '0') === -1) {
|
||||||
|
$amount = bcmul($amount, '-1'); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the description:
|
||||||
|
$description = '' === (string)$transaction['description'] ? $transaction['description'] : $transaction['description'];
|
||||||
|
|
||||||
|
// get the source and destination ID's:
|
||||||
|
$transactionSourceIDs = [(int)$transaction['source_id'], (int)$transaction['destination_id']];
|
||||||
|
sort($transactionSourceIDs);
|
||||||
|
|
||||||
|
// get the source and destination names:
|
||||||
|
$transactionSourceNames = [(string)$transaction['source_name'], (string)$transaction['destination_name']];
|
||||||
|
sort($transactionSourceNames);
|
||||||
|
|
||||||
|
// then loop all transfers:
|
||||||
|
/** @var array $transfer */
|
||||||
|
foreach ($this->transfers as $transfer) {
|
||||||
|
// number of hits for this split-transfer combination:
|
||||||
|
$hits = 0;
|
||||||
|
Log::debug(sprintf('Now looking at transaction journal #%d', $transfer['transaction_journal_id']));
|
||||||
|
// compare amount:
|
||||||
|
$originalAmount = app('steam')->positive($transfer['amount']);
|
||||||
|
Log::debug(sprintf('Amount %s compared to %s', $amount, $originalAmount));
|
||||||
|
if (0 !== bccomp($amount, $originalAmount)) {
|
||||||
|
Log::debug('Amount is not a match, continue with next transfer.');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++$hits;
|
||||||
|
Log::debug(sprintf('Comparison is a hit! (%s)', $hits));
|
||||||
|
|
||||||
|
// compare description:
|
||||||
|
$comparison = '(empty description)' === $transfer['description'] ? '' : $transfer['description'];
|
||||||
|
Log::debug(sprintf('Comparing "%s" to "%s" (original: "%s")', $description, $transfer['description'], $comparison));
|
||||||
|
if ($description !== $comparison) {
|
||||||
|
Log::debug('Description is not a match, continue with next transfer.');
|
||||||
|
continue; // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
++$hits;
|
||||||
|
Log::debug(sprintf('Comparison is a hit! (%s)', $hits));
|
||||||
|
|
||||||
|
// compare date:
|
||||||
|
$transferDate = $transfer['date']->format('Y-m-d H:i:s');
|
||||||
|
Log::debug(sprintf('Comparing dates "%s" to "%s"', $transaction['date'], $transferDate));
|
||||||
|
if ($transaction['date'] !== $transferDate) {
|
||||||
|
Log::debug('Date is not a match, continue with next transfer.');
|
||||||
|
continue; // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
++$hits;
|
||||||
|
Log::debug(sprintf('Comparison is a hit! (%s)', $hits));
|
||||||
|
|
||||||
|
// compare source and destination id's
|
||||||
|
$transferSourceIDs = [(int)$transfer['source_account_id'], (int)$transfer['destination_account_id']];
|
||||||
|
sort($transferSourceIDs);
|
||||||
|
/** @noinspection DisconnectedForeachInstructionInspection */
|
||||||
|
Log::debug('Comparing current transaction source+dest IDs', $transactionSourceIDs);
|
||||||
|
Log::debug('.. with current transfer source+dest IDs', $transferSourceIDs);
|
||||||
|
if ($transactionSourceIDs === $transferSourceIDs) {
|
||||||
|
++$hits;
|
||||||
|
Log::debug(sprintf('Source IDs are the same! (%d)', $hits));
|
||||||
|
}
|
||||||
|
if ($transactionSourceIDs !== $transactionSourceIDs) {
|
||||||
|
Log::debug('Source IDs are not the same.');
|
||||||
|
}
|
||||||
|
unset($transferSourceIDs);
|
||||||
|
|
||||||
|
// compare source and destination names
|
||||||
|
$transferSource = [(string)($transfer['source_account_name'] ?? ''), (string)($transfer['destination_account_name'] ?? '')];
|
||||||
|
sort($transferSource);
|
||||||
|
/** @noinspection DisconnectedForeachInstructionInspection */
|
||||||
|
Log::debug('Comparing current transaction source+dest names', $transactionSourceNames);
|
||||||
|
Log::debug('.. with current transfer source+dest names', $transferSource);
|
||||||
|
if ($transactionSourceNames === $transferSource) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
++$hits;
|
||||||
|
Log::debug(sprintf('Source names are the same! (%d)', $hits));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
if ($transactionSourceNames !== $transferSource) {
|
||||||
|
Log::debug('Source names are not the same.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('Number of hits is %d', $hits));
|
||||||
|
if ($hits >= self::REQUIRED_HITS) {
|
||||||
|
Log::debug(sprintf('Is more than %d, return true.', self::REQUIRED_HITS));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log::debug('Is not an existing transfer, return false.');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log about a duplicate transfer.
|
||||||
|
*
|
||||||
|
* @param array $transaction
|
||||||
|
*/
|
||||||
|
private function logDuplicateTransfer(array $transaction): void
|
||||||
|
{
|
||||||
|
Log::info(
|
||||||
|
'Transaction is a duplicate transfer, and will not be imported (such a transfer exists already).',
|
||||||
|
[
|
||||||
|
'description' => $transaction['description'] ?? '',
|
||||||
|
'amount' => $transaction['transactions'][0]['amount'] ?? 0,
|
||||||
|
'date' => $transaction['date'] ?? '',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionGroup $transactionGroup
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getTransactionFromJournal(TransactionGroup $transactionGroup): array
|
||||||
|
{
|
||||||
|
// collect transactions using the journal collector
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
|
$collector->setUser($this->importJob->user);
|
||||||
|
$collector->setGroup($transactionGroup);
|
||||||
|
|
||||||
|
$result = $collector->getExtractedJournals();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link all imported journals to a tag.
|
* Link all imported journals to a tag.
|
||||||
*
|
*
|
||||||
@ -365,248 +571,50 @@ class ImportArrayStorage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log about a duplicate object (double hash).
|
* Applies the users rules to the created journals.
|
||||||
|
*
|
||||||
|
* @param Collection $collection
|
||||||
*
|
*
|
||||||
* @param array $transaction
|
|
||||||
* @param int $existingId
|
|
||||||
*/
|
*/
|
||||||
private function logDuplicateObject(array $transaction, int $existingId): void
|
private function applyRules(Collection $collection): void
|
||||||
{
|
{
|
||||||
Log::info(
|
$rules = $this->getRules();
|
||||||
'Transaction is a duplicate, and will not be imported (the hash exists).',
|
if ($rules->count() > 0) {
|
||||||
[
|
foreach ($collection as $journal) {
|
||||||
'existing' => $existingId,
|
$rules->each(
|
||||||
'description' => $transaction['description'] ?? '',
|
function (Rule $rule) use ($journal) {
|
||||||
'amount' => $transaction['transactions'][0]['amount'] ?? 0,
|
Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id));
|
||||||
'date' => $transaction['date'] ?? '',
|
/** @var Processor $processor */
|
||||||
]
|
$processor = app(Processor::class);
|
||||||
);
|
$processor->make($rule);
|
||||||
|
$processor->handleTransactionJournal($journal);
|
||||||
}
|
$journal->refresh();
|
||||||
|
if ($rule->stop_processing) {
|
||||||
/**
|
|
||||||
* Log about a duplicate transfer.
|
|
||||||
*
|
|
||||||
* @param array $transaction
|
|
||||||
*/
|
|
||||||
private function logDuplicateTransfer(array $transaction): void
|
|
||||||
{
|
|
||||||
Log::info(
|
|
||||||
'Transaction is a duplicate transfer, and will not be imported (such a transfer exists already).',
|
|
||||||
[
|
|
||||||
'description' => $transaction['description'] ?? '',
|
|
||||||
'amount' => $transaction['transactions'][0]['amount'] ?? 0,
|
|
||||||
'date' => $transaction['date'] ?? '',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shorthand method to quickly set job status
|
|
||||||
*
|
|
||||||
* @param string $status
|
|
||||||
*/
|
|
||||||
private function setStatus(string $status): void
|
|
||||||
{
|
|
||||||
$this->repository->setStatus($this->importJob, $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store array as journals.
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
* @throws FireflyException
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
private function storeArray(): Collection
|
|
||||||
{
|
|
||||||
/** @var array $array */
|
|
||||||
$array = $this->repository->getTransactions($this->importJob);
|
|
||||||
$count = count($array);
|
|
||||||
$toStore = [];
|
|
||||||
|
|
||||||
Log::notice(sprintf('Will now store the transactions. Count of items is %d.', $count));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Detect duplicates in initial array:
|
|
||||||
*/
|
|
||||||
foreach ($array as $index => $transaction) {
|
|
||||||
Log::debug(sprintf('Now at item %d out of %d', $index + 1, $count));
|
|
||||||
if ($this->duplicateDetected($index, $transaction)) {
|
|
||||||
Log::warning(sprintf('Row #%d seems to be a duplicate entry and will be ignored.', $index));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$transaction['import_hash_v2'] = $this->getHash($transaction);
|
|
||||||
$toStore[] = $transaction;
|
|
||||||
}
|
|
||||||
$count = count($toStore);
|
|
||||||
if (0 === $count) {
|
|
||||||
Log::info('No transactions to store left!');
|
|
||||||
|
|
||||||
return new Collection;
|
|
||||||
}
|
|
||||||
Log::notice(sprintf('After a first check for duplicates, the count of items is %d.', $count));
|
|
||||||
Log::notice('Going to store...');
|
|
||||||
// now actually store them:
|
|
||||||
$collection = new Collection;
|
|
||||||
foreach ($toStore as $index => $store) {
|
|
||||||
// do duplicate detection again!
|
|
||||||
if ($this->duplicateDetected($index, $store)) {
|
|
||||||
Log::warning(sprintf('Row #%d seems to be a imported already and will be ignored.', $index), $store);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::debug(sprintf('Going to store entry %d of %d', $index + 1, $count));
|
|
||||||
// convert the date to an object:
|
|
||||||
$store['date'] = Carbon::parse($store['date'], config('app.timezone'));
|
|
||||||
$store['description'] = '' === $store['description'] ? '(empty description)' : $store['description'];
|
|
||||||
// store the journal.
|
|
||||||
try {
|
|
||||||
$journal = $this->journalRepos->store($store);
|
|
||||||
} catch (FireflyException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
$this->repository->addErrorMessage($this->importJob, sprintf('Row #%d could not be imported. %s', $index, $e->getMessage()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info(sprintf('Stored #%d: "%s" (ID #%d)', $index, $journal->description, $journal->id));
|
|
||||||
Log::debug(sprintf('Stored as journal #%d', $journal->id));
|
|
||||||
$collection->push($journal);
|
|
||||||
|
|
||||||
// add to collection of transfers, if necessary:
|
|
||||||
if ('transfer' === strtolower($store['type'])) {
|
|
||||||
$transaction = $this->getTransactionFromJournal($journal);
|
|
||||||
Log::debug('We just stored a transfer, so add the journal to the list of transfers.');
|
|
||||||
$this->transfers->push($transaction);
|
|
||||||
Log::debug(sprintf('List length is now %d', $this->transfers->count()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log::notice(sprintf('Done storing. Firefly III has stored %d transactions.', $collection->count()));
|
|
||||||
|
|
||||||
return $collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a transfer exists.
|
|
||||||
*
|
|
||||||
* @param $transaction
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
* @SuppressWarnings(PHPMD.NPathComplexity)
|
|
||||||
*/
|
|
||||||
private function transferExists(array $transaction): bool
|
|
||||||
{
|
|
||||||
Log::debug('Check if array is a double transfer.');
|
|
||||||
if (strtolower(TransactionType::TRANSFER) !== strtolower($transaction['type'])) {
|
|
||||||
Log::debug(sprintf('Is a %s, not a transfer so no.', $transaction['type']));
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// how many hits do we need?
|
|
||||||
Log::debug(sprintf('Array has %d transactions.', count($transaction['transactions'])));
|
|
||||||
Log::debug(sprintf('System has %d existing transfers', count($this->transfers)));
|
|
||||||
// loop over each split:
|
|
||||||
Log::debug(sprintf('This transfer has %d split(s)', count($transaction['transactions'])));
|
|
||||||
foreach ($transaction['transactions'] as $index => $current) {
|
|
||||||
Log::debug(sprintf('Required hits for transfer comparison is %d', self::REQUIRED_HITS));
|
|
||||||
Log::debug(sprintf('Now at transfer split %d of %d', $index + 1, count($transaction['transactions'])));
|
|
||||||
|
|
||||||
// get the amount:
|
|
||||||
/** @noinspection UnnecessaryCastingInspection */
|
|
||||||
$amount = (string)($current['amount'] ?? '0');
|
|
||||||
if (bccomp($amount, '0') === -1) {
|
|
||||||
$amount = bcmul($amount, '-1'); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the description:
|
|
||||||
$description = '' === (string)$current['description'] ? $transaction['description'] : $current['description'];
|
|
||||||
|
|
||||||
// get the source and destination ID's:
|
|
||||||
$currentSourceIDs = [(int)$current['source_id'], (int)$current['destination_id']];
|
|
||||||
sort($currentSourceIDs);
|
|
||||||
|
|
||||||
// get the source and destination names:
|
|
||||||
$currentSourceNames = [(string)$current['source_name'], (string)$current['destination_name']];
|
|
||||||
sort($currentSourceNames);
|
|
||||||
|
|
||||||
// then loop all transfers:
|
|
||||||
/** @var Transaction $transfer */
|
|
||||||
foreach ($this->transfers as $transfer) {
|
|
||||||
// number of hits for this split-transfer combination:
|
|
||||||
$hits = 0;
|
|
||||||
Log::debug(sprintf('Now looking at transaction journal #%d', $transfer->journal_id));
|
|
||||||
// compare amount:
|
|
||||||
Log::debug(sprintf('Amount %s compared to %s', $amount, $transfer->transaction_amount));
|
|
||||||
if (0 !== bccomp($amount, $transfer->transaction_amount)) {
|
|
||||||
Log::debug('Amount is not a match, continue with next transfer.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++$hits;
|
|
||||||
Log::debug(sprintf('Comparison is a hit! (%s)', $hits));
|
|
||||||
|
|
||||||
// compare description:
|
|
||||||
$comparison = '(empty description)' === $transfer->description ? '' : $transfer->description;
|
|
||||||
Log::debug(sprintf('Comparing "%s" to "%s" (original: "%s")', $description, $transfer->description, $comparison));
|
|
||||||
if ($description !== $comparison) {
|
|
||||||
Log::debug('Description is not a match, continue with next transfer.');
|
|
||||||
continue; // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
++$hits;
|
|
||||||
Log::debug(sprintf('Comparison is a hit! (%s)', $hits));
|
|
||||||
|
|
||||||
// compare date:
|
|
||||||
$transferDate = $transfer->date->format('Y-m-d H:i:s');
|
|
||||||
Log::debug(sprintf('Comparing dates "%s" to "%s"', $transaction['date'], $transferDate));
|
|
||||||
if ($transaction['date'] !== $transferDate) {
|
|
||||||
Log::debug('Date is not a match, continue with next transfer.');
|
|
||||||
continue; // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
++$hits;
|
|
||||||
Log::debug(sprintf('Comparison is a hit! (%s)', $hits));
|
|
||||||
|
|
||||||
// compare source and destination id's
|
|
||||||
$transferSourceIDs = [(int)$transfer->account_id, (int)$transfer->opposing_account_id];
|
|
||||||
sort($transferSourceIDs);
|
|
||||||
/** @noinspection DisconnectedForeachInstructionInspection */
|
|
||||||
Log::debug('Comparing current transaction source+dest IDs', $currentSourceIDs);
|
|
||||||
Log::debug('.. with current transfer source+dest IDs', $transferSourceIDs);
|
|
||||||
if ($currentSourceIDs === $transferSourceIDs) {
|
|
||||||
++$hits;
|
|
||||||
Log::debug(sprintf('Source IDs are the same! (%d)', $hits));
|
|
||||||
}
|
|
||||||
Log::debug('Source IDs are not the same.');
|
|
||||||
unset($transferSourceIDs);
|
|
||||||
|
|
||||||
// compare source and destination names
|
|
||||||
$transferSource = [(string)$transfer->account_name, (string)$transfer->opposing_account_name];
|
|
||||||
sort($transferSource);
|
|
||||||
/** @noinspection DisconnectedForeachInstructionInspection */
|
|
||||||
Log::debug('Comparing current transaction source+dest names', $currentSourceNames);
|
|
||||||
Log::debug('.. with current transfer source+dest names', $transferSource);
|
|
||||||
if ($currentSourceNames === $transferSource) {
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
++$hits;
|
|
||||||
Log::debug(sprintf('Source names are the same! (%d)', $hits));
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
}
|
|
||||||
Log::debug('Source names are not the same.');
|
|
||||||
Log::debug(sprintf('Number of hits is %d', $hits));
|
|
||||||
if ($hits >= self::REQUIRED_HITS) {
|
|
||||||
Log::debug(sprintf('Is more than %d, return true.', self::REQUIRED_HITS));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log::debug('Is not an existing transfer, return false.');
|
|
||||||
|
|
||||||
return false;
|
/**
|
||||||
|
* Gets the users rules.
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function getRules(): Collection
|
||||||
|
{
|
||||||
|
/** @var RuleRepositoryInterface $repository */
|
||||||
|
$repository = app(RuleRepositoryInterface::class);
|
||||||
|
$repository->setUser($this->importJob->user);
|
||||||
|
$set = $repository->getForImport();
|
||||||
|
|
||||||
|
Log::debug(sprintf('Found %d user rules.', $set->count()));
|
||||||
|
|
||||||
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Jobs;
|
namespace FireflyIII\Jobs;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\TransactionRules\Processor;
|
use FireflyIII\TransactionRules\Processor;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
@ -34,6 +34,7 @@ use Illuminate\Support\Collection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ExecuteRuleGroupOnExistingTransactions.
|
* Class ExecuteRuleGroupOnExistingTransactions.
|
||||||
|
* TODO make sure this job honors the "stop_processing" rules.
|
||||||
*/
|
*/
|
||||||
class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
|
class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
|
||||||
{
|
{
|
||||||
@ -148,19 +149,20 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
// Lookup all journals that match the parameters specified
|
// Lookup all journals that match the parameters specified
|
||||||
$transactions = $this->collectJournals();
|
$journals = $this->collectJournals();
|
||||||
|
|
||||||
// Find processors for each rule within the current rule group
|
// Find processors for each rule within the current rule group
|
||||||
$processors = $this->collectProcessors();
|
$processors = $this->collectProcessors();
|
||||||
|
|
||||||
// Execute the rules for each transaction
|
// Execute the rules for each transaction
|
||||||
foreach ($processors as $processor) {
|
foreach ($processors as $processor) {
|
||||||
foreach ($transactions as $transaction) {
|
/** @var array $journal */
|
||||||
|
foreach ($journals as $journal) {
|
||||||
/** @var Processor $processor */
|
/** @var Processor $processor */
|
||||||
$processor->handleTransaction($transaction);
|
$processor->handleJournalArray($journal);
|
||||||
|
|
||||||
}
|
}
|
||||||
// Stop processing this group if the rule specifies 'stop_processing'
|
// Stop processing this group if the rule specifies 'stop_processing'
|
||||||
|
// TODO Fix this.
|
||||||
if ($processor->getRule()->stop_processing) {
|
if ($processor->getRule()->stop_processing) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -170,16 +172,16 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
|
|||||||
/**
|
/**
|
||||||
* Collect all journals that should be processed.
|
* Collect all journals that should be processed.
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function collectJournals(): Collection
|
protected function collectJournals(): array
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
|
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
|
||||||
|
|
||||||
return $collector->getTransactions();
|
return $collector->getExtractedJournals();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Jobs;
|
namespace FireflyIII\Jobs;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Models\Rule;
|
use FireflyIII\Models\Rule;
|
||||||
use FireflyIII\TransactionRules\Processor;
|
use FireflyIII\TransactionRules\Processor;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
@ -161,7 +161,7 @@ class ExecuteRuleOnExistingTransactions extends Job implements ShouldQueue
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
// Lookup all journals that match the parameters specified
|
// Lookup all journals that match the parameters specified
|
||||||
$transactions = $this->collectJournals();
|
$journals = $this->collectJournals();
|
||||||
/** @var Processor $processor */
|
/** @var Processor $processor */
|
||||||
$processor = app(Processor::class);
|
$processor = app(Processor::class);
|
||||||
$processor->make($this->rule, true);
|
$processor->make($this->rule, true);
|
||||||
@ -169,9 +169,10 @@ class ExecuteRuleOnExistingTransactions extends Job implements ShouldQueue
|
|||||||
$misses = 0;
|
$misses = 0;
|
||||||
$total = 0;
|
$total = 0;
|
||||||
// Execute the rules for each transaction
|
// Execute the rules for each transaction
|
||||||
foreach ($transactions as $transaction) {
|
/** @var array $journal */
|
||||||
|
foreach ($journals as $journal) {
|
||||||
++$total;
|
++$total;
|
||||||
$result = $processor->handleTransaction($transaction);
|
$result = $processor->handleJournalArray($journal);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
++$hits;
|
++$hits;
|
||||||
}
|
}
|
||||||
@ -186,15 +187,16 @@ class ExecuteRuleOnExistingTransactions extends Job implements ShouldQueue
|
|||||||
/**
|
/**
|
||||||
* Collect all journals that should be processed.
|
* Collect all journals that should be processed.
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function collectJournals(): Collection
|
protected function collectJournals(): array
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
|
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
|
||||||
|
|
||||||
return $collector->getTransactions();
|
return $collector->getExtractedJournals();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,7 @@ namespace FireflyIII\Repositories\Account;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
|
@ -27,14 +27,12 @@ use Exception;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Factory\TransactionCurrencyFactory;
|
use FireflyIII\Factory\TransactionCurrencyFactory;
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\AvailableBudget;
|
use FireflyIII\Models\AvailableBudget;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\RuleAction;
|
use FireflyIII\Models\RuleAction;
|
||||||
use FireflyIII\Models\RuleTrigger;
|
use FireflyIII\Models\RuleTrigger;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
@ -164,6 +162,7 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$collector->setAccounts($accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $collector->getSum();
|
return $collector->getSum();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -556,18 +555,19 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get all transactions:
|
// get all transactions:
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end);
|
$collector->setAccounts($accounts)->setRange($start, $end);
|
||||||
$collector->setBudgets($budgets);
|
$collector->setBudgets($budgets);
|
||||||
$transactions = $collector->getTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
|
|
||||||
// loop transactions:
|
// loop transactions:
|
||||||
/** @var Transaction $transaction */
|
/** @var array $journal */
|
||||||
foreach ($transactions as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$budgetId = max((int)$transaction->transaction_journal_budget_id, (int)$transaction->transaction_budget_id);
|
$budgetId = (int)$journal['budget_id'];
|
||||||
$date = $transaction->date->format($carbonFormat);
|
$date = $journal['date']->format($carbonFormat);
|
||||||
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $transaction->transaction_amount);
|
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@ -623,25 +623,28 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array
|
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
|
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end);
|
$collector->setAccounts($accounts)->setRange($start, $end);
|
||||||
$collector->setTypes([TransactionType::WITHDRAWAL]);
|
$collector->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
$collector->withoutBudget();
|
$collector->withoutBudget();
|
||||||
$transactions = $collector->getTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
$result = [
|
$result = [
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
'name' => (string)trans('firefly.no_budget'),
|
'name' => (string)trans('firefly.no_budget'),
|
||||||
'sum' => '0',
|
'sum' => '0',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($transactions as $transaction) {
|
/** @var array $journal */
|
||||||
$date = $transaction->date->format($carbonFormat);
|
foreach ($journals as $journal) {
|
||||||
|
$date = $journal['date']->format($carbonFormat);
|
||||||
|
|
||||||
if (!isset($result['entries'][$date])) {
|
if (!isset($result['entries'][$date])) {
|
||||||
$result['entries'][$date] = '0';
|
$result['entries'][$date] = '0';
|
||||||
}
|
}
|
||||||
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
|
$result['entries'][$date] = bcadd($result['entries'][$date], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -794,39 +797,41 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function spentInPeriodWoBudgetMc(Collection $accounts, Carbon $start, Carbon $end): array
|
public function spentInPeriodWoBudgetMc(Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
|
||||||
|
|
||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$collector->setAccounts($accounts);
|
||||||
}
|
}
|
||||||
if (0 === $accounts->count()) {
|
$journals = $collector->getExtractedJournals();
|
||||||
$collector->setAllAssetAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$return = [];
|
$return = [];
|
||||||
$total = [];
|
$total = [];
|
||||||
$currencies = [];
|
$currencies = [];
|
||||||
/** @var Transaction $transaction */
|
/** @var array $journal */
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$code = $transaction->transaction_currency_code;
|
$code = $journal['currency_code'];
|
||||||
if (!isset($currencies[$code])) {
|
if (!isset($currencies[$code])) {
|
||||||
$currencies[$code] = $transaction->transactionCurrency;
|
$currencies[$code] = [
|
||||||
|
'id' => $journal['currency_id'],
|
||||||
|
'name' => $journal['currency_name'],
|
||||||
|
'symbol' => $journal['currency_symbol'],
|
||||||
|
'decimal_places' => $journal['currency_decimal_places'],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
$total[$code] = isset($total[$code]) ? bcadd($total[$code], $transaction->transaction_amount) : $transaction->transaction_amount;
|
$total[$code] = isset($total[$code]) ? bcadd($total[$code], $journal['amount']) : $journal['amount'];
|
||||||
}
|
}
|
||||||
foreach ($total as $code => $spent) {
|
foreach ($total as $code => $spent) {
|
||||||
/** @var TransactionCurrency $currency */
|
/** @var TransactionCurrency $currency */
|
||||||
$currency = $currencies[$code];
|
$currency = $currencies[$code];
|
||||||
$return[] = [
|
$return[] = [
|
||||||
'currency_id' => $currency->id,
|
'currency_id' => $currency['id'],
|
||||||
'currency_code' => $code,
|
'currency_code' => $code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency['symbol'],
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency['decimal_places'],
|
||||||
'amount' => round($spent, $currency->decimal_places),
|
'amount' => round($spent, $currency['decimal_places']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +25,7 @@ namespace FireflyIII\Repositories\Category;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Factory\CategoryFactory;
|
use FireflyIII\Factory\CategoryFactory;
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Services\Internal\Destroy\CategoryDestroyService;
|
use FireflyIII\Services\Internal\Destroy\CategoryDestroyService;
|
||||||
use FireflyIII\Services\Internal\Update\CategoryUpdateService;
|
use FireflyIII\Services\Internal\Update\CategoryUpdateService;
|
||||||
@ -142,43 +140,30 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array
|
public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->withoutCategory();
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->withoutCategory();
|
||||||
|
|
||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$collector->setAccounts($accounts);
|
||||||
}
|
}
|
||||||
if (0 === $accounts->count()) {
|
$journals = $collector->getExtractedJournals();
|
||||||
$collector->setAllAssetAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$set = $set->filter(
|
|
||||||
function (Transaction $transaction) {
|
|
||||||
if (bccomp($transaction->transaction_amount, '0') === 1) {
|
|
||||||
return $transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$return = [];
|
$return = [];
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$currencyId = (int)$journal['currency_id'];
|
||||||
if (!isset($return[$currencyId])) {
|
if (!isset($return[$currencyId])) {
|
||||||
$return[$currencyId] = [
|
$return[$currencyId] = [
|
||||||
'spent' => '0',
|
'spent' => '0',
|
||||||
'currency_id' => $currencyId,
|
'currency_id' => $currencyId,
|
||||||
'currency_symbol' => $transaction->transaction_currency_symbol,
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
'currency_code' => $transaction->transaction_currency_code,
|
'currency_code' => $journal['currency_code'],
|
||||||
'currency_decimal_places' => $transaction->transaction_currency_dp,
|
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $transaction->transaction_amount);
|
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
@ -194,8 +179,9 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
|
public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
||||||
|
|
||||||
@ -209,20 +195,12 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$collector->setAccounts($accounts);
|
||||||
}
|
}
|
||||||
if (0 === $accounts->count()) {
|
$journals = $collector->getExtractedJournals();
|
||||||
$collector->setAllAssetAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$return = [];
|
$return = [];
|
||||||
/** @var Transaction $transaction */
|
foreach ($journals as $journal) {
|
||||||
foreach ($set as $transaction) {
|
$categoryId = (int)$journal['category_id'];
|
||||||
$jrnlCatId = (int)$transaction->transaction_journal_category_id;
|
$currencyId = (int)$journal['currency_id'];
|
||||||
$transCatId = (int)$transaction->transaction_category_id;
|
$name = $journal['category_name'];
|
||||||
$categoryId = max($jrnlCatId, $transCatId);
|
|
||||||
$currencyId = (int)$transaction->transaction_currency_id;
|
|
||||||
$name = $transaction->transaction_category_name;
|
|
||||||
$name = '' === (string)$name ? $transaction->transaction_journal_category_name : $name;
|
|
||||||
// make array for category:
|
// make array for category:
|
||||||
if (!isset($return[$categoryId])) {
|
if (!isset($return[$categoryId])) {
|
||||||
$return[$categoryId] = [
|
$return[$categoryId] = [
|
||||||
@ -234,13 +212,13 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
$return[$categoryId]['earned'][$currencyId] = [
|
$return[$categoryId]['earned'][$currencyId] = [
|
||||||
'earned' => '0',
|
'earned' => '0',
|
||||||
'currency_id' => $currencyId,
|
'currency_id' => $currencyId,
|
||||||
'currency_symbol' => $transaction->transaction_currency_symbol,
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
'currency_code' => $transaction->transaction_currency_code,
|
'currency_code' => $journal['currency_code'],
|
||||||
'currency_decimal_places' => $transaction->transaction_currency_dp,
|
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$return[$categoryId]['earned'][$currencyId]['earned']
|
$return[$categoryId]['earned'][$currencyId]['earned']
|
||||||
= bcadd($return[$categoryId]['earned'][$currencyId]['earned'], $transaction->transaction_amount);
|
= bcadd($return[$categoryId]['earned'][$currencyId]['earned'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
@ -521,23 +499,20 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get all transactions:
|
// get all transactions:
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end);
|
$collector->setAccounts($accounts)->setRange($start, $end);
|
||||||
$collector->setCategories($categories)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
$collector->setCategories($categories)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
->withOpposingAccount();
|
->withAccountInformation()->withCategoryInformation();
|
||||||
$transactions = $collector->getTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
|
|
||||||
// loop transactions:
|
// loop transactions:
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($transactions as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
// if positive, skip:
|
$categoryId = (int)$journal['category_id'];
|
||||||
if (1 === bccomp($transaction->transaction_amount, '0')) {
|
$date = $journal['date']->format($carbonFormat);
|
||||||
continue;
|
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $journal['amount']);
|
||||||
}
|
|
||||||
$categoryId = max((int)$transaction->transaction_journal_category_id, (int)$transaction->transaction_category_id);
|
|
||||||
$date = $transaction->date->format($carbonFormat);
|
|
||||||
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@ -553,29 +528,28 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array
|
public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
|
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end)->withOpposingAccount();
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
|
$collector->setAccounts($accounts)->setRange($start, $end)->withAccountInformation();
|
||||||
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]);
|
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]);
|
||||||
$collector->withoutCategory();
|
$collector->withoutCategory();
|
||||||
$transactions = $collector->getTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
$result = [
|
$result = [
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
'name' => (string)trans('firefly.no_category'),
|
'name' => (string)trans('firefly.no_category'),
|
||||||
'sum' => '0',
|
'sum' => '0',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($transactions as $transaction) {
|
/** @var array $journal */
|
||||||
// if positive, skip:
|
foreach ($journals as $journal) {
|
||||||
if (1 === bccomp($transaction->transaction_amount, '0')) {
|
$date = $journal['date']->format($carbonFormat);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$date = $transaction->date->format($carbonFormat);
|
|
||||||
|
|
||||||
if (!isset($result['entries'][$date])) {
|
if (!isset($result['entries'][$date])) {
|
||||||
$result['entries'][$date] = '0';
|
$result['entries'][$date] = '0';
|
||||||
}
|
}
|
||||||
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
|
$result['entries'][$date] = bcadd($result['entries'][$date], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -604,23 +578,20 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get all transactions:
|
// get all transactions:
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end);
|
$collector->setAccounts($accounts)->setRange($start, $end);
|
||||||
$collector->setCategories($categories)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
$collector->setCategories($categories)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||||
->withOpposingAccount();
|
->withAccountInformation();
|
||||||
$transactions = $collector->getTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
|
|
||||||
// loop transactions:
|
// loop transactions:
|
||||||
/** @var Transaction $transaction */
|
/** @var array $journal */
|
||||||
foreach ($transactions as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
// if negative, skip:
|
$categoryId = (int)$journal['category_id'];
|
||||||
if (bccomp($transaction->transaction_amount, '0') === -1) {
|
$date = $journal['date']->format($carbonFormat);
|
||||||
continue;
|
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $journal['amount']);
|
||||||
}
|
|
||||||
$categoryId = max((int)$transaction->transaction_journal_category_id, (int)$transaction->transaction_category_id);
|
|
||||||
$date = $transaction->date->format($carbonFormat);
|
|
||||||
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@ -637,29 +608,28 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
{
|
{
|
||||||
Log::debug('Now in periodIncomeNoCategory()');
|
Log::debug('Now in periodIncomeNoCategory()');
|
||||||
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
|
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end)->withOpposingAccount();
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
|
$collector->setAccounts($accounts)->setRange($start, $end)->withAccountInformation();
|
||||||
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]);
|
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]);
|
||||||
$collector->withoutCategory();
|
$collector->withoutCategory();
|
||||||
$transactions = $collector->getTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
$result = [
|
$result = [
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
'name' => (string)trans('firefly.no_category'),
|
'name' => (string)trans('firefly.no_category'),
|
||||||
'sum' => '0',
|
'sum' => '0',
|
||||||
];
|
];
|
||||||
Log::debug('Looping transactions..');
|
Log::debug('Looping transactions..');
|
||||||
foreach ($transactions as $transaction) {
|
|
||||||
// if negative, skip:
|
foreach ($journals as $journal) {
|
||||||
if (bccomp($transaction->transaction_amount, '0') === -1) {
|
$date = $journal['date']->format($carbonFormat);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$date = $transaction->date->format($carbonFormat);
|
|
||||||
|
|
||||||
if (!isset($result['entries'][$date])) {
|
if (!isset($result['entries'][$date])) {
|
||||||
$result['entries'][$date] = '0';
|
$result['entries'][$date] = '0';
|
||||||
}
|
}
|
||||||
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
|
$result['entries'][$date] = bcadd($result['entries'][$date], $journal['amount']);
|
||||||
}
|
}
|
||||||
Log::debug('Done looping transactions..');
|
Log::debug('Done looping transactions..');
|
||||||
Log::debug('Finished periodIncomeNoCategory()');
|
Log::debug('Finished periodIncomeNoCategory()');
|
||||||
@ -714,6 +684,8 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
{
|
{
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
|
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategories($categories);
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategories($categories);
|
||||||
|
|
||||||
@ -833,30 +805,17 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string
|
public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory();
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory();
|
||||||
|
|
||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$collector->setAccounts($accounts);
|
||||||
}
|
}
|
||||||
if (0 === $accounts->count()) {
|
|
||||||
$collector->setAllAssetAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = $collector->getTransactions();
|
return $collector->getSum();
|
||||||
$set = $set->filter(
|
|
||||||
function (Transaction $transaction) {
|
|
||||||
if (bccomp($transaction->transaction_amount, '0') === -1) {
|
|
||||||
return $transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (string)$set->sum('transaction_amount');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,11 +25,6 @@ namespace FireflyIII\Repositories\Journal;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use DB;
|
use DB;
|
||||||
use Exception;
|
use Exception;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
|
||||||
use FireflyIII\Factory\TransactionGroupFactory;
|
|
||||||
use FireflyIII\Factory\TransactionJournalFactory;
|
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Note;
|
use FireflyIII\Models\Note;
|
||||||
@ -386,6 +381,35 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return Carbon value of a meta field (or NULL).
|
||||||
|
*
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
* @param string $field
|
||||||
|
*
|
||||||
|
* @return null|Carbon
|
||||||
|
*/
|
||||||
|
public function getMetaDate(TransactionJournal $journal, string $field): ?Carbon
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty('journal-meta-updated');
|
||||||
|
$cache->addProperty($journal->id);
|
||||||
|
$cache->addProperty($field);
|
||||||
|
|
||||||
|
if ($cache->has()) {
|
||||||
|
return new Carbon($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $journal->transactionJournalMeta()->where('name', $field)->first();
|
||||||
|
if (null === $entry) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$value = new Carbon($entry->data);
|
||||||
|
$cache->store($entry->data);
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of all destination accounts related to journal.
|
* Return a list of all destination accounts related to journal.
|
||||||
*
|
*
|
||||||
@ -493,35 +517,6 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return Carbon value of a meta field (or NULL).
|
|
||||||
*
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
* @param string $field
|
|
||||||
*
|
|
||||||
* @return null|Carbon
|
|
||||||
*/
|
|
||||||
public function getMetaDate(TransactionJournal $journal, string $field): ?Carbon
|
|
||||||
{
|
|
||||||
$cache = new CacheProperties;
|
|
||||||
$cache->addProperty('journal-meta-updated');
|
|
||||||
$cache->addProperty($journal->id);
|
|
||||||
$cache->addProperty($field);
|
|
||||||
|
|
||||||
if ($cache->has()) {
|
|
||||||
return new Carbon($cache->get()); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
$entry = $journal->transactionJournalMeta()->where('name', $field)->first();
|
|
||||||
if (null === $entry) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$value = new Carbon($entry->data);
|
|
||||||
$cache->store($entry->data);
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return string value of a meta date (or NULL).
|
* Return string value of a meta date (or NULL).
|
||||||
*
|
*
|
||||||
@ -666,33 +661,6 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
return $journal->transactionType->type;
|
return $journal->transactionType->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $transactionIds
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
public function getTransactionsById(array $transactionIds): Collection
|
|
||||||
{
|
|
||||||
$journalIds = Transaction::whereIn('id', $transactionIds)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
|
|
||||||
$journals = new Collection;
|
|
||||||
foreach ($journalIds as $journalId) {
|
|
||||||
$result = $this->findNull((int)$journalId);
|
|
||||||
if (null !== $result) {
|
|
||||||
$journals->push($result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setUser($this->user);
|
|
||||||
$collector->setAllAssetAccounts();
|
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
//$collector->addFilter(TransferFilter::class);
|
|
||||||
|
|
||||||
$collector->setJournals($journals)->withOpposingAccount();
|
|
||||||
|
|
||||||
return $collector->getTransactions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will tell you if journal is reconciled or not.
|
* Will tell you if journal is reconciled or not.
|
||||||
*
|
*
|
||||||
@ -711,6 +679,22 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $transactionId
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function reconcileById(int $transactionId): bool
|
||||||
|
{
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
$transaction = $this->user->transactions()->find($transactionId);
|
||||||
|
if (null !== $transaction) {
|
||||||
|
return $this->reconcile($transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Transaction $transaction
|
* @param Transaction $transaction
|
||||||
*
|
*
|
||||||
@ -736,22 +720,6 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $transactionId
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function reconcileById(int $transactionId): bool
|
|
||||||
{
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
$transaction = $this->user->transactions()->find($transactionId);
|
|
||||||
if (null !== $transaction) {
|
|
||||||
return $this->reconcile($transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
* @param int $order
|
* @param int $order
|
||||||
|
@ -276,13 +276,6 @@ interface JournalRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function getTransactionType(TransactionJournal $journal): string;
|
public function getTransactionType(TransactionJournal $journal): string;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $transactionIds
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
public function getTransactionsById(array $transactionIds): Collection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will tell you if journal is reconciled or not.
|
* Will tell you if journal is reconciled or not.
|
||||||
*
|
*
|
||||||
@ -319,8 +312,6 @@ interface JournalRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function setUser(User $user);
|
public function setUser(User $user);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update budget for a journal.
|
* Update budget for a journal.
|
||||||
*
|
*
|
||||||
|
@ -26,8 +26,7 @@ namespace FireflyIII\Repositories\Recurring;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Factory\RecurrenceFactory;
|
use FireflyIII\Factory\RecurrenceFactory;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|
||||||
use FireflyIII\Models\Note;
|
use FireflyIII\Models\Note;
|
||||||
use FireflyIII\Models\Preference;
|
use FireflyIII\Models\Preference;
|
||||||
use FireflyIII\Models\Recurrence;
|
use FireflyIII\Models\Recurrence;
|
||||||
@ -290,25 +289,27 @@ class RecurringRepository implements RecurringRepositoryInterface
|
|||||||
->get()->pluck('transaction_journal_id')->toArray();
|
->get()->pluck('transaction_journal_id')->toArray();
|
||||||
$search = [];
|
$search = [];
|
||||||
foreach ($journalMeta as $journalId) {
|
foreach ($journalMeta as $journalId) {
|
||||||
$search[] = ['id' => (int)$journalId];
|
$search[] = (int)$journalId;
|
||||||
}
|
}
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setUser($recurrence->user);
|
|
||||||
$collector->withOpposingAccount()->setAllAssetAccounts()->withCategoryInformation()->withBudgetInformation()->setLimit($pageSize)->setPage($page);
|
|
||||||
// filter on specific journals.
|
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
$collector->setJournals(new Collection($search));
|
|
||||||
|
|
||||||
return $collector->getPaginatedTransactions();
|
$collector->setUser($recurrence->user);
|
||||||
|
$collector->withCategoryInformation()->withBudgetInformation()->setLimit($pageSize)->setPage($page)
|
||||||
|
->withAccountInformation();
|
||||||
|
$collector->setJournalIds($search);
|
||||||
|
|
||||||
|
return $collector->getPaginatedGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO check usage and verify it still works.
|
||||||
|
*
|
||||||
* @param Recurrence $recurrence
|
* @param Recurrence $recurrence
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getTransactions(Recurrence $recurrence): Collection
|
public function getTransactions(Recurrence $recurrence): array
|
||||||
{
|
{
|
||||||
$journalMeta = TransactionJournalMeta
|
$journalMeta = TransactionJournalMeta
|
||||||
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||||
@ -319,17 +320,17 @@ class RecurringRepository implements RecurringRepositoryInterface
|
|||||||
->get()->pluck('transaction_journal_id')->toArray();
|
->get()->pluck('transaction_journal_id')->toArray();
|
||||||
$search = [];
|
$search = [];
|
||||||
foreach ($journalMeta as $journalId) {
|
foreach ($journalMeta as $journalId) {
|
||||||
$search[] = ['id' => (int)$journalId];
|
$search[] = (int)$journalId;
|
||||||
}
|
}
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setUser($recurrence->user);
|
|
||||||
$collector->withOpposingAccount()->setAllAssetAccounts()->withCategoryInformation()->withBudgetInformation();
|
|
||||||
// filter on specific journals.
|
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
$collector->setJournals(new Collection($search));
|
|
||||||
|
|
||||||
return $collector->getTransactions();
|
$collector->setUser($recurrence->user);
|
||||||
|
$collector->withCategoryInformation()->withBudgetInformation()->withAccountInformation();
|
||||||
|
// filter on specific journals.
|
||||||
|
$collector->setJournalIds($search);
|
||||||
|
|
||||||
|
return $collector->getExtractedJournals();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,9 +141,9 @@ interface RecurringRepositoryInterface
|
|||||||
/**
|
/**
|
||||||
* @param Recurrence $recurrence
|
* @param Recurrence $recurrence
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getTransactions(Recurrence $recurrence): Collection;
|
public function getTransactions(Recurrence $recurrence): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the next X iterations starting on the date given in $date.
|
* Calculate the next X iterations starting on the date given in $date.
|
||||||
|
@ -26,7 +26,6 @@ use Carbon\Carbon;
|
|||||||
use DB;
|
use DB;
|
||||||
use FireflyIII\Factory\TagFactory;
|
use FireflyIII\Factory\TagFactory;
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
@ -84,13 +83,13 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function earnedInPeriod(Tag $tag, Carbon $start, Carbon $end): string
|
public function earnedInPeriod(Tag $tag, Carbon $start, Carbon $end): string
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setUser($this->user);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAllAssetAccounts()->setTag($tag);
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
|
|
||||||
return (string)$set->sum('transaction_amount');
|
$collector->setUser($this->user);
|
||||||
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setTag($tag);
|
||||||
|
|
||||||
|
return $collector->getSum();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,13 +243,13 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function spentInPeriod(Tag $tag, Carbon $start, Carbon $end): string
|
public function spentInPeriod(Tag $tag, Carbon $start, Carbon $end): string
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setUser($this->user);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAllAssetAccounts()->setTag($tag);
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
|
|
||||||
return (string)$set->sum('transaction_amount');
|
$collector->setUser($this->user);
|
||||||
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setTag($tag);
|
||||||
|
|
||||||
|
return $collector->getSum();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,12 +25,10 @@ namespace FireflyIII\Support\Http\Controllers;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
@ -86,22 +84,19 @@ trait AugumentData
|
|||||||
*/
|
*/
|
||||||
protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing)->withCategoryInformation();
|
$total = $assets->merge($opposing);
|
||||||
$set = $collector->getTransactions();
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total);
|
||||||
|
$collector->withCategoryInformation();
|
||||||
|
$journals = $collector->getExtractedJournals();
|
||||||
$sum = [];
|
$sum = [];
|
||||||
// loop to support multi currency
|
// loop to support multi currency
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$currencyId = $journal['currency_id'];
|
||||||
$categoryName = $transaction->transaction_category_name;
|
$categoryName = $journal['category_name'];
|
||||||
$categoryId = (int)$transaction->transaction_category_id;
|
$categoryId = (int)$journal['category_id'];
|
||||||
// if null, grab from journal:
|
|
||||||
if (0 === $categoryId) {
|
|
||||||
$categoryName = $transaction->transaction_journal_category_name;
|
|
||||||
$categoryId = (int)$transaction->transaction_journal_category_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
// if not set, set to zero:
|
||||||
if (!isset($sum[$categoryId][$currencyId])) {
|
if (!isset($sum[$categoryId][$currencyId])) {
|
||||||
@ -116,8 +111,8 @@ trait AugumentData
|
|||||||
'name' => $categoryName,
|
'name' => $categoryName,
|
||||||
],
|
],
|
||||||
'currency' => [
|
'currency' => [
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
'symbol' => $journal['currency_symbol'],
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
'dp' => $journal['currency_decimal_places'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -126,9 +121,9 @@ trait AugumentData
|
|||||||
|
|
||||||
// add amount
|
// add amount
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
|
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
|
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $journal['amount']
|
||||||
);
|
);
|
||||||
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount);
|
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sum;
|
return $sum;
|
||||||
@ -146,33 +141,34 @@ trait AugumentData
|
|||||||
*/
|
*/
|
||||||
protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing);
|
$total = $assets->merge($opposing);
|
||||||
$set = $collector->getTransactions();
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total);
|
||||||
|
$journals = $collector->getExtractedJournals();
|
||||||
$sum = [
|
$sum = [
|
||||||
'grand_sum' => '0',
|
'grand_sum' => '0',
|
||||||
'per_currency' => [],
|
'per_currency' => [],
|
||||||
];
|
];
|
||||||
// loop to support multi currency
|
// loop to support multi currency
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$currencyId = (int)$journal['currency_id'];
|
||||||
|
|
||||||
// if not set, set to zero:
|
// if not set, set to zero:
|
||||||
if (!isset($sum['per_currency'][$currencyId])) {
|
if (!isset($sum['per_currency'][$currencyId])) {
|
||||||
$sum['per_currency'][$currencyId] = [
|
$sum['per_currency'][$currencyId] = [
|
||||||
'sum' => '0',
|
'sum' => '0',
|
||||||
'currency' => [
|
'currency' => [
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
'symbol' => $journal['currency_symbol'],
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
'decimal_places' => $journal['currency_decimal_places'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// add amount
|
// add amount
|
||||||
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount);
|
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $journal['amount']);
|
||||||
$sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount);
|
$sum['grand_sum'] = bcadd($sum['grand_sum'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sum;
|
return $sum;
|
||||||
@ -525,19 +521,27 @@ trait AugumentData
|
|||||||
/**
|
/**
|
||||||
* Group set of transactions by name of opposing account.
|
* Group set of transactions by name of opposing account.
|
||||||
*
|
*
|
||||||
* @param Collection $set
|
* @param array $array
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function groupByName(Collection $set): array // filter + group data
|
protected function groupByName(array $array): array // filter + group data
|
||||||
{
|
{
|
||||||
|
|
||||||
// group by opposing account name.
|
// group by opposing account name.
|
||||||
$grouped = [];
|
$grouped = [];
|
||||||
/** @var Transaction $transaction */
|
/** @var array $journal */
|
||||||
foreach ($set as $transaction) {
|
foreach ($array as $journal) {
|
||||||
$name = $transaction->opposing_account_name;
|
$name = '(no name)';
|
||||||
|
if (TransactionType::WITHDRAWAL === $journal['transaction_type_type']) {
|
||||||
|
$name = $journal['destination_account_name'];
|
||||||
|
}
|
||||||
|
if (TransactionType::WITHDRAWAL !== $journal['transaction_type_type']) {
|
||||||
|
$name = $journal['source_account_name'];
|
||||||
|
}
|
||||||
|
|
||||||
$grouped[$name] = $grouped[$name] ?? '0';
|
$grouped[$name] = $grouped[$name] ?? '0';
|
||||||
$grouped[$name] = bcadd($transaction->transaction_amount, $grouped[$name]);
|
$grouped[$name] = bcadd($journal['amount'], $grouped[$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $grouped;
|
return $grouped;
|
||||||
@ -587,22 +591,18 @@ trait AugumentData
|
|||||||
*/
|
*/
|
||||||
protected function spentByBudget(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
protected function spentByBudget(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
|
$total = $assets->merge($opposing);
|
||||||
$collector->setOpposingAccounts($opposing)->withBudgetInformation();
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($total);
|
||||||
$set = $collector->getTransactions();
|
$collector->withBudgetInformation();
|
||||||
|
$journals = $collector->getExtractedJournals();
|
||||||
$sum = [];
|
$sum = [];
|
||||||
// loop to support multi currency
|
// loop to support multi currency
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$currencyId = $journal['currency_id'];
|
||||||
$budgetName = $transaction->transaction_budget_name;
|
$budgetName = $journal['budget_name'];
|
||||||
$budgetId = (int)$transaction->transaction_budget_id;
|
$budgetId = (int)$journal['budget_id'];
|
||||||
// if null, grab from journal:
|
|
||||||
if (0 === $budgetId) {
|
|
||||||
$budgetName = $transaction->transaction_journal_budget_name;
|
|
||||||
$budgetId = (int)$transaction->transaction_journal_budget_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
// if not set, set to zero:
|
||||||
if (!isset($sum[$budgetId][$currencyId])) {
|
if (!isset($sum[$budgetId][$currencyId])) {
|
||||||
@ -617,8 +617,8 @@ trait AugumentData
|
|||||||
'name' => $budgetName,
|
'name' => $budgetName,
|
||||||
],
|
],
|
||||||
'currency' => [
|
'currency' => [
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
'symbol' => $journal['currency_symbol'],
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
'dp' => $journal['currency_decimal_places'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -627,9 +627,9 @@ trait AugumentData
|
|||||||
|
|
||||||
// add amount
|
// add amount
|
||||||
$sum[$budgetId]['per_currency'][$currencyId]['sum'] = bcadd(
|
$sum[$budgetId]['per_currency'][$currencyId]['sum'] = bcadd(
|
||||||
$sum[$budgetId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
|
$sum[$budgetId]['per_currency'][$currencyId]['sum'], $journal['amount']
|
||||||
);
|
);
|
||||||
$sum[$budgetId]['grand_total'] = bcadd($sum[$budgetId]['grand_total'], $transaction->transaction_amount);
|
$sum[$budgetId]['grand_total'] = bcadd($sum[$budgetId]['grand_total'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sum;
|
return $sum;
|
||||||
@ -652,22 +652,18 @@ trait AugumentData
|
|||||||
*/
|
*/
|
||||||
protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
|
$total = $assets->merge($opposing);
|
||||||
$collector->setOpposingAccounts($opposing)->withCategoryInformation();
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($total);
|
||||||
$set = $collector->getTransactions();
|
$collector->withCategoryInformation();
|
||||||
|
$journals = $collector->getExtractedJournals();
|
||||||
$sum = [];
|
$sum = [];
|
||||||
// loop to support multi currency
|
// loop to support multi currency
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$currencyId = (int)$journal['currency_id'];
|
||||||
$categoryName = $transaction->transaction_category_name;
|
$categoryName = $journal['category_name'];
|
||||||
$categoryId = (int)$transaction->transaction_category_id;
|
$categoryId = (int)$journal['category_id'];
|
||||||
// if null, grab from journal:
|
|
||||||
if (0 === $categoryId) {
|
|
||||||
$categoryName = $transaction->transaction_journal_category_name;
|
|
||||||
$categoryId = (int)$transaction->transaction_journal_category_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
// if not set, set to zero:
|
||||||
if (!isset($sum[$categoryId][$currencyId])) {
|
if (!isset($sum[$categoryId][$currencyId])) {
|
||||||
@ -682,8 +678,8 @@ trait AugumentData
|
|||||||
'name' => $categoryName,
|
'name' => $categoryName,
|
||||||
],
|
],
|
||||||
'currency' => [
|
'currency' => [
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
'symbol' => $journal['currency_symbol'],
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
'dp' => $journal['currency_decimal_places'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -692,9 +688,9 @@ trait AugumentData
|
|||||||
|
|
||||||
// add amount
|
// add amount
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
|
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
|
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $journal['amount']
|
||||||
);
|
);
|
||||||
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount);
|
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sum;
|
return $sum;
|
||||||
@ -714,33 +710,35 @@ trait AugumentData
|
|||||||
*/
|
*/
|
||||||
protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
||||||
{
|
{
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing);
|
$total = $assets->merge($opposing);
|
||||||
$set = $collector->getTransactions();
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($total);
|
||||||
|
$journals = $collector->getExtractedJournals();
|
||||||
$sum = [
|
$sum = [
|
||||||
'grand_sum' => '0',
|
'grand_sum' => '0',
|
||||||
'per_currency' => [],
|
'per_currency' => [],
|
||||||
];
|
];
|
||||||
// loop to support multi currency
|
// loop to support multi currency
|
||||||
foreach ($set as $transaction) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = (int)$transaction->transaction_currency_id;
|
$currencyId = (int)$journal['currency_id'];
|
||||||
|
|
||||||
// if not set, set to zero:
|
// if not set, set to zero:
|
||||||
if (!isset($sum['per_currency'][$currencyId])) {
|
if (!isset($sum['per_currency'][$currencyId])) {
|
||||||
$sum['per_currency'][$currencyId] = [
|
$sum['per_currency'][$currencyId] = [
|
||||||
'sum' => '0',
|
'sum' => '0',
|
||||||
'currency' => [
|
'currency' => [
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
'name' => $journal['currency_name'],
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
'symbol' => $journal['currency_symbol'],
|
||||||
|
'decimal_places' => $journal['currency_decimal_places'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// add amount
|
// add amount
|
||||||
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount);
|
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $journal['amount']);
|
||||||
$sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount);
|
$sum['grand_sum'] = bcadd($sum['grand_sum'], $journal['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sum;
|
return $sum;
|
||||||
@ -767,6 +765,7 @@ trait AugumentData
|
|||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$types = [TransactionType::WITHDRAWAL];
|
$types = [TransactionType::WITHDRAWAL];
|
||||||
$collector->setTypes($types)->setRange($start, $end)->withoutBudget();
|
$collector->setTypes($types)->setRange($start, $end)->withoutBudget();
|
||||||
|
|
||||||
return $collector->getSum();
|
return $collector->getSum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,8 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Support\Http\Controllers;
|
namespace FireflyIII\Support\Http\Controllers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\GroupSumCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupSumCollectorInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
|
@ -24,29 +24,18 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Support\Http\Controllers;
|
namespace FireflyIII\Support\Http\Controllers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
|
||||||
use FireflyIII\Exceptions\ValidationException;
|
use FireflyIII\Exceptions\ValidationException;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
|
||||||
use FireflyIII\Helpers\Help\HelpInterface;
|
use FireflyIII\Helpers\Help\HelpInterface;
|
||||||
use FireflyIII\Http\Requests\SplitJournalFormRequest;
|
|
||||||
use FireflyIII\Http\Requests\TestRuleFormRequest;
|
use FireflyIII\Http\Requests\TestRuleFormRequest;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionJournal;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
|
||||||
use FireflyIII\Support\Binder\AccountList;
|
use FireflyIII\Support\Binder\AccountList;
|
||||||
use FireflyIII\Transformers\TransactionTransformer;
|
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Hash;
|
use Hash;
|
||||||
use Illuminate\Contracts\Validation\Validator as ValidatorContract;
|
use Illuminate\Contracts\Validation\Validator as ValidatorContract;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Routing\Route;
|
use Illuminate\Routing\Route;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Log;
|
use Log;
|
||||||
use Route as RouteFacade;
|
use Route as RouteFacade;
|
||||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait RequestInformation
|
* Trait RequestInformation
|
||||||
@ -54,138 +43,7 @@ use Symfony\Component\HttpFoundation\ParameterBag;
|
|||||||
*/
|
*/
|
||||||
trait RequestInformation
|
trait RequestInformation
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Create data-array from a journal.
|
|
||||||
*
|
|
||||||
* @param SplitJournalFormRequest|Request $request
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
|
||||||
protected function arrayFromJournal(Request $request, TransactionJournal $journal): array // convert user input.
|
|
||||||
{
|
|
||||||
/** @var JournalRepositoryInterface $repository */
|
|
||||||
$repository = app(JournalRepositoryInterface::class);
|
|
||||||
$sourceAccounts = $repository->getJournalSourceAccounts($journal);
|
|
||||||
$destinationAccounts = $repository->getJournalDestinationAccounts($journal);
|
|
||||||
$array = [
|
|
||||||
'journal_description' => $request->old('journal_description', $journal->description),
|
|
||||||
'journal_amount' => '0',
|
|
||||||
'journal_foreign_amount' => '0',
|
|
||||||
'sourceAccounts' => $sourceAccounts,
|
|
||||||
'journal_source_id' => $request->old('journal_source_id', $sourceAccounts->first()->id),
|
|
||||||
'journal_source_name' => $request->old('journal_source_name', $sourceAccounts->first()->name),
|
|
||||||
'journal_destination_id' => $request->old('journal_destination_id', $destinationAccounts->first()->id),
|
|
||||||
'destinationAccounts' => $destinationAccounts,
|
|
||||||
'what' => strtolower($this->repository->getTransactionType($journal)),
|
|
||||||
'date' => $request->old('date', $this->repository->getJournalDate($journal, null)),
|
|
||||||
'tags' => implode(',', $journal->tags->pluck('tag')->toArray()),
|
|
||||||
|
|
||||||
// all custom fields:
|
|
||||||
'interest_date' => $request->old('interest_date', $repository->getMetaField($journal, 'interest_date')),
|
|
||||||
'book_date' => $request->old('book_date', $repository->getMetaField($journal, 'book_date')),
|
|
||||||
'process_date' => $request->old('process_date', $repository->getMetaField($journal, 'process_date')),
|
|
||||||
'due_date' => $request->old('due_date', $repository->getMetaField($journal, 'due_date')),
|
|
||||||
'payment_date' => $request->old('payment_date', $repository->getMetaField($journal, 'payment_date')),
|
|
||||||
'invoice_date' => $request->old('invoice_date', $repository->getMetaField($journal, 'invoice_date')),
|
|
||||||
'internal_reference' => $request->old('internal_reference', $repository->getMetaField($journal, 'internal_reference')),
|
|
||||||
'notes' => $request->old('notes', $repository->getNoteText($journal)),
|
|
||||||
|
|
||||||
// transactions.
|
|
||||||
'transactions' => $this->getTransactionDataFromJournal($journal),
|
|
||||||
];
|
|
||||||
// update transactions array with old request data.
|
|
||||||
$array['transactions'] = $this->updateWithPrevious($array['transactions'], $request->old());
|
|
||||||
|
|
||||||
// update journal amount and foreign amount:
|
|
||||||
$array['journal_amount'] = array_sum(array_column($array['transactions'], 'amount'));
|
|
||||||
$array['journal_foreign_amount'] = array_sum(array_column($array['transactions'], 'foreign_amount'));
|
|
||||||
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get transaction overview from journal.
|
|
||||||
*
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @throws FireflyException
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
protected function getTransactionDataFromJournal(TransactionJournal $journal): array // convert object
|
|
||||||
{
|
|
||||||
// use collector to collect transactions.
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setUser(auth()->user());
|
|
||||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
|
||||||
// filter on specific journals.
|
|
||||||
$collector->setJournals(new Collection([$journal]));
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$transactions = [];
|
|
||||||
|
|
||||||
/** @var TransactionTransformer $transformer */
|
|
||||||
$transformer = app(TransactionTransformer::class);
|
|
||||||
$transformer->setParameters(new ParameterBag());
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$res = [];
|
|
||||||
if ((float)$transaction->transaction_amount > 0 && $journal->transactionType->type === TransactionType::DEPOSIT) {
|
|
||||||
$res = $transformer->transform($transaction);
|
|
||||||
}
|
|
||||||
if ((float)$transaction->transaction_amount < 0 && $journal->transactionType->type !== TransactionType::DEPOSIT) {
|
|
||||||
$res = $transformer->transform($transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($res) > 0) {
|
|
||||||
$res['amount'] = app('steam')->positive((string)$res['amount']);
|
|
||||||
$res['foreign_amount'] = app('steam')->positive((string)$res['foreign_amount']);
|
|
||||||
$transactions[] = $res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get info from old input.
|
|
||||||
*
|
|
||||||
* @param $array
|
|
||||||
* @param $old
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
protected function updateWithPrevious($array, $old): array // update object with new info
|
|
||||||
{
|
|
||||||
if (0 === count($old) || !isset($old['transactions'])) {
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
$old = $old['transactions'];
|
|
||||||
|
|
||||||
foreach ($old as $index => $row) {
|
|
||||||
if (isset($array[$index])) {
|
|
||||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
|
||||||
$array[$index] = array_merge($array[$index], $row);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// take some info from first transaction, that should at least exist.
|
|
||||||
$array[$index] = $row;
|
|
||||||
$array[$index]['currency_id'] = $array[0]['currency_id'];
|
|
||||||
$array[$index]['currency_code'] = $array[0]['currency_code'] ?? '';
|
|
||||||
$array[$index]['currency_symbol'] = $array[0]['currency_symbol'] ?? '';
|
|
||||||
$array[$index]['foreign_amount'] = round($array[0]['foreign_destination_amount'] ?? '0', 12);
|
|
||||||
$array[$index]['foreign_currency_id'] = $array[0]['foreign_currency_id'];
|
|
||||||
$array[$index]['foreign_currency_code'] = $array[0]['foreign_currency_code'];
|
|
||||||
$array[$index]['foreign_currency_symbol'] = $array[0]['foreign_currency_symbol'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the domain of FF system.
|
* Get the domain of FF system.
|
||||||
|
@ -46,14 +46,18 @@ trait TransactionCalculation
|
|||||||
*/
|
*/
|
||||||
protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array
|
protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
|
$total = $accounts->merge($opposing);
|
||||||
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setAccounts($accounts)
|
$collector->setAccounts($total)
|
||||||
->setRange($start, $end)
|
->setRange($start, $end)
|
||||||
->setTypes([TransactionType::WITHDRAWAL])
|
->withAccountInformation()
|
||||||
->setAccounts($opposing);
|
->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
|
||||||
return $collector->getExtractedJournals();
|
$result = $collector->getExtractedJournals();
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -156,10 +160,10 @@ trait TransactionCalculation
|
|||||||
*/
|
*/
|
||||||
protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array
|
protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
|
$total =$accounts->merge($opposing);
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])
|
$collector->setAccounts($total)->setRange($start, $end)->withAccountInformation()->setTypes([TransactionType::DEPOSIT]);
|
||||||
->setAccounts($opposing);
|
|
||||||
|
|
||||||
return $collector->getExtractedJournals();
|
return $collector->getExtractedJournals();
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,12 @@ class ImportTransaction
|
|||||||
'opposing-bic' => 'opposingBic',
|
'opposing-bic' => 'opposingBic',
|
||||||
'opposing-number' => 'opposingNumber',
|
'opposing-number' => 'opposingNumber',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// overrule some old role values.
|
||||||
|
if ('original-source' === $role) {
|
||||||
|
$role = 'original_source';
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($basics[$role])) {
|
if (isset($basics[$role])) {
|
||||||
$field = $basics[$role];
|
$field = $basics[$role];
|
||||||
$this->$field = $columnValue->getValue();
|
$this->$field = $columnValue->getValue();
|
||||||
@ -229,6 +235,18 @@ class ImportTransaction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the mapped value if it exists in the ColumnValue object.
|
||||||
|
*
|
||||||
|
* @param ColumnValue $columnValue
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function getMappedValue(ColumnValue $columnValue): int
|
||||||
|
{
|
||||||
|
return $columnValue->getMappedValue() > 0 ? $columnValue->getMappedValue() : (int)$columnValue->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the amount of this transaction.
|
* Calculate the amount of this transaction.
|
||||||
*
|
*
|
||||||
@ -276,6 +294,40 @@ class ImportTransaction
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This methods decides which input value to use for the amount calculation.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function selectAmountInput(): array
|
||||||
|
{
|
||||||
|
$info = [];
|
||||||
|
$converterClass = '';
|
||||||
|
if (null !== $this->amount) {
|
||||||
|
Log::debug('Amount value is not NULL, assume this is the correct value.');
|
||||||
|
$converterClass = Amount::class;
|
||||||
|
$info['amount'] = $this->amount;
|
||||||
|
}
|
||||||
|
if (null !== $this->amountDebit) {
|
||||||
|
Log::debug('Amount DEBIT value is not NULL, assume this is the correct value (overrules Amount).');
|
||||||
|
$converterClass = AmountDebit::class;
|
||||||
|
$info['amount'] = $this->amountDebit;
|
||||||
|
}
|
||||||
|
if (null !== $this->amountCredit) {
|
||||||
|
Log::debug('Amount CREDIT value is not NULL, assume this is the correct value (overrules Amount and AmountDebit).');
|
||||||
|
$converterClass = AmountCredit::class;
|
||||||
|
$info['amount'] = $this->amountCredit;
|
||||||
|
}
|
||||||
|
if (null !== $this->amountNegated) {
|
||||||
|
Log::debug('Amount NEGATED value is not NULL, assume this is the correct value (overrules Amount and AmountDebit and AmountCredit).');
|
||||||
|
$converterClass = AmountNegated::class;
|
||||||
|
$info['amount'] = $this->amountNegated;
|
||||||
|
}
|
||||||
|
$info['class'] = $converterClass;
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method that calculates the foreign amount isn't nearly as complex,\
|
* The method that calculates the foreign amount isn't nearly as complex,\
|
||||||
* because Firefly III only supports one foreign amount field. So the foreign amount is there
|
* because Firefly III only supports one foreign amount field. So the foreign amount is there
|
||||||
@ -372,50 +424,4 @@ class ImportTransaction
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the mapped value if it exists in the ColumnValue object.
|
|
||||||
*
|
|
||||||
* @param ColumnValue $columnValue
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function getMappedValue(ColumnValue $columnValue): int
|
|
||||||
{
|
|
||||||
return $columnValue->getMappedValue() > 0 ? $columnValue->getMappedValue() : (int)$columnValue->getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This methods decides which input value to use for the amount calculation.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function selectAmountInput(): array
|
|
||||||
{
|
|
||||||
$info = [];
|
|
||||||
$converterClass = '';
|
|
||||||
if (null !== $this->amount) {
|
|
||||||
Log::debug('Amount value is not NULL, assume this is the correct value.');
|
|
||||||
$converterClass = Amount::class;
|
|
||||||
$info['amount'] = $this->amount;
|
|
||||||
}
|
|
||||||
if (null !== $this->amountDebit) {
|
|
||||||
Log::debug('Amount DEBIT value is not NULL, assume this is the correct value (overrules Amount).');
|
|
||||||
$converterClass = AmountDebit::class;
|
|
||||||
$info['amount'] = $this->amountDebit;
|
|
||||||
}
|
|
||||||
if (null !== $this->amountCredit) {
|
|
||||||
Log::debug('Amount CREDIT value is not NULL, assume this is the correct value (overrules Amount and AmountDebit).');
|
|
||||||
$converterClass = AmountCredit::class;
|
|
||||||
$info['amount'] = $this->amountCredit;
|
|
||||||
}
|
|
||||||
if (null !== $this->amountNegated) {
|
|
||||||
Log::debug('Amount NEGATED value is not NULL, assume this is the correct value (overrules Amount and AmountDebit and AmountCredit).');
|
|
||||||
$converterClass = AmountNegated::class;
|
|
||||||
$info['amount'] = $this->amountNegated;
|
|
||||||
}
|
|
||||||
$info['class'] = $converterClass;
|
|
||||||
|
|
||||||
return $info;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -89,79 +89,11 @@ class ImportableConverter
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ImportJob $importJob
|
|
||||||
*/
|
|
||||||
public function setImportJob(ImportJob $importJob): void
|
|
||||||
{
|
|
||||||
$this->importJob = $importJob;
|
|
||||||
$this->config = $importJob->configuration;
|
|
||||||
|
|
||||||
// repository is used for error messages
|
|
||||||
$this->repository = app(ImportJobRepositoryInterface::class);
|
|
||||||
$this->repository->setUser($importJob->user);
|
|
||||||
|
|
||||||
// asset account mapper can map asset accounts (makes sense right?)
|
|
||||||
$this->assetMapper = app(AssetAccountMapper::class);
|
|
||||||
$this->assetMapper->setUser($importJob->user);
|
|
||||||
$this->assetMapper->setDefaultAccount($this->config['import-account'] ?? 0);
|
|
||||||
|
|
||||||
// asset account repository is used for currency information
|
|
||||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
|
||||||
$this->accountRepository->setUser($importJob->user);
|
|
||||||
|
|
||||||
// opposing account mapper:
|
|
||||||
$this->opposingMapper = app(OpposingAccountMapper::class);
|
|
||||||
$this->opposingMapper->setUser($importJob->user);
|
|
||||||
|
|
||||||
// currency mapper:
|
|
||||||
$this->currencyMapper = app(CurrencyMapper::class);
|
|
||||||
$this->currencyMapper->setUser($importJob->user);
|
|
||||||
$this->defaultCurrency = app('amount')->getDefaultCurrencyByUser($importJob->user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param array $mappedValues
|
|
||||||
*/
|
|
||||||
public function setMappedValues(array $mappedValues): void
|
|
||||||
{
|
|
||||||
$this->mappedValues = $mappedValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|null $date
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
private function convertDateValue(string $date = null): ?string
|
|
||||||
{
|
|
||||||
$result = null;
|
|
||||||
if (null !== $date) {
|
|
||||||
try {
|
|
||||||
// add exclamation mark for better parsing. http://php.net/manual/en/datetime.createfromformat.php
|
|
||||||
$dateFormat = $this->config['date-format'] ?? 'Ymd';
|
|
||||||
if ('!' !== $dateFormat{0}) {
|
|
||||||
$dateFormat = '!' . $dateFormat;
|
|
||||||
}
|
|
||||||
$object = Carbon::createFromFormat($dateFormat, $date);
|
|
||||||
$result = $object->format('Y-m-d H:i:s');
|
|
||||||
Log::debug(sprintf('createFromFormat: Turning "%s" into "%s" using "%s"', $date, $result, $this->config['date-format'] ?? 'Ymd'));
|
|
||||||
} catch (InvalidDateException|InvalidArgumentException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ImportTransaction $importable
|
* @param ImportTransaction $importable
|
||||||
*
|
*
|
||||||
* @throws FireflyException
|
|
||||||
* @return array
|
* @return array
|
||||||
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
private function convertSingle(ImportTransaction $importable): array
|
private function convertSingle(ImportTransaction $importable): array
|
||||||
{
|
{
|
||||||
@ -218,14 +150,53 @@ class ImportableConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
|
'user' => $this->importJob->user_id,
|
||||||
|
'group_title' => null,
|
||||||
|
'transactions' => [
|
||||||
|
[
|
||||||
|
'user' => $this->importJob->user_id,
|
||||||
'type' => $transactionType,
|
'type' => $transactionType,
|
||||||
'date' => $this->convertDateValue($importable->date) ?? Carbon::now()->format('Y-m-d H:i:s'),
|
'date' => $this->convertDateValue($importable->date) ?? Carbon::now()->format('Y-m-d H:i:s'),
|
||||||
'tags' => $importable->tags,
|
'order' => 0,
|
||||||
'user' => $this->importJob->user_id,
|
|
||||||
'notes' => $importable->note,
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => null,
|
||||||
|
|
||||||
|
'foreign_currency_id' => $importable->foreignCurrencyId,
|
||||||
|
'foreign_currency_code' => null === $foreignCurrency ? null : $foreignCurrency->code,
|
||||||
|
|
||||||
|
'amount' => $amount,
|
||||||
|
'foreign_amount' => $foreignAmount,
|
||||||
|
|
||||||
|
'description' => $importable->description,
|
||||||
|
|
||||||
|
'source_id' => $source->id,
|
||||||
|
'source_name' => null,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_name' => null,
|
||||||
|
|
||||||
|
'budget_id' => $importable->budgetId,
|
||||||
|
'budget_name' => $importable->budgetName,
|
||||||
|
|
||||||
|
'category_id' => $importable->categoryId,
|
||||||
|
'category_name' => $importable->categoryName,
|
||||||
|
|
||||||
|
'bill_id' => $importable->billId,
|
||||||
|
'bill_name' => $importable->billName,
|
||||||
|
|
||||||
|
'piggy_bank_id' => null,
|
||||||
|
'piggy_bank_name' => null,
|
||||||
|
|
||||||
|
'reconciled' => false,
|
||||||
|
|
||||||
|
'notes' => $importable->note,
|
||||||
|
'tags' => $importable->tags,
|
||||||
|
|
||||||
// all custom fields:
|
|
||||||
'internal_reference' => $importable->meta['internal-reference'] ?? null,
|
'internal_reference' => $importable->meta['internal-reference'] ?? null,
|
||||||
|
'external_id' => $importable->externalId,
|
||||||
|
'original_source' => $importable->meta['original-source'] ?? null,
|
||||||
|
|
||||||
'sepa_cc' => $importable->meta['sepa_cc'] ?? null,
|
'sepa_cc' => $importable->meta['sepa_cc'] ?? null,
|
||||||
'sepa_ct_op' => $importable->meta['sepa_ct_op'] ?? null,
|
'sepa_ct_op' => $importable->meta['sepa_ct_op'] ?? null,
|
||||||
'sepa_ct_id' => $importable->meta['sepa_ct_id'] ?? null,
|
'sepa_ct_id' => $importable->meta['sepa_ct_id'] ?? null,
|
||||||
@ -234,44 +205,43 @@ class ImportableConverter
|
|||||||
'sepa_ep' => $importable->meta['sepa_ep'] ?? null,
|
'sepa_ep' => $importable->meta['sepa_ep'] ?? null,
|
||||||
'sepa_ci' => $importable->meta['sepa_ci'] ?? null,
|
'sepa_ci' => $importable->meta['sepa_ci'] ?? null,
|
||||||
'sepa_batch_id' => $importable->meta['sepa_batch_id'] ?? null,
|
'sepa_batch_id' => $importable->meta['sepa_batch_id'] ?? null,
|
||||||
|
|
||||||
'interest_date' => $this->convertDateValue($importable->meta['date-interest'] ?? null),
|
'interest_date' => $this->convertDateValue($importable->meta['date-interest'] ?? null),
|
||||||
'book_date' => $this->convertDateValue($importable->meta['date-book'] ?? null),
|
'book_date' => $this->convertDateValue($importable->meta['date-book'] ?? null),
|
||||||
'process_date' => $this->convertDateValue($importable->meta['date-process'] ?? null),
|
'process_date' => $this->convertDateValue($importable->meta['date-process'] ?? null),
|
||||||
'due_date' => $this->convertDateValue($importable->meta['date-due'] ?? null),
|
'due_date' => $this->convertDateValue($importable->meta['date-due'] ?? null),
|
||||||
'payment_date' => $this->convertDateValue($importable->meta['date-payment'] ?? null),
|
'payment_date' => $this->convertDateValue($importable->meta['date-payment'] ?? null),
|
||||||
'invoice_date' => $this->convertDateValue($importable->meta['date-invoice'] ?? null),
|
'invoice_date' => $this->convertDateValue($importable->meta['date-invoice'] ?? null),
|
||||||
'external_id' => $importable->externalId,
|
|
||||||
'original-source' => $importable->meta['original-source'] ?? null,
|
|
||||||
// journal data:
|
|
||||||
'description' => $importable->description,
|
|
||||||
'piggy_bank_id' => null,
|
|
||||||
'piggy_bank_name' => null,
|
|
||||||
'bill_id' => $importable->billId,
|
|
||||||
'bill_name' => $importable->billName,
|
|
||||||
|
|
||||||
// transaction data:
|
|
||||||
'transactions' => [
|
|
||||||
[
|
|
||||||
'currency_id' => $currency->id,
|
|
||||||
'currency_code' => null,
|
|
||||||
'description' => null,
|
|
||||||
'amount' => $amount,
|
|
||||||
'budget_id' => $importable->budgetId,
|
|
||||||
'budget_name' => $importable->budgetName,
|
|
||||||
'category_id' => $importable->categoryId,
|
|
||||||
'category_name' => $importable->categoryName,
|
|
||||||
'source_id' => $source->id,
|
|
||||||
'source_name' => null,
|
|
||||||
'destination_id' => $destination->id,
|
|
||||||
'destination_name' => null,
|
|
||||||
'foreign_currency_id' => $importable->foreignCurrencyId,
|
|
||||||
'foreign_currency_code' => null === $foreignCurrency ? null : $foreignCurrency->code,
|
|
||||||
'foreign_amount' => $foreignAmount,
|
|
||||||
'reconciled' => false,
|
|
||||||
'identifier' => 0,
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $source
|
||||||
|
* @param string $destination
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getTransactionType(string $source, string $destination): string
|
||||||
|
{
|
||||||
|
$type = 'unknown';
|
||||||
|
|
||||||
|
if ($source === AccountType::ASSET && $destination === AccountType::ASSET) {
|
||||||
|
Log::debug('Source and destination are asset accounts. This is a transfer.');
|
||||||
|
$type = 'transfer';
|
||||||
|
}
|
||||||
|
if ($source === AccountType::REVENUE) {
|
||||||
|
Log::debug('Source is a revenue account. This is a deposit.');
|
||||||
|
$type = 'deposit';
|
||||||
|
}
|
||||||
|
if ($destination === AccountType::EXPENSE) {
|
||||||
|
Log::debug('Destination is an expense account. This is a withdrawal.');
|
||||||
|
$type = 'withdrawal';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -305,28 +275,70 @@ class ImportableConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $source
|
* @param string|null $date
|
||||||
* @param string $destination
|
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private function getTransactionType(string $source, string $destination): string
|
private function convertDateValue(string $date = null): ?string
|
||||||
{
|
{
|
||||||
$type = 'unknown';
|
$result = null;
|
||||||
|
if (null !== $date) {
|
||||||
if ($source === AccountType::ASSET && $destination === AccountType::ASSET) {
|
try {
|
||||||
Log::debug('Source and destination are asset accounts. This is a transfer.');
|
// add exclamation mark for better parsing. http://php.net/manual/en/datetime.createfromformat.php
|
||||||
$type = 'transfer';
|
$dateFormat = $this->config['date-format'] ?? 'Ymd';
|
||||||
|
if ('!' !== $dateFormat{0}) {
|
||||||
|
$dateFormat = '!' . $dateFormat;
|
||||||
}
|
}
|
||||||
if ($source === AccountType::REVENUE) {
|
$object = Carbon::createFromFormat($dateFormat, $date);
|
||||||
Log::debug('Source is a revenue account. This is a deposit.');
|
$result = $object->format('Y-m-d H:i:s');
|
||||||
$type = 'deposit';
|
Log::debug(sprintf('createFromFormat: Turning "%s" into "%s" using "%s"', $date, $result, $this->config['date-format'] ?? 'Ymd'));
|
||||||
|
} catch (InvalidDateException|InvalidArgumentException $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
Log::error($e->getTraceAsString());
|
||||||
}
|
}
|
||||||
if ($destination === AccountType::EXPENSE) {
|
|
||||||
Log::debug('Destination is an expense account. This is a withdrawal.');
|
|
||||||
$type = 'withdrawal';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $type;
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ImportJob $importJob
|
||||||
|
*/
|
||||||
|
public function setImportJob(ImportJob $importJob): void
|
||||||
|
{
|
||||||
|
$this->importJob = $importJob;
|
||||||
|
$this->config = $importJob->configuration;
|
||||||
|
|
||||||
|
// repository is used for error messages
|
||||||
|
$this->repository = app(ImportJobRepositoryInterface::class);
|
||||||
|
$this->repository->setUser($importJob->user);
|
||||||
|
|
||||||
|
// asset account mapper can map asset accounts (makes sense right?)
|
||||||
|
$this->assetMapper = app(AssetAccountMapper::class);
|
||||||
|
$this->assetMapper->setUser($importJob->user);
|
||||||
|
$this->assetMapper->setDefaultAccount($this->config['import-account'] ?? 0);
|
||||||
|
|
||||||
|
// asset account repository is used for currency information
|
||||||
|
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||||
|
$this->accountRepository->setUser($importJob->user);
|
||||||
|
|
||||||
|
// opposing account mapper:
|
||||||
|
$this->opposingMapper = app(OpposingAccountMapper::class);
|
||||||
|
$this->opposingMapper->setUser($importJob->user);
|
||||||
|
|
||||||
|
// currency mapper:
|
||||||
|
$this->currencyMapper = app(CurrencyMapper::class);
|
||||||
|
$this->currencyMapper->setUser($importJob->user);
|
||||||
|
$this->defaultCurrency = app('amount')->getDefaultCurrencyByUser($importJob->user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
|
* @param array $mappedValues
|
||||||
|
*/
|
||||||
|
public function setMappedValues(array $mappedValues): void
|
||||||
|
{
|
||||||
|
$this->mappedValues = $mappedValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Support\Search;
|
namespace FireflyIII\Support\Search;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\DoubleTransactionFilter;
|
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
@ -132,6 +130,22 @@ class Search implements SearchInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $string
|
||||||
|
*/
|
||||||
|
private function extractModifier(string $string): void
|
||||||
|
{
|
||||||
|
$parts = explode(':', $string);
|
||||||
|
if (2 === count($parts) && '' !== trim((string)$parts[1]) && '' !== trim((string)$parts[0])) {
|
||||||
|
$type = trim((string)$parts[0]);
|
||||||
|
$value = trim((string)$parts[1]);
|
||||||
|
if (\in_array($type, $this->validModifiers, true)) {
|
||||||
|
// filter for valid type
|
||||||
|
$this->modifiers->push(['type' => $type, 'value' => $value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
@ -149,16 +163,13 @@ class Search implements SearchInterface
|
|||||||
$pageSize = 50;
|
$pageSize = 50;
|
||||||
$page = 1;
|
$page = 1;
|
||||||
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->withOpposingAccount();
|
|
||||||
if ($this->hasModifiers()) {
|
$collector->setLimit($pageSize)->setPage($page)->withAccountInformation();
|
||||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
$collector->withCategoryInformation()->withBudgetInformation();
|
||||||
}
|
|
||||||
|
|
||||||
$collector->setSearchWords($this->words);
|
$collector->setSearchWords($this->words);
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
$collector->addFilter(DoubleTransactionFilter::class);
|
|
||||||
|
|
||||||
// Most modifiers can be applied to the collector directly.
|
// Most modifiers can be applied to the collector directly.
|
||||||
$collector = $this->applyModifiers($collector);
|
$collector = $this->applyModifiers($collector);
|
||||||
@ -168,37 +179,18 @@ class Search implements SearchInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $limit
|
* @param GroupCollectorInterface $collector
|
||||||
*/
|
|
||||||
public function setLimit(int $limit): void
|
|
||||||
{
|
|
||||||
$this->limit = $limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param User $user
|
|
||||||
*/
|
|
||||||
public function setUser(User $user): void
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->accountRepository->setUser($user);
|
|
||||||
$this->billRepository->setUser($user);
|
|
||||||
$this->categoryRepository->setUser($user);
|
|
||||||
$this->budgetRepository->setUser($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param TransactionCollectorInterface $collector
|
|
||||||
*
|
*
|
||||||
* @return TransactionCollectorInterface
|
* @return GroupCollectorInterface
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||||
*/
|
*/
|
||||||
private function applyModifiers(TransactionCollectorInterface $collector): TransactionCollectorInterface
|
private function applyModifiers(GroupCollectorInterface $collector): GroupCollectorInterface
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
* 'bill',
|
* 'bill',
|
||||||
*/
|
*/
|
||||||
|
$totalAccounts = new Collection;
|
||||||
|
|
||||||
foreach ($this->modifiers as $modifier) {
|
foreach ($this->modifiers as $modifier) {
|
||||||
switch ($modifier['type']) {
|
switch ($modifier['type']) {
|
||||||
@ -209,7 +201,7 @@ class Search implements SearchInterface
|
|||||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
||||||
$accounts = $this->accountRepository->searchAccount($modifier['value'], $searchTypes);
|
$accounts = $this->accountRepository->searchAccount($modifier['value'], $searchTypes);
|
||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$totalAccounts = $accounts->merge($totalAccounts);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'destination':
|
case 'destination':
|
||||||
@ -217,7 +209,7 @@ class Search implements SearchInterface
|
|||||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE];
|
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE];
|
||||||
$accounts = $this->accountRepository->searchAccount($modifier['value'], $searchTypes);
|
$accounts = $this->accountRepository->searchAccount($modifier['value'], $searchTypes);
|
||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setOpposingAccounts($accounts);
|
$totalAccounts = $accounts->merge($totalAccounts);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'category':
|
case 'category':
|
||||||
@ -280,23 +272,28 @@ class Search implements SearchInterface
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$collector->setAccounts($totalAccounts);
|
||||||
|
|
||||||
return $collector;
|
return $collector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $string
|
* @param int $limit
|
||||||
*/
|
*/
|
||||||
private function extractModifier(string $string): void
|
public function setLimit(int $limit): void
|
||||||
{
|
{
|
||||||
$parts = explode(':', $string);
|
$this->limit = $limit;
|
||||||
if (2 === count($parts) && '' !== trim((string)$parts[1]) && '' !== trim((string)$parts[0])) {
|
}
|
||||||
$type = trim((string)$parts[0]);
|
|
||||||
$value = trim((string)$parts[1]);
|
/**
|
||||||
if (\in_array($type, $this->validModifiers, true)) {
|
* @param User $user
|
||||||
// filter for valid type
|
*/
|
||||||
$this->modifiers->push(['type' => $type, 'value' => $value]);
|
public function setUser(User $user): void
|
||||||
}
|
{
|
||||||
}
|
$this->user = $user;
|
||||||
|
$this->accountRepository->setUser($user);
|
||||||
|
$this->billRepository->setUser($user);
|
||||||
|
$this->categoryRepository->setUser($user);
|
||||||
|
$this->budgetRepository->setUser($user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,11 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\TransactionRules;
|
namespace FireflyIII\TransactionRules;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|
||||||
use FireflyIII\Models\Rule;
|
use FireflyIII\Models\Rule;
|
||||||
use FireflyIII\Models\RuleTrigger;
|
use FireflyIII\Models\RuleTrigger;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
@ -78,16 +77,16 @@ class TransactionMatcher
|
|||||||
* transaction journals matching the given rule. This is accomplished by trying to fire these
|
* transaction journals matching the given rule. This is accomplished by trying to fire these
|
||||||
* triggers onto each transaction journal until enough matches are found ($limit).
|
* triggers onto each transaction journal until enough matches are found ($limit).
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return array
|
||||||
* @throws \FireflyIII\Exceptions\FireflyException
|
* @throws \FireflyIII\Exceptions\FireflyException
|
||||||
*/
|
*/
|
||||||
public function findTransactionsByRule(): Collection
|
public function findTransactionsByRule(): array
|
||||||
{
|
{
|
||||||
Log::debug('Now in findTransactionsByRule()');
|
Log::debug('Now in findTransactionsByRule()');
|
||||||
if (0 === count($this->rule->ruleTriggers)) {
|
if (0 === count($this->rule->ruleTriggers)) {
|
||||||
Log::error('Rule has no triggers!');
|
Log::error('Rule has no triggers!');
|
||||||
|
|
||||||
return new Collection;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variables used within the loop.
|
// Variables used within the loop.
|
||||||
@ -98,7 +97,7 @@ class TransactionMatcher
|
|||||||
|
|
||||||
// If the list of matchingTransactions is larger than the maximum number of results
|
// If the list of matchingTransactions is larger than the maximum number of results
|
||||||
// (e.g. if a large percentage of the transactions match), truncate the list
|
// (e.g. if a large percentage of the transactions match), truncate the list
|
||||||
$result = $result->slice(0, $this->searchLimit);
|
$result = array_slice($result, 0, $this->searchLimit);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@ -254,7 +253,7 @@ class TransactionMatcher
|
|||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function runProcessor(Processor $processor): Collection
|
private function runProcessor(Processor $processor): array
|
||||||
{
|
{
|
||||||
Log::debug('Now in runprocessor()');
|
Log::debug('Now in runprocessor()');
|
||||||
// since we have a rule in $this->rule, we can add some of the triggers
|
// since we have a rule in $this->rule, we can add some of the triggers
|
||||||
@ -269,22 +268,23 @@ class TransactionMatcher
|
|||||||
$pageSize = min($this->searchLimit, min($this->triggeredLimit, 50));
|
$pageSize = min($this->searchLimit, min($this->triggeredLimit, 50));
|
||||||
$processed = 0;
|
$processed = 0;
|
||||||
$page = 1;
|
$page = 1;
|
||||||
$result = new Collection;
|
$result = [];
|
||||||
|
|
||||||
Log::debug(sprintf('Search limit is %d, triggered limit is %d, so page size is %d', $this->searchLimit, $this->triggeredLimit, $pageSize));
|
Log::debug(sprintf('Search limit is %d, triggered limit is %d, so page size is %d', $this->searchLimit, $this->triggeredLimit, $pageSize));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
Log::debug('Start of do-loop');
|
Log::debug('Start of do-loop');
|
||||||
// Fetch a batch of transactions from the database
|
// Fetch a batch of transactions from the database
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector->setUser(auth()->user());
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->withOpposingAccount();
|
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$collector->setUser($user);
|
||||||
|
|
||||||
// limit asset accounts:
|
// limit asset accounts:
|
||||||
if (0 === $this->accounts->count()) {
|
|
||||||
$collector->setAllAssetAccounts();
|
|
||||||
}
|
|
||||||
if ($this->accounts->count() > 0) {
|
if ($this->accounts->count() > 0) {
|
||||||
$collector->setAccounts($this->accounts);
|
$collector->setAccounts($this->accounts);
|
||||||
}
|
}
|
||||||
@ -305,36 +305,35 @@ class TransactionMatcher
|
|||||||
Log::debug(sprintf('Amount must be exactly %s', $this->exactAmount));
|
Log::debug(sprintf('Amount must be exactly %s', $this->exactAmount));
|
||||||
$collector->amountIs($this->exactAmount);
|
$collector->amountIs($this->exactAmount);
|
||||||
}
|
}
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
|
|
||||||
$set = $collector->getPaginatedTransactions();
|
$journals = $collector->getExtractedJournals();
|
||||||
Log::debug(sprintf('Found %d journals to check. ', $set->count()));
|
Log::debug(sprintf('Found %d transaction journals to check. ', count($journals)));
|
||||||
|
|
||||||
// Filter transactions that match the given triggers.
|
// Filter transactions that match the given triggers.
|
||||||
$filtered = $set->filter(
|
$filtered = [];
|
||||||
function (Transaction $transaction) use ($processor) {
|
/** @var array $journal */
|
||||||
Log::debug(sprintf('Test the triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id));
|
foreach ($journals as $journal) {
|
||||||
|
$result = $processor->handleJournalArray($journal);
|
||||||
return $processor->handleTransaction($transaction);
|
if ($result) {
|
||||||
|
$filtered[] = $journal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
Log::debug(sprintf('Found %d journals that match.', $filtered->count()));
|
Log::debug(sprintf('Found %d journals that match.', count($filtered)));
|
||||||
|
|
||||||
// merge:
|
// merge:
|
||||||
/** @var Collection $result */
|
$result = $result + $filtered;
|
||||||
$result = $result->merge($filtered);
|
Log::debug(sprintf('Total count is now %d', count($result)));
|
||||||
Log::debug(sprintf('Total count is now %d', $result->count()));
|
|
||||||
|
|
||||||
// Update counters
|
// Update counters
|
||||||
++$page;
|
++$page;
|
||||||
$processed += count($set);
|
$processed += count($journals);
|
||||||
|
|
||||||
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
|
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
|
||||||
|
|
||||||
// Check for conditions to finish the loop
|
// Check for conditions to finish the loop
|
||||||
$reachedEndOfList = $set->count() < 1;
|
$reachedEndOfList = count($journals) < 1;
|
||||||
$foundEnough = $result->count() >= $this->triggeredLimit;
|
$foundEnough = count($result) >= $this->triggeredLimit;
|
||||||
$searchedEnough = ($processed >= $this->searchLimit);
|
$searchedEnough = ($processed >= $this->searchLimit);
|
||||||
|
|
||||||
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
|
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
|
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
|
||||||
You can find it in your Firefly III installation:<br />
|
You can find it in your Firefly III installation:<br />
|
||||||
{% for journal in journals %}
|
{% for journal in journals %}
|
||||||
<a href="{{ route('transactions.show', journal.id) }}">{{ journal.description }}</a> ({{ journal|journalTotalAmount }})
|
<a href="{{ route('transactions.show', journal.id) }}">{{ journal.description }}</a> (TODO)
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{% for journal in journals %}
|
{% for journal in journals %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ route('transactions.show', journal.id) }}">{{ journal.description }}</a> ({{ journal|journalTotalAmount }})
|
<a href="{{ route('transactions.show', journal.id) }}">{{ journal.description }}</a> (TODO)
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -10,7 +10,7 @@ Firefly III has created {{ journals.count }} transactions for you.
|
|||||||
You can find in in your Firefly III installation:
|
You can find in in your Firefly III installation:
|
||||||
|
|
||||||
{% for journal in journals %}
|
{% for journal in journals %}
|
||||||
{{ journal.description }}: {{ route('transactions.show', journal.id) }} ({{ journal|journalTotalAmountPlain }})
|
{{ journal.description }}: {{ route('transactions.show', journal.id) }} (TODO)
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ You can find in in your Firefly III installation:
|
|||||||
You can find them in your Firefly III installation:
|
You can find them in your Firefly III installation:
|
||||||
|
|
||||||
{% for journal in journals %}
|
{% for journal in journals %}
|
||||||
- {{ journal.description }}: {{ route('transactions.show', journal.id) }} ({{ journal|journalTotalAmountPlain }})
|
- {{ journal.description }}: {{ route('transactions.show', journal.id) }} (TODO)
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user