mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
make sure reports work as expected.
This commit is contained in:
@@ -24,11 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Helpers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
||||
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@@ -48,9 +44,9 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
/** @var array The ways to group transactions, given the type of chart. */
|
||||
static protected $grouping
|
||||
= [
|
||||
'account' => ['opposing_account_id'],
|
||||
'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'],
|
||||
'category' => ['transaction_journal_category_id', 'transaction_category_id'],
|
||||
'account' => ['destination_account_id'],
|
||||
'budget' => ['budget_id'],
|
||||
'category' => ['category_id'],
|
||||
'tag' => [],
|
||||
];
|
||||
/** @var Collection Involved accounts. */
|
||||
@@ -114,25 +110,29 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
|
||||
// also collect all other transactions
|
||||
if ($this->collectOtherObjects && 'expense' === $direction) {
|
||||
/** @var TransactionCollectorInterface $collector */
|
||||
$collector = app(TransactionCollectorInterface::class);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
$collector->setUser($this->user);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
|
||||
$journals = $collector->getTransactions();
|
||||
$sum = (string)$journals->sum('transaction_amount');
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$sum = $this->sumJournals($journals);
|
||||
$sum = bcmul($sum, '-1');
|
||||
$sum = bcsub($sum, $this->total);
|
||||
$chartData[$key] = $sum;
|
||||
}
|
||||
|
||||
if ($this->collectOtherObjects && 'income' === $direction) {
|
||||
/** @var TransactionCollectorInterface $collector */
|
||||
$collector = app(TransactionCollectorInterface::class);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
$collector->setUser($this->user);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]);
|
||||
$journals = $collector->getTransactions();
|
||||
$sum = (string)$journals->sum('transaction_amount');
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$sum = $this->sumJournals($journals);
|
||||
$sum = bcsub($sum, $this->total);
|
||||
$chartData[$key] = $sum;
|
||||
}
|
||||
@@ -140,6 +140,152 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transactions.
|
||||
*
|
||||
* @param string $direction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTransactions(string $direction): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||
if ('expense' === $direction) {
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
}
|
||||
|
||||
$collector->setUser($this->user);
|
||||
$collector->setAccounts($this->accounts);
|
||||
$collector->setRange($this->start, $this->end);
|
||||
$collector->setTypes($types);
|
||||
$collector->withAccountInformation();
|
||||
|
||||
$collector->setBudgets($this->budgets);
|
||||
$collector->setCategories($this->categories);
|
||||
|
||||
$collector->withCategoryInformation();
|
||||
$collector->withBudgetInformation();
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->tags->count() > 0) {
|
||||
$collector->setTags($this->tags);
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group by a specific field.
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $fields
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*
|
||||
*/
|
||||
protected function groupByFields(array $array, array $fields): array
|
||||
{
|
||||
$grouped = [];
|
||||
if (0 === count($fields) && $this->tags->count() > 0) {
|
||||
// do a special group on tags:
|
||||
$grouped = $this->groupByTag($array); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (0 !== count($fields) || $this->tags->count() <= 0) {
|
||||
$grouped = [];
|
||||
/** @var array $journal */
|
||||
foreach ($array as $journal) {
|
||||
$values = [];
|
||||
foreach ($fields as $field) {
|
||||
$values[] = (int)$journal[$field];
|
||||
}
|
||||
$value = max($values);
|
||||
$grouped[$value] = $grouped[$value] ?? '0';
|
||||
$grouped[$value] = bcadd($journal['amount'], $grouped[$value]);
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group by tag (slightly different).
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function groupByTag(Collection $set): array
|
||||
{
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$journal = $transaction->transactionJournal;
|
||||
$tags = $journal->tags;
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
$tagId = $tag->id;
|
||||
$grouped[$tagId] = $grouped[$tagId] ?? '0';
|
||||
$grouped[$tagId] = bcadd($transaction->transaction_amount, $grouped[$tagId]);
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Organise by certain type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function organizeByType(string $type, array $array): array
|
||||
{
|
||||
$chartData = [];
|
||||
$names = [];
|
||||
$repository = app($this->repositories[$type]);
|
||||
$repository->setUser($this->user);
|
||||
foreach ($array as $objectId => $amount) {
|
||||
if (!isset($names[$objectId])) {
|
||||
$object = $repository->findNull((int)$objectId);
|
||||
$name = null === $object ? '(no name)' : $object->name;
|
||||
$names[$objectId] = $name ?? $object->tag;
|
||||
}
|
||||
$amount = app('steam')->positive($amount);
|
||||
$this->total = bcadd($this->total, $amount);
|
||||
$chartData[$names[$objectId]] = $amount;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $journals
|
||||
* @return string
|
||||
*/
|
||||
private function sumJournals(array $journals): string
|
||||
{
|
||||
$sum = '0';
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$amount = (string)$journal['amount'];
|
||||
$sum = bcadd($sum, $amount);
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accounts setter.
|
||||
*
|
||||
@@ -267,140 +413,4 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transactions.
|
||||
*
|
||||
* @param string $direction
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getTransactions(string $direction): Collection
|
||||
{
|
||||
/** @var TransactionCollectorInterface $collector */
|
||||
$collector = app(TransactionCollectorInterface::class);
|
||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||
$collector->addFilter(NegativeAmountFilter::class);
|
||||
if ('expense' === $direction) {
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$collector->addFilter(PositiveAmountFilter::class);
|
||||
$collector->removeFilter(NegativeAmountFilter::class);
|
||||
}
|
||||
|
||||
$collector->setUser($this->user);
|
||||
$collector->setAccounts($this->accounts);
|
||||
$collector->setRange($this->start, $this->end);
|
||||
$collector->setTypes($types);
|
||||
$collector->withOpposingAccount();
|
||||
$collector->addFilter(OpposingAccountFilter::class);
|
||||
|
||||
if ('income' === $direction) {
|
||||
$collector->removeFilter(TransferFilter::class);
|
||||
}
|
||||
|
||||
$collector->setBudgets($this->budgets);
|
||||
$collector->setCategories($this->categories);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->tags->count() > 0) {
|
||||
$collector->setTags($this->tags);
|
||||
$collector->withCategoryInformation();
|
||||
$collector->withBudgetInformation();
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $collector->getTransactions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group by a specific field.
|
||||
*
|
||||
* @param Collection $set
|
||||
* @param array $fields
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*
|
||||
*/
|
||||
protected function groupByFields(Collection $set, array $fields): array
|
||||
{
|
||||
$grouped = [];
|
||||
if (0 === \count($fields) && $this->tags->count() > 0) {
|
||||
// do a special group on tags:
|
||||
$grouped = $this->groupByTag($set); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (0 !== \count($fields) || $this->tags->count() <= 0) {
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$values = [];
|
||||
foreach ($fields as $field) {
|
||||
$values[] = (int)$transaction->$field;
|
||||
}
|
||||
$value = max($values);
|
||||
$grouped[$value] = $grouped[$value] ?? '0';
|
||||
$grouped[$value] = bcadd($transaction->transaction_amount, $grouped[$value]);
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Organise by certain type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function organizeByType(string $type, array $array): array
|
||||
{
|
||||
$chartData = [];
|
||||
$names = [];
|
||||
$repository = app($this->repositories[$type]);
|
||||
$repository->setUser($this->user);
|
||||
foreach ($array as $objectId => $amount) {
|
||||
if (!isset($names[$objectId])) {
|
||||
$object = $repository->findNull((int)$objectId);
|
||||
$name = null === $object ? '(no name)' : $object->name;
|
||||
$names[$objectId] = $name ?? $object->tag;
|
||||
}
|
||||
$amount = app('steam')->positive($amount);
|
||||
$this->total = bcadd($this->total, $amount);
|
||||
$chartData[$names[$objectId]] = $amount;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group by tag (slightly different).
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function groupByTag(Collection $set): array
|
||||
{
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$journal = $transaction->transactionJournal;
|
||||
$tags = $journal->tags;
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
$tagId = $tag->id;
|
||||
$grouped[$tagId] = $grouped[$tagId] ?? '0';
|
||||
$grouped[$tagId] = bcadd($transaction->transaction_amount, $grouped[$tagId]);
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidDateException;
|
||||
use Exception;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
@@ -37,8 +38,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class GroupCollector
|
||||
@@ -226,16 +226,28 @@ class GroupCollector implements GroupCollectorInterface
|
||||
{
|
||||
$result = $augmentedGroup->toArray();
|
||||
$result['tags'] = [];
|
||||
$result['tag_ids'] = [];
|
||||
$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'])) {
|
||||
$result['tags'][] = $augmentedGroup['tag'];
|
||||
}
|
||||
if (isset($augmentedGroup['tag_id'])) {
|
||||
$result['tag_ids'][] = $augmentedGroup['tag_id'];
|
||||
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;
|
||||
@@ -249,15 +261,26 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function mergeTags(array $existingJournal, TransactionGroup $newGroup): array
|
||||
{
|
||||
$newArray = $newGroup->toArray();
|
||||
if (isset($newArray['tag_id'])) {
|
||||
$existingJournal['tag_ids'][] = (int)$newArray['tag_id'];
|
||||
}
|
||||
if (isset($newArray['tag'])) {
|
||||
$existingJournal['tags'][] = $newArray['tag'];
|
||||
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'],
|
||||
];
|
||||
}
|
||||
$existingJournal['tags'] = array_unique($existingJournal['tags']);
|
||||
$existingJournal['tag_ids'] = array_unique($existingJournal['tag_ids']);
|
||||
|
||||
return $existingJournal;
|
||||
}
|
||||
@@ -403,8 +426,10 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): GroupCollectorInterface
|
||||
{
|
||||
$this->withBudgetInformation();
|
||||
$this->query->whereIn('budgets.id', $budgets->pluck('id')->toArray());
|
||||
if ($budgets->count() > 0) {
|
||||
$this->withBudgetInformation();
|
||||
$this->query->whereIn('budgets.id', $budgets->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -560,7 +585,12 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$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';
|
||||
$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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,8 +756,10 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): GroupCollectorInterface
|
||||
{
|
||||
$this->withCategoryInformation();
|
||||
$this->query->where('categories.id', $categories->pluck('id')->toArray());
|
||||
if ($categories->count() > 0) {
|
||||
$this->withCategoryInformation();
|
||||
$this->query->whereIn('categories.id', $categories->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user