akaunting 3.0 (the last dance)
This commit is contained in:
@ -9,9 +9,17 @@ class AccountBalance extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.account_balance';
|
||||
|
||||
public $description = 'widgets.description.account_balance';
|
||||
|
||||
public $report_class = 'App\Reports\IncomeExpense';
|
||||
|
||||
public function show()
|
||||
{
|
||||
$accounts = Account::with('income_transactions', 'expense_transactions')->enabled()->take(5)->get();
|
||||
$accounts = Account::with('income_transactions', 'expense_transactions')->enabled()->take(5)->get()->map(function($account) {
|
||||
$account->balance_formatted = money($account->balance, $account->currency_code, true);
|
||||
|
||||
return $account;
|
||||
})->all();
|
||||
|
||||
return $this->view('widgets.account_balance', [
|
||||
'accounts' => $accounts,
|
||||
|
24
app/Widgets/BankFeeds.php
Normal file
24
app/Widgets/BankFeeds.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Traits\Cloud;
|
||||
use App\Traits\Modules;
|
||||
|
||||
class BankFeeds extends Widget
|
||||
{
|
||||
use Cloud, Modules;
|
||||
|
||||
public $default_name = 'widgets.bank_feeds';
|
||||
|
||||
public function show()
|
||||
{
|
||||
$module = $this->getModulesByWidget('bank-feeds');
|
||||
|
||||
return $this->view('widgets.bank_feeds', [
|
||||
'module' => $module,
|
||||
'learn_more_url' => $this->getCloudBankFeedsUrl(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use Akaunting\Apexcharts\Charts as Apexcharts;
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Banking\Transaction;
|
||||
use App\Traits\Currencies;
|
||||
use App\Traits\DateTime;
|
||||
use App\Utilities\Chartjs;
|
||||
use Date;
|
||||
use App\Utilities\Date;
|
||||
|
||||
class CashFlow extends Widget
|
||||
{
|
||||
@ -16,10 +16,68 @@ class CashFlow extends Widget
|
||||
public $default_name = 'widgets.cash_flow';
|
||||
|
||||
public $default_settings = [
|
||||
'width' => 'col-md-12',
|
||||
'width' => 'w-full my-8 px-12',
|
||||
];
|
||||
|
||||
public $description = 'widgets.description.cash_flow';
|
||||
|
||||
public $report_class = 'Modules\CashFlowStatement\Reports\CashFlowStatement';
|
||||
|
||||
public $start_date;
|
||||
|
||||
public $end_date;
|
||||
|
||||
public $period;
|
||||
|
||||
public function show()
|
||||
{
|
||||
$this->setFilter();
|
||||
|
||||
$labels = $this->getLabels();
|
||||
|
||||
$income = array_values($this->calculateTotals('income'));
|
||||
$expense = array_values($this->calculateTotals('expense'));
|
||||
$profit = array_values($this->calculateProfit($income, $expense));
|
||||
|
||||
$colors = $this->getColors();
|
||||
|
||||
$options = [
|
||||
'chart' => [
|
||||
'stacked' => true,
|
||||
],
|
||||
'plotOptions' => [
|
||||
'bar' => [
|
||||
'columnWidth' => '40%',
|
||||
],
|
||||
],
|
||||
'legend' => [
|
||||
'position' => 'top',
|
||||
],
|
||||
];
|
||||
|
||||
$chart = new Apexcharts();
|
||||
|
||||
$chart->setType('line')
|
||||
->setOptions($options)
|
||||
->setLabels(array_values($labels))
|
||||
->setColors($colors)
|
||||
->setDataset(trans('general.incoming'), 'column', $income)
|
||||
->setDataset(trans('general.outgoing'), 'column', $expense)
|
||||
->setDataset(trans_choice('general.profits', 1), 'line', $profit);
|
||||
|
||||
$totals = [
|
||||
'incoming' => money(array_sum($income), setting('default.currency'), true),
|
||||
'outgoing' => money(abs(array_sum($expense)), setting('default.currency'), true),
|
||||
'profit' => money(array_sum($profit), setting('default.currency'), true),
|
||||
];
|
||||
|
||||
return $this->view('widgets.cash_flow', [
|
||||
'chart' => $chart,
|
||||
'totals' => $totals,
|
||||
]);
|
||||
}
|
||||
|
||||
public function setFilter(): void
|
||||
{
|
||||
$financial_start = $this->getFinancialStart()->format('Y-m-d');
|
||||
|
||||
@ -28,31 +86,35 @@ class CashFlow extends Widget
|
||||
$year_start = $financial_start;
|
||||
}
|
||||
|
||||
$start = Date::parse(request('start_date', $year_start));
|
||||
$end = Date::parse(request('end_date', Date::parse($year_start)->addYear(1)->subDays(1)->format('Y-m-d')));
|
||||
$period = request('period', 'month');
|
||||
$this->start_date = Date::parse(request('start_date', $year_start));
|
||||
$this->end_date = Date::parse(request('end_date', Date::parse($year_start)->addYear(1)->subDays(1)->format('Y-m-d')));
|
||||
$this->period = request('period', 'month');
|
||||
}
|
||||
|
||||
public function getLabels(): array
|
||||
{
|
||||
$range = request('range', 'custom');
|
||||
|
||||
$start_month = $start->month;
|
||||
$end_month = $end->month;
|
||||
$start_month = $this->start_date->month;
|
||||
$end_month = $this->end_date->month;
|
||||
|
||||
// Monthly
|
||||
$labels = [];
|
||||
|
||||
$s = clone $start;
|
||||
$s = clone $this->start_date;
|
||||
|
||||
if ($range == 'last_12_months') {
|
||||
$end_month = 12;
|
||||
$start_month = 0;
|
||||
} elseif ($range == 'custom') {
|
||||
$end_month = $end->diffInMonths($start);
|
||||
$end_month = $this->end_date->diffInMonths($this->start_date);
|
||||
$start_month = 0;
|
||||
}
|
||||
|
||||
for ($j = $end_month; $j >= $start_month; $j--) {
|
||||
$labels[$end_month - $j] = $s->format('M Y');
|
||||
|
||||
if ($period == 'month') {
|
||||
if ($this->period == 'month') {
|
||||
$s->addMonth();
|
||||
} else {
|
||||
$s->addMonths(3);
|
||||
@ -60,74 +122,43 @@ class CashFlow extends Widget
|
||||
}
|
||||
}
|
||||
|
||||
$income = $this->calculateTotals('income', $start, $end, $period);
|
||||
$expense = $this->calculateTotals('expense', $start, $end, $period);
|
||||
$profit = $this->calculateProfit($income, $expense);
|
||||
|
||||
$chart = new Chartjs();
|
||||
$chart->type('line')
|
||||
->width(0)
|
||||
->height(300)
|
||||
->options($this->getLineChartOptions())
|
||||
->labels(array_values($labels));
|
||||
|
||||
$chart->dataset(trans_choice('general.incomes', 1), 'line', array_values($income))
|
||||
->backgroundColor('#328aef')
|
||||
->color('#328aef')
|
||||
->options([
|
||||
'borderWidth' => 4,
|
||||
'pointStyle' => 'line',
|
||||
])
|
||||
->fill(false);
|
||||
|
||||
$chart->dataset(trans_choice('general.expenses', 2), 'line', array_values($expense))
|
||||
->backgroundColor('#ef3232')
|
||||
->color('#ef3232')
|
||||
->options([
|
||||
'borderWidth' => 4,
|
||||
'pointStyle' => 'line',
|
||||
])
|
||||
->fill(false);
|
||||
|
||||
$chart->dataset(trans_choice('general.profits', 1), 'line', array_values($profit))
|
||||
->backgroundColor('#6da252')
|
||||
->color('#6da252')
|
||||
->options([
|
||||
'borderWidth' => 4,
|
||||
'pointStyle' => 'line',
|
||||
])
|
||||
->fill(false);
|
||||
|
||||
return $this->view('widgets.line_chart', [
|
||||
'chart' => $chart,
|
||||
]);
|
||||
return $labels;
|
||||
}
|
||||
|
||||
private function calculateTotals($type, $start, $end, $period)
|
||||
public function getColors(): array
|
||||
{
|
||||
return [
|
||||
'#8bb475',
|
||||
'#fb7185',
|
||||
'#7779A2',
|
||||
];
|
||||
}
|
||||
|
||||
private function calculateTotals($type): array
|
||||
{
|
||||
$totals = [];
|
||||
|
||||
$date_format = 'Y-m';
|
||||
|
||||
if ($period == 'month') {
|
||||
if ($this->period == 'month') {
|
||||
$n = 1;
|
||||
$start_date = $start->format($date_format);
|
||||
$end_date = $end->format($date_format);
|
||||
$start_date = $this->start_date->format($date_format);
|
||||
$end_date = $this->end_date->format($date_format);
|
||||
$next_date = $start_date;
|
||||
} else {
|
||||
$n = 3;
|
||||
$start_date = $start->quarter;
|
||||
$end_date = $end->quarter;
|
||||
$start_date = $this->start_date->quarter;
|
||||
$end_date = $this->end_date->quarter;
|
||||
$next_date = $start_date;
|
||||
}
|
||||
|
||||
$s = clone $start;
|
||||
$s = clone $this->start_date;
|
||||
|
||||
//$totals[$start_date] = 0;
|
||||
while ($next_date <= $end_date) {
|
||||
$totals[$next_date] = 0;
|
||||
|
||||
if ($period == 'month') {
|
||||
if ($this->period == 'month') {
|
||||
$next_date = $s->addMonths($n)->format($date_format);
|
||||
} else {
|
||||
if (isset($totals[4])) {
|
||||
@ -138,17 +169,21 @@ class CashFlow extends Widget
|
||||
}
|
||||
}
|
||||
|
||||
$items = $this->applyFilters(Transaction::$type()->whereBetween('paid_at', [$start, $end])->isNotTransfer())->get();
|
||||
$items = $this->applyFilters(Transaction::$type()->whereBetween('paid_at', [$this->start_date, $this->end_date])->isNotTransfer())->get();
|
||||
|
||||
$this->setTotals($totals, $items, $date_format, $period);
|
||||
$this->setTotals($totals, $items, $date_format);
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
private function setTotals(&$totals, $items, $date_format, $period)
|
||||
private function setTotals(&$totals, $items, $date_format): void
|
||||
{
|
||||
$type = 'income';
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($period == 'month') {
|
||||
$type = $item->type;
|
||||
|
||||
if ($this->period == 'month') {
|
||||
$i = Date::parse($item->paid_at)->format($date_format);
|
||||
} else {
|
||||
$i = Date::parse($item->paid_at)->quarter;
|
||||
@ -164,20 +199,24 @@ class CashFlow extends Widget
|
||||
$precision = config('money.' . setting('default.currency') . '.precision');
|
||||
|
||||
foreach ($totals as $key => $value) {
|
||||
if ($type == 'expense') {
|
||||
$value = -1 * $value;
|
||||
}
|
||||
|
||||
$totals[$key] = round($value, $precision);
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateProfit($incomes, $expenses)
|
||||
private function calculateProfit($incomes, $expenses): array
|
||||
{
|
||||
$profit = [];
|
||||
|
||||
$precision = config('money.' . setting('default.currency') . '.precision');
|
||||
|
||||
foreach ($incomes as $key => $income) {
|
||||
if ($income > 0 && $income > $expenses[$key]) {
|
||||
$profit[$key] = $income - $expenses[$key];
|
||||
} else {
|
||||
$profit[$key] = 0;
|
||||
}
|
||||
$value = $income - abs($expenses[$key]);
|
||||
|
||||
$profit[$key] = round($value, $precision);
|
||||
}
|
||||
|
||||
return $profit;
|
||||
|
@ -9,13 +9,13 @@ class ExpensesByCategory extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.expenses_by_category';
|
||||
|
||||
public $default_settings = [
|
||||
'width' => 'col-md-6',
|
||||
];
|
||||
public $description = 'widgets.description.expenses_by_category';
|
||||
|
||||
public $report_class = 'App\Reports\ExpenseSummary';
|
||||
|
||||
public function show()
|
||||
{
|
||||
Category::with('expense_transactions')->expense()->each(function ($category) {
|
||||
Category::withSubCategory()->with('expense_transactions')->expense()->each(function ($category) {
|
||||
$amount = 0;
|
||||
|
||||
$this->applyFilters($category->expense_transactions)->each(function ($transaction) use (&$amount) {
|
||||
@ -25,7 +25,7 @@ class ExpensesByCategory extends Widget
|
||||
$this->addMoneyToDonut($category->color, $amount, $category->name);
|
||||
});
|
||||
|
||||
$chart = $this->getDonutChart(trans_choice('general.expenses', 2), 0, 160, 6);
|
||||
$chart = $this->getDonutChart(trans_choice('general.expenses', 2), '100%', 300, 6);
|
||||
|
||||
return $this->view('widgets.donut_chart', [
|
||||
'chart' => $chart,
|
||||
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Setting\Category;
|
||||
|
||||
class IncomeByCategory extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.income_by_category';
|
||||
|
||||
public $default_settings = [
|
||||
'width' => 'col-md-6',
|
||||
];
|
||||
|
||||
public function show()
|
||||
{
|
||||
Category::with('income_transactions')->income()->each(function ($category) {
|
||||
$amount = 0;
|
||||
|
||||
$this->applyFilters($category->income_transactions)->each(function ($transaction) use (&$amount) {
|
||||
$amount += $transaction->getAmountConvertedToDefault();
|
||||
});
|
||||
|
||||
$this->addMoneyToDonut($category->color, $amount, $category->name);
|
||||
});
|
||||
|
||||
$chart = $this->getDonutChart(trans_choice('general.incomes', 1), 0, 160, 6);
|
||||
|
||||
return $this->view('widgets.donut_chart', [
|
||||
'chart' => $chart,
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Banking\Transaction;
|
||||
|
||||
class LatestExpenses extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.latest_expenses';
|
||||
|
||||
public function show()
|
||||
{
|
||||
$transactions = $this->applyFilters(Transaction::with('category')->expense()->orderBy('paid_at', 'desc')->isNotTransfer()->take(5))->get();
|
||||
|
||||
return $this->view('widgets.latest_expenses', [
|
||||
'transactions' => $transactions,
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Banking\Transaction;
|
||||
|
||||
class LatestIncome extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.latest_income';
|
||||
|
||||
public function show()
|
||||
{
|
||||
$transactions = $this->applyFilters(Transaction::with('category')->income()->orderBy('paid_at', 'desc')->isNotTransfer()->take(5))->get();
|
||||
|
||||
return $this->view('widgets.latest_income', [
|
||||
'transactions' => $transactions,
|
||||
]);
|
||||
}
|
||||
}
|
79
app/Widgets/Payables.php
Normal file
79
app/Widgets/Payables.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Document\Document;
|
||||
use App\Utilities\Date;
|
||||
|
||||
class Payables extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.payables';
|
||||
|
||||
public $description = 'widgets.description.payables';
|
||||
|
||||
public $report_class = 'Modules\AgedReceivablesPayables\Reports\AgedPayables';
|
||||
|
||||
public function show()
|
||||
{
|
||||
$open = $overdue = 0;
|
||||
|
||||
$periods = [
|
||||
'overdue_1_30' => 0,
|
||||
'overdue_30_60' => 0,
|
||||
'overdue_60_90' => 0,
|
||||
'overdue_90_un' => 0,
|
||||
];
|
||||
|
||||
$query = Document::bill()->with('transactions')->accrued()->notPaid();
|
||||
|
||||
$this->applyFilters($query, ['date_field' => 'issued_at'])->each(function ($bill) use (&$open, &$overdue, &$periods) {
|
||||
list($open_tmp, $overdue_tmp) = $this->calculateDocumentTotals($bill);
|
||||
|
||||
$open += $open_tmp;
|
||||
$overdue += $overdue_tmp;
|
||||
|
||||
foreach ($periods as $period_name => $period_amount) {
|
||||
$arr = explode('_', $period_name);
|
||||
|
||||
if ($arr[2] == 'un') {
|
||||
$arr[2] = '9999';
|
||||
}
|
||||
|
||||
$start = Date::today()->subDays($arr[2])->toDateString() . ' 00:00:00';
|
||||
$end = Date::today()->subDays($arr[1])->toDateString() . ' 23:59:59';
|
||||
|
||||
if (! Date::parse($bill->due_at)->isBetween($start, $end)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$periods[$period_name] += $overdue_tmp;
|
||||
}
|
||||
});
|
||||
|
||||
foreach ($periods as $period_name => $period_amount) {
|
||||
$periods[$period_name] = money($period_amount, setting('default.currency'), true);
|
||||
}
|
||||
|
||||
$has_progress = !empty($open) || !empty($overdue);
|
||||
$progress = !empty($open) ? (int) ($open * 100) / ($open + $overdue) : 0;
|
||||
|
||||
$grand = $open + $overdue;
|
||||
|
||||
$totals = [
|
||||
'grand' => money($grand, setting('default.currency'), true),
|
||||
'open' => money($open, setting('default.currency'), true),
|
||||
'overdue' => money($overdue, setting('default.currency'), true),
|
||||
];
|
||||
|
||||
$grand_total_text = trans('widgets.total_unpaid_bills');
|
||||
|
||||
return $this->view('widgets.receivables_payables', [
|
||||
'totals' => $totals,
|
||||
'has_progress' => $has_progress,
|
||||
'progress' => $progress,
|
||||
'periods' => $periods,
|
||||
'grand_total_text' => $grand_total_text,
|
||||
]);
|
||||
}
|
||||
}
|
217
app/Widgets/ProfitLoss.php
Normal file
217
app/Widgets/ProfitLoss.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Utilities\Recurring;
|
||||
use App\Models\Document\Document;
|
||||
use App\Models\Banking\Transaction;
|
||||
use Akaunting\Apexcharts\Charts as Apexcharts;
|
||||
use App\Traits\Currencies;
|
||||
use App\Traits\DateTime;
|
||||
use App\Utilities\Date;
|
||||
|
||||
class ProfitLoss extends Widget
|
||||
{
|
||||
use Currencies, DateTime;
|
||||
|
||||
public $default_name = 'widgets.profit_loss';
|
||||
|
||||
public $description = 'widgets.description.profit_loss';
|
||||
|
||||
public $report_class = 'App\Reports\ProfitLoss';
|
||||
|
||||
public $start_date;
|
||||
|
||||
public $end_date;
|
||||
|
||||
public $period;
|
||||
|
||||
public function show()
|
||||
{
|
||||
$this->setFilter();
|
||||
|
||||
$labels = $this->getLabels();
|
||||
|
||||
$income = $this->getIncome();
|
||||
|
||||
$expense = $this->getExpense();
|
||||
|
||||
$colors = $this->getColors();
|
||||
|
||||
$chart = new Apexcharts();
|
||||
|
||||
$options = [
|
||||
'legend' => [
|
||||
'position' => 'top',
|
||||
'markers' => [
|
||||
'radius' => '12',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$chart->setType('bar')
|
||||
->setOptions($options)
|
||||
->setLabels(array_values($labels))
|
||||
->setColors($colors)
|
||||
->setDataset(trans_choice('general.incomes', 1), 'column', array_values($income))
|
||||
->setDataset(trans_choice('general.expenses', 1), 'column', array_values($expense));
|
||||
|
||||
return $this->view('widgets.bar_chart', [
|
||||
'chart' => $chart,
|
||||
]);
|
||||
}
|
||||
|
||||
public function setFilter(): void
|
||||
{
|
||||
$financial_start = $this->getFinancialStart()->format('Y-m-d');
|
||||
|
||||
// check and assign year start
|
||||
if (($year_start = Date::today()->startOfYear()->format('Y-m-d')) !== $financial_start) {
|
||||
$year_start = $financial_start;
|
||||
}
|
||||
|
||||
$this->start_date = Date::parse(request('start_date', $year_start));
|
||||
$this->end_date = Date::parse(request('end_date', Date::parse($year_start)->addYear(1)->subDays(1)->format('Y-m-d')));
|
||||
$this->period = request('period', 'month');
|
||||
}
|
||||
|
||||
public function getLabels(): array
|
||||
{
|
||||
$range = request('range', 'custom');
|
||||
|
||||
$start_month = $this->start_date->month;
|
||||
$end_month = $this->end_date->month;
|
||||
|
||||
// Monthly
|
||||
$labels = [];
|
||||
|
||||
$s = clone $this->start_date;
|
||||
|
||||
if ($range == 'last_12_months') {
|
||||
$end_month = 12;
|
||||
$start_month = 0;
|
||||
} elseif ($range == 'custom') {
|
||||
$end_month = $this->end_date->diffInMonths($this->start_date);
|
||||
$start_month = 0;
|
||||
}
|
||||
|
||||
for ($j = $end_month; $j >= $start_month; $j--) {
|
||||
$labels[$end_month - $j] = $s->format('M Y');
|
||||
|
||||
if ($this->period == 'month') {
|
||||
$s->addMonth();
|
||||
} else {
|
||||
$s->addMonths(3);
|
||||
$j -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
public function getIncome(): array
|
||||
{
|
||||
// Invoices
|
||||
$query = Document::invoice()->with('recurring', 'totals', 'transactions')->accrued();
|
||||
$invoices = $this->applyFilters($query, ['date_field' => 'issued_at'])->get();
|
||||
Recurring::reflect($invoices, 'issued_at');
|
||||
$totals = $this->calculateTotals($invoices, 'issued_at');
|
||||
|
||||
// Transactions
|
||||
$query = Transaction::with('recurring')->income()->isNotDocument()->isNotTransfer();
|
||||
$transactions = $this->applyFilters($query, ['date_field' => 'paid_at'])->get();
|
||||
Recurring::reflect($transactions, 'paid_at');
|
||||
$totals = $this->calculateTotals($transactions, 'paid_at', $totals);
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
public function getExpense(): array
|
||||
{
|
||||
// Bills
|
||||
$query = Document::bill()->with('recurring', 'totals', 'transactions')->accrued();
|
||||
$bills = $this->applyFilters($query, ['date_field' => 'issued_at'])->get();
|
||||
Recurring::reflect($bills, 'issued_at');
|
||||
$totals = $this->calculateTotals($bills, 'issued_at');
|
||||
|
||||
// Transactions
|
||||
$query = Transaction::with('recurring')->expense()->isNotDocument()->isNotTransfer();
|
||||
$transactions = $this->applyFilters($query, ['date_field' => 'paid_at'])->get();
|
||||
Recurring::reflect($transactions, 'paid_at');
|
||||
$totals = $this->calculateTotals($transactions, 'paid_at', $totals);
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
public function getColors(): array
|
||||
{
|
||||
return [
|
||||
'#8bb475',
|
||||
'#fb7185',
|
||||
];
|
||||
}
|
||||
|
||||
public function calculateTotals($items, $date_field, $totals = []): array
|
||||
{
|
||||
$date_format = 'Y-m';
|
||||
|
||||
if ($this->period == 'month') {
|
||||
$n = 1;
|
||||
$start_date = $this->start_date->format($date_format);
|
||||
$end_date = $this->end_date->format($date_format);
|
||||
$next_date = $start_date;
|
||||
} else {
|
||||
$n = 3;
|
||||
$start_date = $this->start_date->quarter;
|
||||
$end_date = $this->end_date->quarter;
|
||||
$next_date = $start_date;
|
||||
}
|
||||
|
||||
$s = clone $this->start_date;
|
||||
|
||||
//$totals[$start_date] = 0;
|
||||
while ($next_date <= $end_date) {
|
||||
if (! isset($totals[$next_date])) {
|
||||
$totals[$next_date] = 0;
|
||||
}
|
||||
|
||||
if ($this->period == 'month') {
|
||||
$next_date = $s->addMonths($n)->format($date_format);
|
||||
} else {
|
||||
if (isset($totals[4])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$next_date = $s->addMonths($n)->quarter;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setTotals($totals, $items, $date_field, $date_format);
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
public function setTotals(&$totals, $items, $date_field, $date_format): void
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
if ($this->period == 'month') {
|
||||
$i = Date::parse($item->$date_field)->format($date_format);
|
||||
} else {
|
||||
$i = Date::parse($item->$date_field)->quarter;
|
||||
}
|
||||
|
||||
if (! isset($totals[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$totals[$i] += $item->getAmountConvertedToDefault();
|
||||
}
|
||||
|
||||
$precision = config('money.' . setting('default.currency') . '.precision');
|
||||
|
||||
foreach ($totals as $key => $value) {
|
||||
$totals[$key] = round($value, $precision);
|
||||
}
|
||||
}
|
||||
}
|
79
app/Widgets/Receivables.php
Normal file
79
app/Widgets/Receivables.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Document\Document;
|
||||
use App\Utilities\Date;
|
||||
|
||||
class Receivables extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.receivables';
|
||||
|
||||
public $description = 'widgets.description.receivables';
|
||||
|
||||
public $report_class = 'Modules\AgedReceivablesPayables\Reports\AgedReceivables';
|
||||
|
||||
public function show()
|
||||
{
|
||||
$open = $overdue = 0;
|
||||
|
||||
$periods = [
|
||||
'overdue_1_30' => 0,
|
||||
'overdue_30_60' => 0,
|
||||
'overdue_60_90' => 0,
|
||||
'overdue_90_un' => 0,
|
||||
];
|
||||
|
||||
$query = Document::invoice()->with('transactions')->accrued()->notPaid();
|
||||
|
||||
$this->applyFilters($query, ['date_field' => 'issued_at'])->each(function ($invoice) use (&$open, &$overdue, &$periods) {
|
||||
list($open_tmp, $overdue_tmp) = $this->calculateDocumentTotals($invoice);
|
||||
|
||||
$open += $open_tmp;
|
||||
$overdue += $overdue_tmp;
|
||||
|
||||
foreach ($periods as $period_name => $period_amount) {
|
||||
$arr = explode('_', $period_name);
|
||||
|
||||
if ($arr[2] == 'un') {
|
||||
$arr[2] = '9999';
|
||||
}
|
||||
|
||||
$start = Date::today()->subDays($arr[2])->toDateString() . ' 00:00:00';
|
||||
$end = Date::today()->subDays($arr[1])->toDateString() . ' 23:59:59';
|
||||
|
||||
if (! Date::parse($invoice->due_at)->isBetween($start, $end)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$periods[$period_name] += $overdue_tmp;
|
||||
}
|
||||
});
|
||||
|
||||
foreach ($periods as $period_name => $period_amount) {
|
||||
$periods[$period_name] = money($period_amount, setting('default.currency'), true);
|
||||
}
|
||||
|
||||
$has_progress = !empty($open) || !empty($overdue);
|
||||
$progress = !empty($open) ? (int) ($open * 100) / ($open + $overdue) : 0;
|
||||
|
||||
$grand = $open + $overdue;
|
||||
|
||||
$totals = [
|
||||
'grand' => money($grand, setting('default.currency'), true),
|
||||
'open' => money($open, setting('default.currency'), true),
|
||||
'overdue' => money($overdue, setting('default.currency'), true),
|
||||
];
|
||||
|
||||
$grand_total_text = trans('widgets.total_unpaid_invoices');
|
||||
|
||||
return $this->view('widgets.receivables_payables', [
|
||||
'totals' => $totals,
|
||||
'has_progress' => $has_progress,
|
||||
'progress' => $progress,
|
||||
'periods' => $periods,
|
||||
'grand_total_text' => $grand_total_text,
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Banking\Transaction;
|
||||
use App\Models\Document\Document;
|
||||
|
||||
class TotalExpenses extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.total_expenses';
|
||||
|
||||
public $views = [
|
||||
'header' => 'partials.widgets.stats_header',
|
||||
];
|
||||
|
||||
public function show()
|
||||
{
|
||||
$current = $open = $overdue = 0;
|
||||
|
||||
$this->applyFilters(Transaction::expense()->isNotTransfer())->each(function ($transaction) use (&$current) {
|
||||
$current += $transaction->getAmountConvertedToDefault();
|
||||
});
|
||||
|
||||
$this->applyFilters(
|
||||
Document::bill()->with('transactions')->accrued()->notPaid(),
|
||||
['date_field' => 'created_at']
|
||||
)->each(
|
||||
function ($bill) use (&$open, &$overdue) {
|
||||
list($open_tmp, $overdue_tmp) = $this->calculateDocumentTotals($bill);
|
||||
|
||||
$open += $open_tmp;
|
||||
$overdue += $overdue_tmp;
|
||||
}
|
||||
);
|
||||
|
||||
$grand = $current + $open + $overdue;
|
||||
|
||||
$progress = 100;
|
||||
|
||||
if (!empty($open) && !empty($overdue)) {
|
||||
$progress = (int) ($open * 100) / ($open + $overdue);
|
||||
}
|
||||
|
||||
$totals = [
|
||||
'grand' => money($grand, setting('default.currency'), true),
|
||||
'open' => money($open, setting('default.currency'), true),
|
||||
'overdue' => money($overdue, setting('default.currency'), true),
|
||||
'progress' => $progress,
|
||||
];
|
||||
|
||||
return $this->view('widgets.total_expenses', [
|
||||
'totals' => $totals,
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Banking\Transaction;
|
||||
use App\Models\Document\Document;
|
||||
|
||||
class TotalIncome extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.total_income';
|
||||
|
||||
public $views = [
|
||||
'header' => 'partials.widgets.stats_header',
|
||||
];
|
||||
|
||||
public function show()
|
||||
{
|
||||
$current = $open = $overdue = 0;
|
||||
|
||||
$this->applyFilters(Transaction::income()->isNotTransfer())->each(function ($transaction) use (&$current) {
|
||||
$current += $transaction->getAmountConvertedToDefault();
|
||||
});
|
||||
|
||||
$this->applyFilters(
|
||||
Document::invoice()->with('transactions')->accrued()->notPaid(),
|
||||
['date_field' => 'created_at']
|
||||
)->each(
|
||||
function ($invoice) use (&$open, &$overdue) {
|
||||
list($open_tmp, $overdue_tmp) = $this->calculateDocumentTotals($invoice);
|
||||
|
||||
$open += $open_tmp;
|
||||
$overdue += $overdue_tmp;
|
||||
}
|
||||
);
|
||||
|
||||
$grand = $current + $open + $overdue;
|
||||
|
||||
$progress = 100;
|
||||
|
||||
if (!empty($open) && !empty($overdue)) {
|
||||
$progress = (int) ($open * 100) / ($open + $overdue);
|
||||
}
|
||||
|
||||
$totals = [
|
||||
'grand' => money($grand, setting('default.currency'), true),
|
||||
'open' => money($open, setting('default.currency'), true),
|
||||
'overdue' => money($overdue, setting('default.currency'), true),
|
||||
'progress' => $progress,
|
||||
];
|
||||
|
||||
return $this->view('widgets.total_income', [
|
||||
'totals' => $totals,
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Widgets;
|
||||
|
||||
use App\Abstracts\Widget;
|
||||
use App\Models\Banking\Transaction;
|
||||
use App\Models\Document\Document;
|
||||
|
||||
class TotalProfit extends Widget
|
||||
{
|
||||
public $default_name = 'widgets.total_profit';
|
||||
|
||||
public $views = [
|
||||
'header' => 'partials.widgets.stats_header',
|
||||
];
|
||||
|
||||
public function show()
|
||||
{
|
||||
$current_income = $open_invoice = $overdue_invoice = 0;
|
||||
$current_expenses = $open_bill = $overdue_bill = 0;
|
||||
|
||||
$this->applyFilters(Transaction::isNotTransfer())->each(function ($transaction) use (&$current_income, &$current_expenses) {
|
||||
$amount = $transaction->getAmountConvertedToDefault();
|
||||
|
||||
if ($transaction->type == 'income') {
|
||||
$current_income += $amount;
|
||||
} else {
|
||||
$current_expenses += $amount;
|
||||
}
|
||||
});
|
||||
|
||||
$this->applyFilters(
|
||||
Document::invoice()->with('transactions')->accrued()->notPaid(),
|
||||
['date_field' => 'created_at']
|
||||
)->each(
|
||||
function ($invoice) use (&$open_invoice, &$overdue_invoice) {
|
||||
list($open_tmp, $overdue_tmp) = $this->calculateDocumentTotals($invoice);
|
||||
|
||||
$open_invoice += $open_tmp;
|
||||
$overdue_invoice += $overdue_tmp;
|
||||
}
|
||||
);
|
||||
|
||||
$this->applyFilters(
|
||||
Document::bill()->with('transactions')->accrued()->notPaid(),
|
||||
['date_field' => 'created_at']
|
||||
)->each(
|
||||
function ($bill) use (&$open_bill, &$overdue_bill) {
|
||||
list($open_tmp, $overdue_tmp) = $this->calculateDocumentTotals($bill);
|
||||
|
||||
$open_bill += $open_tmp;
|
||||
$overdue_bill += $overdue_tmp;
|
||||
}
|
||||
);
|
||||
|
||||
$current = $current_income - $current_expenses;
|
||||
$open = $open_invoice - $open_bill;
|
||||
$overdue = $overdue_invoice - $overdue_bill;
|
||||
|
||||
$grand = $current + $open + $overdue;
|
||||
|
||||
$progress = 100;
|
||||
|
||||
if (!empty($open) && !empty($overdue)) {
|
||||
$progress = (int) ($open * 100) / ($open + $overdue);
|
||||
}
|
||||
|
||||
$totals = [
|
||||
'grand' => money($grand, setting('default.currency'), true),
|
||||
'open' => money($open, setting('default.currency'), true),
|
||||
'overdue' => money($overdue, setting('default.currency'), true),
|
||||
'progress' => $progress,
|
||||
];
|
||||
|
||||
return $this->view('widgets.total_profit', [
|
||||
'totals' => $totals,
|
||||
]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user