diff --git a/.github/stale.yml b/.github/stale.yml index b8870bbf00..afe541f002 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -13,8 +13,6 @@ exemptLabels: - enhancement - feature - bug - - possible-bug - - "possible bug" - announcement # Set to true to ignore issues in a project (defaults to false) diff --git a/.travis.yml b/.travis.yml index 655b6f0faf..4ad6b1b005 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,21 +14,21 @@ jobs: include: - dist: xenial arch: amd64 - env: ARCH=amd64 CHANNEL=alpha VERSION=4.8.2-alpha.3 + env: ARCH=amd64 CHANNEL=alpha VERSION=4.8.2-alpha.4 stage: build script: ./.deploy/docker/travis.sh - dist: xenial arch: amd64 - env: ARCH=arm CHANNEL=alpha VERSION=4.8.2-alpha.3 + env: ARCH=arm CHANNEL=alpha VERSION=4.8.2-alpha.4 stage: build script: ./.deploy/docker/travis.sh - dist: xenial arch: arm64 - env: ARCH=arm64 CHANNEL=alpha VERSION=4.8.2-alpha.3 + env: ARCH=arm64 CHANNEL=alpha VERSION=4.8.2-alpha.4 stage: build script: ./.deploy/docker/travis.sh - dist: xenial arch: amd64 - env: CHANNEL=alpha VERSION=4.8.2-alpha.3 + env: CHANNEL=alpha VERSION=4.8.2-alpha.4 stage: manifest script: ./.deploy/docker/manifest.sh \ No newline at end of file diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 96c1738c49..c57ccb183e 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -127,7 +127,7 @@ class IndexController extends Controller $array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0'; // budgeted in period: - $budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency,); + $budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency, ); $array['budgeted'] = $budgeted; $availableBudgets[] = $array; unset($spentArr); @@ -135,7 +135,7 @@ class IndexController extends Controller if (0 === count($availableBudgets)) { // get budgeted for default currency: - $budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency,); + $budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency, ); $spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency); $spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0'; unset($spentArr); diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 928e4e95a0..a9dc284a09 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -56,7 +56,6 @@ class BudgetController extends Controller protected $repository; /** @var BudgetLimitRepositoryInterface */ private $blRepository; - /** @var NoBudgetRepositoryInterface */ private $nbRepository; @@ -379,7 +378,7 @@ class BudgetController extends Controller $cache->addProperty($end); $cache->addProperty('chart.budget.frontpage'); if ($cache->has()) { - return response()->json($cache->get()); // @codeCoverageIgnore + //return response()->json($cache->get()); // @codeCoverageIgnore } $budgets = $this->repository->getActiveBudgets(); $chartData = [ @@ -390,23 +389,42 @@ class BudgetController extends Controller /** @var Budget $budget */ foreach ($budgets as $budget) { - // get relevant repetitions: - $limits = $this->blRepository->getBudgetLimits($budget, $start, $end); - $expenses = $this->getExpensesForBudget($limits, $budget, $start, $end); - - foreach ($expenses as $name => $row) { - $chartData[0]['entries'][$name] = $row['spent']; - $chartData[1]['entries'][$name] = $row['left']; - $chartData[2]['entries'][$name] = $row['overspent']; + $limits = $this->blRepository->getBudgetLimits($budget, $start, $end); + if (0 === $limits->count()) { + $spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]), null); + /** @var array $entry */ + foreach ($spent as $entry) { + $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); + $chartData[0]['entries'][$title] = bcmul($entry['sum'], '-1'); // spent + $chartData[1]['entries'][$title] = 0; // left to spend + $chartData[2]['entries'][$title] = 0; // overspent + } + } + if (0 !== $limits->count()) { + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $spent = $this->opsRepository->sumExpenses( + $limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency + ); + /** @var array $entry */ + foreach ($spent as $entry) { + $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); + if ($limit->start_date->startOfDay()->ne($start->startOfDay()) || $limit->end_date->startOfDay()->ne($end->startOfDay())) { + $title = sprintf( + '%s (%s) (%s - %s)', $budget->name, $entry['currency_name'], + $limit->start_date->formatLocalized($this->monthAndDayFormat), + $limit->end_date->formatLocalized($this->monthAndDayFormat) + ); + } + + $chartData[0]['entries'][$title] = bcmul($entry['sum'], '-1'); // spent + $chartData[1]['entries'][$title] = 1 === bccomp($limit->amount, bcmul($entry['sum'], '-1')) ? bcadd($entry['sum'], $limit->amount) + : '0'; + $chartData[2]['entries'][$title] = 1 === bccomp($limit->amount, bcmul($entry['sum'], '-1')) ? + '0' : bcmul(bcadd($entry['sum'], $limit->amount), '-1'); + } + } } - } - // for no budget: - $spent = $this->spentInPeriodWithout($start, $end); - $name = (string)trans('firefly.no_budget'); - if (0 !== bccomp($spent, '0')) { - $chartData[0]['entries'][$name] = bcmul($spent, '-1'); - $chartData[1]['entries'][$name] = '0'; - $chartData[2]['entries'][$name] = '0'; } $data = $this->generator->multiSet($chartData); diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php index d18d31eb5c..ee56d4958c 100644 --- a/app/Jobs/CreateRecurringTransactions.php +++ b/app/Jobs/CreateRecurringTransactions.php @@ -218,7 +218,6 @@ class CreateRecurringTransactions implements ShouldQueue $return = []; /** @var RecurrenceTransaction $transaction */ foreach ($transactions as $index => $transaction) { - $single = [ 'type' => strtolower($recurrence->transactionType->type), 'date' => $date, diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index fde9377424..e323e25307 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -282,7 +282,8 @@ class RecurringRepository implements RecurringRepositoryInterface /** @var RecurrenceMeta $meta */ foreach ($transaction->recurrenceTransactionMeta as $meta) { if ('tags' === $meta->name && '' !== $meta->value) { - $tags = explode(',', $meta->value); + //$tags = explode(',', $meta->value); + $tags = json_decode($meta->value, true, 512, JSON_THROW_ON_ERROR); } } diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index 08dc85a6fb..fc318675bc 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -45,8 +45,13 @@ class UpdateRequest implements UpdateRequestInterface $uri = config('firefly.update_endpoint'); Log::debug(sprintf('Going to call %s', $uri)); try { - $client = new Client(); - $res = $client->request('GET', $uri); + $client = new Client(); + $options = [ + 'headers' => [ + 'User-Agent' => sprintf('FireflyIII/%s/%s', config('firefly.version'), $channel), + ], + ]; + $res = $client->request('GET', $uri, $options); } catch (GuzzleException|Exception $e) { throw new FireflyException(sprintf('Response error from update check: %s', $e->getMessage())); } diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index 73c3b75ea7..f8fe215e8b 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -71,109 +71,6 @@ trait AugumentData return $combined; } - /** - * Group by category (earnings). - * - * @param Collection $assets - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array - * - */ - protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $total = $assets->merge($opposing); - $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total); - $collector->withCategoryInformation(); - $journals = $collector->getExtractedJournals(); - $sum = []; - // loop to support multi currency - foreach ($journals as $journal) { - $currencyId = $journal['currency_id']; - $categoryName = $journal['category_name']; - $categoryId = (int)$journal['category_id']; - - // if not set, set to zero: - if (!isset($sum[$categoryId][$currencyId])) { - $sum[$categoryId] = [ - 'grand_total' => '0', - 'name' => $categoryName, - 'per_currency' => [ - $currencyId => [ - 'sum' => '0', - 'category' => [ - 'id' => $categoryId, - 'name' => $categoryName, - ], - 'currency' => [ - 'symbol' => $journal['currency_symbol'], - 'dp' => $journal['currency_decimal_places'], - ], - ], - ], - ]; - } - - // add amount - $sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd( - $sum[$categoryId]['per_currency'][$currencyId]['sum'], $journal['amount'] - ); - $sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $journal['amount']); - } - - return $sum; - } - - /** - * Earned in period for accounts. - * - * @param Collection $assets - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $total = $assets->merge($opposing); - $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total); - $journals = $collector->getExtractedJournals(); - $sum = [ - 'grand_sum' => '0', - 'per_currency' => [], - ]; - // loop to support multi currency - foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - - // if not set, set to zero: - if (!isset($sum['per_currency'][$currencyId])) { - $sum['per_currency'][$currencyId] = [ - 'sum' => '0', - 'currency' => [ - 'symbol' => $journal['currency_symbol'], - 'decimal_places' => $journal['currency_decimal_places'], - ], - ]; - } - - // add amount - $sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $journal['amount']); - $sum['grand_sum'] = bcadd($sum['grand_sum'], $journal['amount']); - } - - return $sum; - } - /** * Small helper function for the revenue and expense account charts. * @@ -209,34 +106,6 @@ trait AugumentData return $return; } - /** - * Returns the budget limits belonging to the given budget and valid on the given day. - * - * @param Collection $budgetLimits - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - protected function filterBudgetLimits(Collection $budgetLimits, Budget $budget, Carbon $start, Carbon $end): Collection // filter data - { - $set = $budgetLimits->filter( - static function (BudgetLimit $budgetLimit) use ($budget, $start, $end) { - if ($budgetLimit->budget_id === $budget->id - && $budgetLimit->start_date->lte($start) // start of budget limit is on or before start - && $budgetLimit->end_date->gte($end) // end of budget limit is on or after end - ) { - return $budgetLimit; - } - - return false; - } - ); - - return $set; - } - /** * Get the account names belonging to a bunch of account ID's. * @@ -285,39 +154,6 @@ trait AugumentData return $return; } - /** - * Get the amount of money budgeted in a period. - * - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - protected function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array // get data + augment with info - { - /** @var BudgetLimitRepositoryInterface $blRepository */ - $blRepository = app(BudgetLimitRepositoryInterface::class); - - $key = app('navigation')->preferredCarbonFormat($start, $end); - $range = app('navigation')->preferredRangeFormat($start, $end); - $current = clone $start; - $budgeted = []; - while ($current < $end) { - /** @var Carbon $currentStart */ - $currentStart = app('navigation')->startOfPeriod($current, $range); - /** @var Carbon $currentEnd */ - $currentEnd = app('navigation')->endOfPeriod($current, $range); - $budgetLimits = $blRepository->getBudgetLimits($budget, $currentStart, $currentEnd); - $index = $currentStart->format($key); - $budgeted[$index] = $budgetLimits->sum('amount'); - $currentEnd->addDay(); - $current = clone $currentEnd; - } - - return $budgeted; - } - /** * Get the category names from a set of category ID's. Small helper function for some of the charts. * @@ -342,48 +178,6 @@ trait AugumentData return $return; } - /** - * Get the expenses for a budget in a date range. - * - * @param Collection $limits - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array - * - */ - protected function getExpensesForBudget(Collection $limits, Budget $budget, Carbon $start, Carbon $end): array // get data + augment with info - { - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - - /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository = app(OperationsRepositoryInterface::class); - - $return = []; - if (0 === $limits->count()) { - $spent = $opsRepository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); - if (0 !== bccomp($spent, '0')) { - $return[$budget->name]['spent'] = bcmul($spent, '-1'); - $return[$budget->name]['left'] = 0; - $return[$budget->name]['overspent'] = 0; - } - - return $return; - } - - $rows = $this->spentInPeriodMulti($budget, $limits); - foreach ($rows as $name => $row) { - if (0 !== bccomp($row['spent'], '0') || 0 !== bccomp($row['left'], '0')) { - $return[$name] = $row; - } - } - unset($rows); - - return $return; - } - /** * Gets all budget limits for a budget. * @@ -425,48 +219,6 @@ trait AugumentData return $set; } - /** - * Helper function that groups expenses. - * - * @param array $array - * - * @return array - */ - protected function groupByBudget(array $array): array // filter + group data - { - // group by category ID: - $grouped = []; - /** @var array $journal */ - foreach ($array as $journal) { - $budgetId = (int)$journal['budget_id']; - $grouped[$budgetId] = $grouped[$budgetId] ?? '0'; - $grouped[$budgetId] = bcadd($journal['amount'], $grouped[$budgetId]); - } - - return $grouped; - } - - /** - * Group transactions by category. - * - * @param array $array - * - * @return array - */ - protected function groupByCategory(array $array): array // filter + group data - { - // group by category ID: - $grouped = []; - /** @var array $journal */ - foreach ($array as $journal) { - $categoryId = (int)$journal['category_id']; - $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; - $grouped[$categoryId] = bcadd($journal['amount'], $grouped[$categoryId]); - } - - return $grouped; - } - /** * Group set of transactions by name of opposing account. * @@ -496,147 +248,6 @@ trait AugumentData return $grouped; } - /** - * Group transactions by tag. - * - * @param array $array - * - * @return array - */ - protected function groupByTag(array $array): array // filter + group data - { - // group by category ID: - $grouped = []; - /** @var array $journal */ - foreach ($array as $journal) { - $tags = $journal['tags'] ?? []; - /** - * @var int $id - * @var array $tag - */ - foreach ($tags as $id => $tag) { - $grouped[$id] = $grouped[$id] ?? '0'; - $grouped[$id] = bcadd($journal['amount'], $grouped[$id]); - } - } - - return $grouped; - } - - /** - * Spent by budget. - * - * @param Collection $assets - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array - * - */ - protected function spentByBudget(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $total = $assets->merge($opposing); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($total); - $collector->withBudgetInformation(); - $journals = $collector->getExtractedJournals(); - $sum = []; - // loop to support multi currency - foreach ($journals as $journal) { - $currencyId = $journal['currency_id']; - $budgetName = $journal['budget_name']; - $budgetId = (int)$journal['budget_id']; - - // if not set, set to zero: - if (!isset($sum[$budgetId][$currencyId])) { - $sum[$budgetId] = [ - 'grand_total' => '0', - 'name' => $budgetName, - 'per_currency' => [ - $currencyId => [ - 'sum' => '0', - 'budget' => [ - 'id' => $budgetId, - 'name' => $budgetName, - ], - 'currency' => [ - 'symbol' => $journal['currency_symbol'], - 'dp' => $journal['currency_decimal_places'], - ], - ], - ], - ]; - } - - // add amount - $sum[$budgetId]['per_currency'][$currencyId]['sum'] = bcadd( - $sum[$budgetId]['per_currency'][$currencyId]['sum'], $journal['amount'] - ); - $sum[$budgetId]['grand_total'] = bcadd($sum[$budgetId]['grand_total'], $journal['amount']); - } - - return $sum; - } - - /** - * Spent by category. - * - * @param Collection $assets - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array - * - */ - protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $total = $assets->merge($opposing); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($total); - $collector->withCategoryInformation(); - $journals = $collector->getExtractedJournals(); - $sum = []; - // loop to support multi currency - foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - $categoryName = $journal['category_name']; - $categoryId = (int)$journal['category_id']; - - // if not set, set to zero: - if (!isset($sum[$categoryId][$currencyId])) { - $sum[$categoryId] = [ - 'grand_total' => '0', - 'name' => $categoryName, - 'per_currency' => [ - $currencyId => [ - 'sum' => '0', - 'category' => [ - 'id' => $categoryId, - 'name' => $categoryName, - ], - 'currency' => [ - 'symbol' => $journal['currency_symbol'], - 'dp' => $journal['currency_decimal_places'], - ], - ], - ], - ]; - } - - // add amount - $sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd( - $sum[$categoryId]['per_currency'][$currencyId]['sum'], $journal['amount'] - ); - $sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $journal['amount']); - } - - return $sum; - } - /** * Spent in a period. * @@ -682,85 +293,4 @@ trait AugumentData return $sum; } - - /** - * - * Returns an array with the following values: - * 0 => - * 'name' => name of budget + repetition - * 'left' => left in budget repetition (always zero) - * 'overspent' => spent more than budget repetition? (always zero) - * 'spent' => actually spent in period for budget - * 1 => (etc) - * - * @param Budget $budget - * @param Collection $limits - * - * @return array - * - */ - protected function spentInPeriodMulti(Budget $budget, Collection $limits): array // get data + augment with info - { - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - - /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository = app(OperationsRepositoryInterface::class); - - $return = []; - $format = (string)trans('config.month_and_day'); - $name = $budget->name; - /** @var BudgetLimit $budgetLimit */ - foreach ($limits as $budgetLimit) { - $expenses = $opsRepository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); - $expenses = app('steam')->positive($expenses); - - if ($limits->count() > 1) { - $name = $budget->name . ' ' . trans( - 'firefly.between_dates', - [ - 'start' => $budgetLimit->start_date->formatLocalized($format), - 'end' => $budgetLimit->end_date->formatLocalized($format), - ] - ); - } - $amount = $budgetLimit->amount; - $leftInLimit = bcsub($amount, $expenses); - $hasOverspent = bccomp($leftInLimit, '0') === -1; - $left = $hasOverspent ? '0' : bcsub($amount, $expenses); - $spent = $hasOverspent ? $amount : $expenses; - $overspent = $hasOverspent ? app('steam')->positive($leftInLimit) : '0'; - - $return[$name] = [ - 'left' => $left, - 'overspent' => $overspent, - 'spent' => $spent, - ]; - } - - return $return; - } - - /** - * Returns an array with the following values: - * 'name' => "no budget" in local language - * 'repetition_left' => left in budget repetition (always zero) - * 'repetition_overspent' => spent more than budget repetition? (always zero) - * 'spent' => actually spent in period for budget. - * - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - protected function spentInPeriodWithout(Carbon $start, Carbon $end): string // get data + augment with info - { - // collector - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $types = [TransactionType::WITHDRAWAL]; - $collector->setTypes($types)->setRange($start, $end)->withoutBudget(); - - return $collector->getSum(); - } } diff --git a/config/firefly.php b/config/firefly.php index 83ce83f303..83754e32e8 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -125,7 +125,7 @@ return [ 'is_demo_site' => false, ], 'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true, - 'version' => '4.8.2-alpha.3', + 'version' => '4.8.2-alpha.4', 'api_version' => '0.10.5', 'db_version' => 11, 'maxUploadSize' => 15242880,