mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-11 15:16:27 +00:00
Also add no budget and no category overview.
This commit is contained in:
@@ -92,7 +92,7 @@ class ShowController extends Controller
|
|||||||
// get first journal ever to set off the budget period overview.
|
// get first journal ever to set off the budget period overview.
|
||||||
$first = $this->journalRepos->firstNull();
|
$first = $this->journalRepos->firstNull();
|
||||||
$firstDate = $first instanceof TransactionJournal ? $first->date : $start;
|
$firstDate = $first instanceof TransactionJournal ? $first->date : $start;
|
||||||
$periods = $this->getNoBudgetPeriodOverview($firstDate, $end);
|
$periods = $this->getNoModelPeriodOverview('budget', $firstDate, $end);
|
||||||
$page = (int) $request->get('page');
|
$page = (int) $request->get('page');
|
||||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||||
|
|
||||||
|
@@ -35,6 +35,7 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview;
|
|||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,7 +75,7 @@ class NoCategoryController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(Request $request, ?Carbon $start = null, ?Carbon $end = null)
|
public function show(Request $request, ?Carbon $start = null, ?Carbon $end = null)
|
||||||
{
|
{
|
||||||
app('log')->debug('Start of noCategory()');
|
Log::debug('Start of noCategory()');
|
||||||
$start ??= session('start');
|
$start ??= session('start');
|
||||||
$end ??= session('end');
|
$end ??= session('end');
|
||||||
|
|
||||||
@@ -82,14 +83,12 @@ class NoCategoryController extends Controller
|
|||||||
/** @var Carbon $end */
|
/** @var Carbon $end */
|
||||||
$page = (int) $request->get('page');
|
$page = (int) $request->get('page');
|
||||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||||
$subTitle = trans(
|
$subTitle = trans('firefly.without_category_between', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]);
|
||||||
'firefly.without_category_between',
|
$first = $this->journalRepos->firstNull()->date ?? clone $start;
|
||||||
['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]
|
$periods = $this->getNoModelPeriodOverview('category', $first, $end);
|
||||||
);
|
|
||||||
$periods = $this->getNoCategoryPeriodOverview($start);
|
|
||||||
|
|
||||||
app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
|
Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
|
||||||
app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
|
Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
|
||||||
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
@@ -117,13 +116,13 @@ class NoCategoryController extends Controller
|
|||||||
$periods = new Collection();
|
$periods = new Collection();
|
||||||
$page = (int) $request->get('page');
|
$page = (int) $request->get('page');
|
||||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||||
app('log')->debug('Start of noCategory()');
|
Log::debug('Start of noCategory()');
|
||||||
$subTitle = (string) trans('firefly.all_journals_without_category');
|
$subTitle = (string) trans('firefly.all_journals_without_category');
|
||||||
$first = $this->journalRepos->firstNull();
|
$first = $this->journalRepos->firstNull();
|
||||||
$start = $first instanceof TransactionJournal ? $first->date : new Carbon();
|
$start = $first instanceof TransactionJournal ? $first->date : new Carbon();
|
||||||
$end = today(config('app.timezone'));
|
$end = today(config('app.timezone'));
|
||||||
app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
|
Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
|
||||||
app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
|
Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
|
||||||
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
@@ -8,6 +8,7 @@ use FireflyIII\Casts\SeparateTimezoneCaster;
|
|||||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
|
|
||||||
class PeriodStatistic extends Model
|
class PeriodStatistic extends Model
|
||||||
@@ -24,6 +25,11 @@ class PeriodStatistic extends Model
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function userGroup(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(UserGroup::class);
|
||||||
|
}
|
||||||
|
|
||||||
protected function count(): Attribute
|
protected function count(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
|
@@ -76,6 +76,14 @@ class UserGroup extends Model
|
|||||||
return $this->hasMany(Account::class);
|
return $this->hasMany(Account::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to accounts.
|
||||||
|
*/
|
||||||
|
public function periodStatistics(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(PeriodStatistic::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link to attachments.
|
* Link to attachments.
|
||||||
*/
|
*/
|
||||||
|
@@ -163,7 +163,6 @@ class FireflyServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
$this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class);
|
$this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class);
|
||||||
$this->app->bind(ALERepositoryInterface::class, ALERepository::class);
|
$this->app->bind(ALERepositoryInterface::class, ALERepository::class);
|
||||||
$this->app->bind(PeriodStatisticRepositoryInterface::class, PeriodStatisticRepository::class);
|
|
||||||
|
|
||||||
$this->app->bind(
|
$this->app->bind(
|
||||||
static function (Application $app): ObjectGroupRepositoryInterface {
|
static function (Application $app): ObjectGroupRepositoryInterface {
|
||||||
@@ -177,6 +176,18 @@ class FireflyServiceProvider extends ServiceProvider
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->app->bind(
|
||||||
|
static function (Application $app): PeriodStatisticRepositoryInterface {
|
||||||
|
/** @var PeriodStatisticRepository $repository */
|
||||||
|
$repository = app(PeriodStatisticRepository::class);
|
||||||
|
if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth)
|
||||||
|
$repository->setUser(auth()->user());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repository;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$this->app->bind(
|
$this->app->bind(
|
||||||
static function (Application $app): WebhookRepositoryInterface {
|
static function (Application $app): WebhookRepositoryInterface {
|
||||||
/** @var WebhookRepository $repository */
|
/** @var WebhookRepository $repository */
|
||||||
|
@@ -25,37 +25,40 @@ namespace FireflyIII\Repositories\PeriodStatistic;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Models\PeriodStatistic;
|
use FireflyIII\Models\PeriodStatistic;
|
||||||
|
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
||||||
|
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
|
class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, UserGroupInterface
|
||||||
{
|
{
|
||||||
|
use UserGroupTrait;
|
||||||
|
|
||||||
public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection
|
public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection
|
||||||
{
|
{
|
||||||
return $model->primaryPeriodStatistics()
|
return $model->primaryPeriodStatistics()
|
||||||
->where('start', $start)
|
->where('start', $start)
|
||||||
->where('end', $end)
|
->where('end', $end)
|
||||||
->whereIn('type', $types)
|
->whereIn('type', $types)
|
||||||
->get()
|
->get();
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection
|
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection
|
||||||
{
|
{
|
||||||
return $model->primaryPeriodStatistics()
|
return $model->primaryPeriodStatistics()
|
||||||
->where('start', $start)
|
->where('start', $start)
|
||||||
->where('end', $end)
|
->where('end', $end)
|
||||||
->where('type', $type)
|
->where('type', $type)
|
||||||
->get()
|
->get();
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic
|
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic
|
||||||
{
|
{
|
||||||
$stat = new PeriodStatistic();
|
$stat = new PeriodStatistic();
|
||||||
$stat->primaryStatable()->associate($model);
|
$stat->primaryStatable()->associate($model);
|
||||||
$stat->transaction_currency_id = $currencyId;
|
$stat->transaction_currency_id = $currencyId;
|
||||||
|
$stat->user_group_id = $this->getUserGroup()->id;
|
||||||
$stat->start = $start;
|
$stat->start = $start;
|
||||||
$stat->start_tz = $start->format('e');
|
$stat->start_tz = $start->format('e');
|
||||||
$stat->end = $end;
|
$stat->end = $end;
|
||||||
@@ -66,16 +69,16 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
|
|||||||
$stat->save();
|
$stat->save();
|
||||||
|
|
||||||
Log::debug(sprintf(
|
Log::debug(sprintf(
|
||||||
'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.',
|
'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.',
|
||||||
$stat->id,
|
$stat->id,
|
||||||
$model::class,
|
$model::class,
|
||||||
$model->id,
|
$model->id,
|
||||||
$stat->transaction_currency_id,
|
$stat->transaction_currency_id,
|
||||||
$stat->start->toW3cString(),
|
$stat->start->toW3cString(),
|
||||||
$stat->end->toW3cString(),
|
$stat->end->toW3cString(),
|
||||||
$count,
|
$count,
|
||||||
$amount
|
$amount
|
||||||
));
|
));
|
||||||
|
|
||||||
return $stat;
|
return $stat;
|
||||||
}
|
}
|
||||||
@@ -89,4 +92,41 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
|
|||||||
{
|
{
|
||||||
$model->primaryPeriodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->delete();
|
$model->primaryPeriodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
|
public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection
|
||||||
|
{
|
||||||
|
return $this->userGroup->periodStatistics()
|
||||||
|
->where('type', 'LIKE', sprintf('%s%%', $prefix))
|
||||||
|
->where('start', '>=', $start)->where('end', '<=', $end)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
|
public function savePrefixedStatistic(string $prefix, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic
|
||||||
|
{
|
||||||
|
$stat = new PeriodStatistic();
|
||||||
|
$stat->transaction_currency_id = $currencyId;
|
||||||
|
$stat->user_group_id = $this->getUserGroup()->id;
|
||||||
|
$stat->start = $start;
|
||||||
|
$stat->start_tz = $start->format('e');
|
||||||
|
$stat->end = $end;
|
||||||
|
$stat->end_tz = $end->format('e');
|
||||||
|
$stat->amount = $amount;
|
||||||
|
$stat->count = $count;
|
||||||
|
$stat->type = sprintf('%s_%s',$prefix, $type);
|
||||||
|
$stat->save();
|
||||||
|
|
||||||
|
Log::debug(sprintf(
|
||||||
|
'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.',
|
||||||
|
$stat->id,
|
||||||
|
$stat->transaction_currency_id,
|
||||||
|
$stat->type,
|
||||||
|
$stat->start->toW3cString(),
|
||||||
|
$stat->end->toW3cString(),
|
||||||
|
$count,
|
||||||
|
$amount
|
||||||
|
));
|
||||||
|
|
||||||
|
return $stat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,8 +35,10 @@ interface PeriodStatisticRepositoryInterface
|
|||||||
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection;
|
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection;
|
||||||
|
|
||||||
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic;
|
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic;
|
||||||
|
public function savePrefixedStatistic(string $prefix, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic;
|
||||||
|
|
||||||
public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection;
|
public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection;
|
||||||
|
public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection;
|
||||||
|
|
||||||
public function deleteStatisticsForModel(Model $model, Carbon $date): void;
|
public function deleteStatisticsForModel(Model $model, Carbon $date): void;
|
||||||
}
|
}
|
||||||
|
@@ -169,117 +169,129 @@ trait PeriodOverview
|
|||||||
*
|
*
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
|
protected function getNoModelPeriodOverview(string $model, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$range = Navigation::getViewRange(true);
|
Log::debug(sprintf('Now in getNoModelPeriodOverview(%s, %s %s)', $model, $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
$this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class);
|
||||||
|
$range = Navigation::getViewRange(true);
|
||||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||||
|
|
||||||
$cache = new CacheProperties();
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
$cache->addProperty($this->convertToPrimary);
|
|
||||||
$cache->addProperty('no-budget-period-entries');
|
|
||||||
|
|
||||||
if ($cache->has()) {
|
|
||||||
return $cache->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var array $dates */
|
/** @var array $dates */
|
||||||
$dates = Navigation::blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
[$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end);
|
||||||
|
$entries = [];
|
||||||
// get all expenses without a budget.
|
$this->statistics = $this->periodStatisticRepo->allInRangeForPrefix(sprintf('no_%s', $model), $start, $end);
|
||||||
/** @var GroupCollectorInterface $collector */
|
Log::debug(sprintf('Collected %d stats', $this->statistics->count()));
|
||||||
$collector = app(GroupCollectorInterface::class);
|
|
||||||
$collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
|
|
||||||
$journals = $collector->getExtractedJournals();
|
|
||||||
|
|
||||||
foreach ($dates as $currentDate) {
|
foreach ($dates as $currentDate) {
|
||||||
$set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']);
|
$entries[] = $this->getSingleNoModelPeriodOverview($model, $currentDate['start'], $currentDate['end'], $currentDate['period']);
|
||||||
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
|
||||||
$entries[]
|
|
||||||
= [
|
|
||||||
'title' => $title,
|
|
||||||
'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
|
|
||||||
'total_transactions' => count($set),
|
|
||||||
'spent' => $this->groupByCurrency($set),
|
|
||||||
'earned' => [],
|
|
||||||
'transferred_away' => [],
|
|
||||||
'transferred_in' => [],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
$cache->store($entries);
|
|
||||||
|
|
||||||
return $entries;
|
return $entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getSingleNoModelPeriodOverview(string $model, Carbon $start, Carbon $end, string $period): array
|
||||||
* TODO fix the date.
|
|
||||||
*
|
|
||||||
* Show period overview for no category view.
|
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
|
||||||
protected function getNoCategoryPeriodOverview(Carbon $theDate): array
|
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
|
Log::debug(sprintf('getSingleNoModelPeriodOverview(%s, %s, %s, %s)', $model, $start->format('Y-m-d'), $end->format('Y-m-d'), $period));
|
||||||
$range = Navigation::getViewRange(true);
|
$statistics = $this->filterPrefixedStatistics($start, $end, sprintf('no_%s', $model));
|
||||||
$first = $this->journalRepos->firstNull();
|
$title = Navigation::periodShow($end, $period);
|
||||||
$start = null === $first ? new Carbon() : $first->date;
|
|
||||||
$end = clone $theDate;
|
|
||||||
$end = Navigation::endOfPeriod($end, $range);
|
|
||||||
|
|
||||||
Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
|
if (0 === $statistics->count()) {
|
||||||
Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
|
Log::debug(sprintf('Found no statistics in period %s - %s, regenerating them.', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
switch ($model) {
|
||||||
|
default:
|
||||||
|
throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model));
|
||||||
|
case 'budget':
|
||||||
|
// get all expenses without a budget.
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
|
||||||
|
$spent = $collector->getExtractedJournals();
|
||||||
|
$earned = [];
|
||||||
|
$transferred = [];
|
||||||
|
break;
|
||||||
|
case 'category':
|
||||||
|
// collect all expenses in this period:
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->withoutCategory();
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
|
||||||
|
$earned = $collector->getExtractedJournals();
|
||||||
|
|
||||||
// properties for cache
|
// collect all income in this period:
|
||||||
$dates = Navigation::blockPeriods($start, $end, $range);
|
/** @var GroupCollectorInterface $collector */
|
||||||
$entries = [];
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->withoutCategory();
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
|
||||||
|
$spent = $collector->getExtractedJournals();
|
||||||
|
|
||||||
// collect all expenses in this period:
|
// collect all transfers in this period:
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->withoutCategory();
|
$collector->withoutCategory();
|
||||||
$collector->setRange($start, $end);
|
$collector->setRange($start, $end);
|
||||||
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
|
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
|
||||||
$earnedSet = $collector->getExtractedJournals();
|
$transferred = $collector->getExtractedJournals();
|
||||||
|
break;
|
||||||
// collect all income in this period:
|
}
|
||||||
/** @var GroupCollectorInterface $collector */
|
$groupedSpent = $this->groupByCurrency($spent);
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$groupedEarned = $this->groupByCurrency($earned);
|
||||||
$collector->withoutCategory();
|
$groupedTransferred = $this->groupByCurrency($transferred);
|
||||||
$collector->setRange($start, $end);
|
$entry
|
||||||
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
|
= [
|
||||||
$spentSet = $collector->getExtractedJournals();
|
|
||||||
|
|
||||||
// collect all transfers in this period:
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
|
||||||
$collector = app(GroupCollectorInterface::class);
|
|
||||||
$collector->withoutCategory();
|
|
||||||
$collector->setRange($start, $end);
|
|
||||||
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
|
|
||||||
$transferSet = $collector->getExtractedJournals();
|
|
||||||
|
|
||||||
/** @var array $currentDate */
|
|
||||||
foreach ($dates as $currentDate) {
|
|
||||||
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
|
||||||
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
|
||||||
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
|
||||||
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
|
||||||
$entries[]
|
|
||||||
= [
|
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
|
'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]),
|
||||||
'total_transactions' => count($spent) + count($earned) + count($transferred),
|
'total_transactions' => count($spent),
|
||||||
'spent' => $this->groupByCurrency($spent),
|
'spent' => $groupedSpent,
|
||||||
'earned' => $this->groupByCurrency($earned),
|
'earned' => $groupedEarned,
|
||||||
'transferred' => $this->groupByCurrency($transferred),
|
'transferred' => $groupedTransferred,
|
||||||
];
|
];
|
||||||
|
$this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'spent', $groupedSpent);
|
||||||
|
$this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'earned', $groupedEarned);
|
||||||
|
$this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'transferred', $groupedTransferred);
|
||||||
|
return $entry;
|
||||||
}
|
}
|
||||||
Log::debug('End of loops');
|
Log::debug(sprintf('Found %d statistics in period %s - %s.', count($statistics), $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
|
||||||
return $entries;
|
$entry
|
||||||
|
= [
|
||||||
|
'title' => $title,
|
||||||
|
'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]),
|
||||||
|
'total_transactions' => 0,
|
||||||
|
'spent' => [],
|
||||||
|
'earned' => [],
|
||||||
|
'transferred' => [],
|
||||||
|
];
|
||||||
|
$grouped = [];
|
||||||
|
/** @var PeriodStatistic $statistic */
|
||||||
|
foreach ($statistics as $statistic) {
|
||||||
|
$type = str_replace(sprintf('no_%s_', $model), '', $statistic->type);
|
||||||
|
$id = (int)$statistic->transaction_currency_id;
|
||||||
|
$currency = Amount::getTransactionCurrencyById($id);
|
||||||
|
$grouped[$type]['count'] ??= 0;
|
||||||
|
$grouped[$type][$id] = [
|
||||||
|
'amount' => (string)$statistic->amount,
|
||||||
|
'count' => (int)$statistic->count,
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_name' => $currency->name,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
];
|
||||||
|
$grouped[$type]['count'] += (int)$statistic->count;
|
||||||
|
}
|
||||||
|
$types = ['spent', 'earned', 'transferred'];
|
||||||
|
foreach ($types as $type) {
|
||||||
|
if (array_key_exists($type, $grouped)) {
|
||||||
|
$entry['total_transactions'] += $grouped[$type]['count'];
|
||||||
|
unset($grouped[$type]['count']);
|
||||||
|
$entry[$type] = $grouped[$type];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getSingleModelPeriod(Model $model, string $period, Carbon $start, Carbon $end): array
|
protected function getSingleModelPeriod(Model $model, string $period, Carbon $start, Carbon $end): array
|
||||||
@@ -303,30 +315,34 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function filterStatistics(Carbon $start, Carbon $end, string $type): Collection
|
private function filterStatistics(Carbon $start, Carbon $end, string $type): Collection
|
||||||
{
|
{
|
||||||
|
if (0 === $this->statistics->count()) {
|
||||||
|
Log::warning('Have no statistic to filter!');
|
||||||
|
return new Collection;
|
||||||
|
}
|
||||||
return $this->statistics->filter(
|
return $this->statistics->filter(
|
||||||
function (PeriodStatistic $statistic) use ($start, $end, $type) {
|
function (PeriodStatistic $statistic) use ($start, $end, $type) {
|
||||||
if (
|
|
||||||
!$statistic->end->equalTo($end)
|
|
||||||
&& $statistic->end->format('Y-m-d H:i:s') === $end->format('Y-m-d H:i:s')
|
|
||||||
) {
|
|
||||||
echo sprintf('End: "%s" vs "%s": %s', $statistic->end->toW3cString(), $end->toW3cString(), var_export($statistic->end->eq($end), true));
|
|
||||||
var_dump($statistic->end);
|
|
||||||
var_dump($end);
|
|
||||||
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type;
|
return $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function filterPrefixedStatistics(Carbon $start, Carbon $end, string $prefix): Collection
|
||||||
|
{
|
||||||
|
if (0 === $this->statistics->count()) {
|
||||||
|
Log::warning('Have no statistic to filter!');
|
||||||
|
return new Collection;
|
||||||
|
}
|
||||||
|
return $this->statistics->filter(
|
||||||
|
function (PeriodStatistic $statistic) use ($start, $end, $prefix) {
|
||||||
|
return $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array
|
private function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type));
|
Log::debug(sprintf('Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type));
|
||||||
$statistics = $this->filterStatistics($start, $end, $type);
|
$statistics = $this->filterStatistics($start, $end, $type);
|
||||||
@@ -497,7 +513,7 @@ trait PeriodOverview
|
|||||||
return $entries;
|
return $entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void
|
private function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void
|
||||||
{
|
{
|
||||||
unset($array['count']);
|
unset($array['count']);
|
||||||
Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array)));
|
Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array)));
|
||||||
@@ -510,6 +526,19 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function saveGroupedForPrefix(string $prefix, Carbon $start, Carbon $end, string $type, array $array): void
|
||||||
|
{
|
||||||
|
unset($array['count']);
|
||||||
|
Log::debug(sprintf('saveGroupedForPrefix("%s", %s, %s, "%s", array(%d))', $prefix, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array)));
|
||||||
|
foreach ($array as $entry) {
|
||||||
|
$this->periodStatisticRepo->savePrefixedStatistic($prefix, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']);
|
||||||
|
}
|
||||||
|
if (0 === count($array)) {
|
||||||
|
Log::debug('Save empty statistic.');
|
||||||
|
$this->periodStatisticRepo->savePrefixedStatistic($prefix, $this->primaryCurrency->id, $start, $end, $type, 0, '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter a list of journals by a set of dates, and then group them by currency.
|
* Filter a list of journals by a set of dates, and then group them by currency.
|
||||||
*/
|
*/
|
||||||
@@ -584,6 +613,9 @@ trait PeriodOverview
|
|||||||
$return = [
|
$return = [
|
||||||
'count' => 0,
|
'count' => 0,
|
||||||
];
|
];
|
||||||
|
if (0 === count($journals)) {
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/** @var array $journal */
|
/** @var array $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
|
@@ -14,6 +14,10 @@ return new class extends Migration
|
|||||||
Schema::create('period_statistics', function (Blueprint $table) {
|
Schema::create('period_statistics', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
|
// reference to user group id.
|
||||||
|
$table->bigInteger('user_group_id', false, true);
|
||||||
|
|
||||||
$table->integer('primary_statable_id', false, true)->nullable();
|
$table->integer('primary_statable_id', false, true)->nullable();
|
||||||
$table->string('primary_statable_type', 255)->nullable();
|
$table->string('primary_statable_type', 255)->nullable();
|
||||||
|
|
||||||
@@ -33,6 +37,7 @@ return new class extends Migration
|
|||||||
$table->string('type',255);
|
$table->string('type',255);
|
||||||
$table->integer('count', false, true)->default(0);
|
$table->integer('count', false, true)->default(0);
|
||||||
$table->decimal('amount', 32, 12);
|
$table->decimal('amount', 32, 12);
|
||||||
|
$table->foreign('user_group_id')->references('id')->on('user_groups')->onDelete('cascade');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user