Merge branch 'master' of github.com:akaunting/akaunting

This commit is contained in:
Cüneyt Şentürk 2020-01-04 15:08:18 +03:00
commit 366b855d53
29 changed files with 181 additions and 272 deletions

View File

@ -8,22 +8,15 @@ use Illuminate\Support\Arr;
abstract class FormRequest extends BaseFormRequest abstract class FormRequest extends BaseFormRequest
{ {
/** /**
* Set the company id to the request. * Prepare the data for validation.
* *
* @return \Illuminate\Contracts\Validation\Validator * @return void
*/ */
protected function getValidatorInstance() protected function prepareForValidation()
{ {
// Get request data $this->merge([
$data = $this->all(); 'company_id' => session('company_id'),
]);
// Add active company id
$data['company_id'] = session('company_id');
// Reset the request data
$this->getInputSource()->replace($data);
return parent::getValidatorInstance();
} }
/** /**

View File

@ -32,8 +32,6 @@ abstract class Report
public $filters = []; public $filters = [];
public $category = 'income-expense';
public $icon = 'fa fa-chart-pie'; public $icon = 'fa fa-chart-pie';
public $indents = [ public $indents = [
@ -80,14 +78,14 @@ abstract class Report
abstract public function getTotals(); abstract public function getTotals();
public function getName() public function getDefaultName()
{ {
return Str::title(str_replace('_', ' ', Str::snake((new \ReflectionClass($this))->getShortName()))); return Str::title(str_replace('_', ' ', Str::snake((new \ReflectionClass($this))->getShortName())));
} }
public function getCategory() public function getCategory()
{ {
return $this->category; return trans('reports.income_expense');
} }
public function getIcon() public function getIcon()

View File

@ -19,24 +19,23 @@ class Reports extends Controller
*/ */
public function index() public function index()
{ {
$classes = []; $classes = $categories = [];
$reports = ['income-expense' => [], 'accounting' => []];
$items = Report::collect(); $reports = Report::collect();
foreach ($items as $item) { foreach ($reports as $report) {
$class = Utility::getClassInstance($item); $class = Utility::getClassInstance($report);
if (!$class->canRead()) { if (!$class->canRead()) {
continue; continue;
} }
$reports[$class->getCategory()][] = $item; $classes[$report->id] = $class;
$classes[$item->id] = $class; $categories[$class->getCategory()][] = $report;
} }
return view('common.reports.index', compact('reports', 'classes')); return view('common.reports.index', compact('categories', 'classes'));
} }
/** /**

View File

@ -9,8 +9,6 @@ use App\Utilities\Recurring;
class ExpenseSummary extends Report class ExpenseSummary extends Report
{ {
public $category = 'income-expense';
public $icon = 'fa fa-shopping-cart'; public $icon = 'fa fa-shopping-cart';
public $chart = [ public $chart = [
@ -28,11 +26,16 @@ class ExpenseSummary extends Report
], ],
]; ];
public function getName() public function getDefaultName()
{ {
return trans('reports.summary.expense'); return trans('reports.summary.expense');
} }
public function getCategory()
{
return trans('reports.income_expense');
}
public function getTotals() public function getTotals()
{ {
$payments = $this->applyFilters(Transaction::type('expense')->isNotTransfer(), ['date_field' => 'paid_at'])->get(); $payments = $this->applyFilters(Transaction::type('expense')->isNotTransfer(), ['date_field' => 'paid_at'])->get();

View File

@ -10,15 +10,18 @@ use App\Utilities\Recurring;
class IncomeExpenseSummary extends Report class IncomeExpenseSummary extends Report
{ {
public $category = 'income-expense';
public $icon = 'fa fa-chart-pie'; public $icon = 'fa fa-chart-pie';
public function getName() public function getDefaultName()
{ {
return trans('reports.summary.income_expense'); return trans('reports.summary.income_expense');
} }
public function getCategory()
{
return trans('reports.income_expense');
}
public function getTotals() public function getTotals()
{ {
$income_transactions = $this->applyFilters(Transaction::type('income')->isNotTransfer(), ['date_field' => 'paid_at'])->get(); $income_transactions = $this->applyFilters(Transaction::type('income')->isNotTransfer(), ['date_field' => 'paid_at'])->get();

View File

@ -9,8 +9,6 @@ use App\Utilities\Recurring;
class IncomeSummary extends Report class IncomeSummary extends Report
{ {
public $category = 'income-expense';
public $icon = 'fa fa-money-bill'; public $icon = 'fa fa-money-bill';
public $chart = [ public $chart = [
@ -28,11 +26,16 @@ class IncomeSummary extends Report
], ],
]; ];
public function getName() public function getDefaultName()
{ {
return trans('reports.summary.income'); return trans('reports.summary.income');
} }
public function getCategory()
{
return trans('reports.income_expense');
}
public function getTotals() public function getTotals()
{ {
$transactions = $this->applyFilters(Transaction::type('income')->isNotTransfer(), ['date_field' => 'paid_at'])->get(); $transactions = $this->applyFilters(Transaction::type('income')->isNotTransfer(), ['date_field' => 'paid_at'])->get();

View File

@ -11,17 +11,20 @@ use App\Utilities\Recurring;
class ProfitLoss extends Report class ProfitLoss extends Report
{ {
public $category = 'accounting';
public $icon = 'fa fa-heart'; public $icon = 'fa fa-heart';
public $chart = false; public $chart = false;
public function getName() public function getDefaultName()
{ {
return trans('reports.profit_loss'); return trans('reports.profit_loss');
} }
public function getCategory()
{
return trans('general.accounting');
}
public function setViews() public function setViews()
{ {
parent::setViews(); parent::setViews();

View File

@ -15,17 +15,20 @@ class TaxSummary extends Report
{ {
use Currencies; use Currencies;
public $category = 'accounting';
public $icon = 'fa fa-percent'; public $icon = 'fa fa-percent';
public $chart = false; public $chart = false;
public function getName() public function getDefaultName()
{ {
return trans('reports.summary.tax'); return trans('reports.summary.tax');
} }
public function getCategory()
{
return trans('general.accounting');
}
public function setViews() public function setViews()
{ {
parent::setViews(); parent::setViews();

View File

@ -2,6 +2,7 @@
namespace App\Utilities; namespace App\Utilities;
use App\Models\Common\Report;
use App\Models\Module\Module; use App\Models\Module\Module;
class Reports class Reports
@ -10,7 +11,7 @@ class Reports
{ {
$classes = []; $classes = [];
$core_classes = [ $list = [
'App\Reports\IncomeSummary', 'App\Reports\IncomeSummary',
'App\Reports\ExpenseSummary', 'App\Reports\ExpenseSummary',
'App\Reports\IncomeExpenseSummary', 'App\Reports\IncomeExpenseSummary',
@ -18,35 +19,25 @@ class Reports
'App\Reports\ProfitLoss', 'App\Reports\ProfitLoss',
]; ];
static::parseClasses($classes, $core_classes); Module::enabled()->each(function ($module) use (&$list) {
$modules = Module::enabled()->get();
foreach ($modules as $module) {
$m = module($module->alias); $m = module($module->alias);
// Check if the module exists and has reports
if (!$m || empty($m->get('reports'))) { if (!$m || empty($m->get('reports'))) {
continue; return;
} }
static::parseClasses($classes, $m->get('reports')); $list = array_merge($list, (array) $m->get('reports'));
} });
return $classes;
}
protected static function parseClasses(&$classes, $list)
{
foreach ($list as $class) { foreach ($list as $class) {
if (!class_exists($class)) { if (!class_exists($class)) {
continue; continue;
} }
$name = (new $class())->getName(); $classes[$class] = (new $class())->getDefaultName();
$classes[$class] = $name;
} }
return $classes;
} }
public static function getGroups() public static function getGroups()
@ -81,8 +72,18 @@ class Reports
]; ];
} }
public static function getClassInstance($report, $get_totals = true) public static function getClassInstance($model, $get_totals = true)
{ {
return (new $report->class($report, $get_totals)); if (is_string($model)) {
$model = Report::where('class', $model)->first();
}
if ((!$model instanceof Report) || !class_exists($model->class)) {
return false;
}
$class = $model->class;
return new $class($model, $get_totals);
} }
} }

View File

@ -2,6 +2,7 @@
namespace App\Utilities; namespace App\Utilities;
use App\Models\Common\Widget;
use App\Models\Module\Module; use App\Models\Module\Module;
class Widgets class Widgets
@ -10,7 +11,7 @@ class Widgets
{ {
$classes = []; $classes = [];
$core_classes = [ $list = [
'App\Widgets\TotalIncome', 'App\Widgets\TotalIncome',
'App\Widgets\TotalExpenses', 'App\Widgets\TotalExpenses',
'App\Widgets\TotalProfit', 'App\Widgets\TotalProfit',
@ -22,46 +23,48 @@ class Widgets
'App\Widgets\LatestExpenses', 'App\Widgets\LatestExpenses',
]; ];
static::parseClasses($classes, $core_classes); Module::enabled()->each(function ($module) use (&$list) {
$modules = Module::enabled()->get();
foreach ($modules as $module) {
$m = module($module->alias); $m = module($module->alias);
// Check if the module exists and has widgets
if (!$m || empty($m->get('widgets'))) { if (!$m || empty($m->get('widgets'))) {
continue; return;
} }
static::parseClasses($classes, $m->get('widgets')); $list = array_merge($list, (array) $m->get('widgets'));
} });
return $classes;
}
protected static function parseClasses(&$classes, $list)
{
foreach ($list as $class) { foreach ($list as $class) {
if (!class_exists($class)) { if (!class_exists($class)) {
continue; continue;
} }
$name = (new $class())->getDefaultName(); $classes[$class] = (new $class())->getDefaultName();
$classes[$class] = $name;
}
} }
public static function getInstance($model) return $classes;
}
public static function getClassInstance($model)
{ {
if (is_string($model)) {
$model = Widget::where('class', $model)->first();
}
if ((!$model instanceof Widget) || !class_exists($model->class)) {
return false;
}
$class = $model->class; $class = $model->class;
return new $class($model); return new $class($model);
} }
public static function show($model) public static function show($model, ...$arguments)
{ {
return static::getInstance($model)->show(); if (!$class = static::getClassInstance($model)) {
return '';
}
return $class->show(...$arguments);
} }
} }

View File

@ -41,12 +41,16 @@ if (!function_exists('company_date')) {
if (!function_exists('show_widget')) { if (!function_exists('show_widget')) {
/** /**
* Format the given date based on company settings. * Show a widget.
* *
* @return string * @return string
*/ */
function show_widget($model) function show_widget()
{ {
return Widgets::show($model); $arguments = func_get_args();
$model = array_shift($arguments);
return Widgets::show($model, ...$arguments);
} }
} }

View File

@ -92,7 +92,7 @@ class CashFlow extends Widget
]) ])
->fill(false); ->fill(false);
return $this->view('widgets.cash_flow', [ return $this->view('widgets.line_chart', [
'chart' => $chart, 'chart' => $chart,
]); ]);
} }

View File

@ -23,7 +23,7 @@ class ExpensesByCategory extends Widget
$chart = $this->getDonutChart(trans_choice('general.expenses', 2), 0, 160, 6); $chart = $this->getDonutChart(trans_choice('general.expenses', 2), 0, 160, 6);
return $this->view('widgets.expenses_by_category', [ return $this->view('widgets.donut_chart', [
'chart' => $chart, 'chart' => $chart,
]); ]);
} }

View File

@ -23,7 +23,7 @@ class IncomeByCategory extends Widget
$chart = $this->getDonutChart(trans_choice('general.incomes', 1), 0, 160, 6); $chart = $this->getDonutChart(trans_choice('general.incomes', 1), 0, 160, 6);
return $this->view('widgets.income_by_category', [ return $this->view('widgets.donut_chart', [
'chart' => $chart, 'chart' => $chart,
]); ]);
} }

View File

@ -23,7 +23,6 @@ class CreateReportsTable extends Migration
$table->string('period'); $table->string('period');
$table->string('basis'); $table->string('basis');
$table->string('chart'); $table->string('chart');
$table->boolean('enabled');
$table->timestamps(); $table->timestamps();
$table->softDeletes(); $table->softDeletes();

View File

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class DropEnabledColumnReportsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('reports', function (Blueprint $table) {
$table->dropColumn('enabled');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('reports', function (Blueprint $table) {
$table->boolean('enabled')->default(1);
});
}
}

View File

@ -36,8 +36,10 @@ class InstallCommand extends Command
$old_company_id = session('company_id'); $old_company_id = session('company_id');
// Set company id
session(['company_id' => $company_id]); session(['company_id' => $company_id]);
setting()->setExtraColumns(['company_id' => $company_id]);
setting()->forgetAll();
setting()->load(true);
$module = module($alias); $module = module($alias);
@ -47,7 +49,6 @@ class InstallCommand extends Command
'enabled' => '1', 'enabled' => '1',
]); ]);
// Add history
ModuleHistory::create([ ModuleHistory::create([
'company_id' => $company_id, 'company_id' => $company_id,
'module_id' => $model->id, 'module_id' => $model->id,
@ -56,21 +57,21 @@ class InstallCommand extends Command
'description' => trans('modules.installed', ['module' => $alias]), 'description' => trans('modules.installed', ['module' => $alias]),
]); ]);
// Clear cache
$this->call('cache:clear'); $this->call('cache:clear');
// Update database // Update database
$this->call('migrate', ['--force' => true]); $this->call('migrate', ['--force' => true]);
// Trigger event
event(new \App\Events\Module\Installed($alias, $company_id)); event(new \App\Events\Module\Installed($alias, $company_id));
// Unset company id
session()->forget('company_id'); session()->forget('company_id');
setting()->forgetAll();
// Set company id
if (!empty($old_company_id)) { if (!empty($old_company_id)) {
session(['company_id' => $old_company_id]); session(['company_id' => $old_company_id]);
setting()->setExtraColumns(['company_id' => $old_company_id]);
setting()->load(true);
} }
$this->info('Module installed!'); $this->info('Module installed!');

View File

@ -39,13 +39,13 @@ button:focus
/*--------Forgot Text Finish--------*/ /*--------Forgot Text Finish--------*/
/*--------Dashboard Categories--------*/ /*--------Chart Donut Height--------*/
.dashboard-categories .chart-donut
{ {
position:relative !important; position:relative !important;
height:23vh !important; height:23vh !important;
} }
/*--------Dashboard Categories Finish--------*/ /*--------Chart Donut Height Finish--------*/
/*--------Cursor Pointer--------*/ /*--------Cursor Pointer--------*/

View File

@ -12,12 +12,12 @@
@section('content') @section('content')
<div class="row mb-4"> <div class="row mb-4">
@foreach($categories as $name => $reports)
<div class="col-md-12"> <div class="col-md-12">
<h3 id="stats">{{ trans('reports.income_expense') }}</h3> <h3>{{ $name }}</h3>
</div> </div>
@foreach($reports['income-expense'] as $report) @foreach($reports as $report)
<div class="col-md-4"> <div class="col-md-4">
<div class="card card-stats"> <div class="card card-stats">
<span> <span>
@ -63,56 +63,7 @@
</div> </div>
@endforeach @endforeach
<div class="col-md-12">
<h3 id="stats">{{ trans('general.accounting') }}</h3>
</div>
@foreach($reports['accounting'] as $report)
<div class="col-md-4">
<div class="card card-stats">
<span>
<div class="dropdown card-action-button">
<a class="btn btn-sm items-align-center py-2 mr-0 shadow-none--hover" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-ellipsis-v text-primary"></i>
</a>
<div class="dropdown-menu dropdown-menu-right dropdown-menu-arrow">
@permission('create-common-reports')
<a class="dropdown-item" href="{{ route('reports.edit', $report->id) }}">{{ trans('general.edit') }}</a>
<div class="dropdown-divider"></div>
@endpermission
@permission('delete-common-reports')
{!! Form::deleteLink($report, 'common/reports') !!}
@endpermission
</div>
</div>
</span>
<div class="card-body">
<div class="row">
<div class="col">
<a href="{{ route('reports.show', $report->id) }}">
<h5 class="card-title text-uppercase text-muted mb-0">{{ $report->name }}</h5>
<span class="h2 font-weight-bold mb-0">{{ $classes[$report->id]->getTotal() }}</span>
</a>
</div>
<div class="col-auto">
<a href="{{ route('reports.show', $report->id) }}">
<div class="icon icon-shape bg-orange text-white rounded-circle shadow">
<i class="{{ $classes[$report->id]->getIcon() }}"></i>
</div>
</a>
</div>
</div>
<p class="mt-3 mb-0 text-sm">
<a href="{{ route('reports.show', $report->id) }}">
<span class="text-nowrap">{{ $report->description }}</span>
</a>
</p>
</div>
</div>
</div>
@endforeach @endforeach
</div> </div>
@endsection @endsection

View File

@ -1,6 +1,6 @@
<div class="card-header"> <div class="card-header">
{!! Form::open([ {!! Form::open([
'url' => 'common/reports/' . $class->report->id . '/display', 'url' => 'common/reports/' . $class->report->id,
'role' => 'form', 'role' => 'form',
'method' => 'GET', 'method' => 'GET',
]) !!} ]) !!}

View File

@ -1,4 +1,4 @@
<div id="widgets-account-balance" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card"> <div class="card">
@include('partials.widgets.standard_header', ['header_class' => 'border-bottom-0']) @include('partials.widgets.standard_header', ['header_class' => 'border-bottom-0'])

View File

@ -1,9 +1,9 @@
<div id="widgets-income-by-category" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card"> <div class="card">
@include('partials.widgets.standard_header') @include('partials.widgets.standard_header')
<div class="card-body" id="income-category-doughnut"> <div class="card-body" id="widget-donut-{{ $model->id }}">
<div class="dashboard-categories"> <div class="chart-donut">
{!! $chart->container() !!} {!! $chart->container() !!}
</div> </div>
</div> </div>
@ -12,8 +12,8 @@
@push('charts') @push('charts')
<script> <script>
var income_category_doughnut = new Vue({ var widget_donut_{{ $model->id }} = new Vue({
el: '#income-category-doughnut', el: '#widget-donut-{{ $model->id }}',
}); });
</script> </script>
@endpush @endpush

View File

@ -1,23 +0,0 @@
<div id="widgets-expenses-by-category" class="{{ $model->settings->width }}">
<div class="card">
@include('partials.widgets.standard_header')
<div class="card-body" id="expenses-category-doughnut">
<div class="dashboard-categories">
{!! $chart->container() !!}
</div>
</div>
</div>
</div>
@push('charts')
<script>
var expenses_category_doughnut = new Vue({
el: '#expenses-category-doughnut',
});
</script>
@endpush
@push('body_scripts')
{!! $chart->script() !!}
@endpush

View File

@ -1,4 +1,4 @@
<div id="widgets-latest-expenses" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card"> <div class="card">
@include('partials.widgets.standard_header', ['header_class' => 'border-bottom-0']) @include('partials.widgets.standard_header', ['header_class' => 'border-bottom-0'])

View File

@ -1,4 +1,4 @@
<div id="widgets-latest-income" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card"> <div class="card">
@include('partials.widgets.standard_header', ['header_class' => 'border-bottom-0']) @include('partials.widgets.standard_header', ['header_class' => 'border-bottom-0'])

View File

@ -1,8 +1,8 @@
<div id="widgets-cash-flow" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card"> <div class="card">
@include('partials.widgets.standard_header') @include('partials.widgets.standard_header')
<div class="card-body" id="cashflow"> <div class="card-body" id="widget-line-{{ $model->id }}">
<div class="chart"> <div class="chart">
{!! $chart->container() !!} {!! $chart->container() !!}
</div> </div>
@ -12,8 +12,8 @@
@push('charts') @push('charts')
<script> <script>
var cash_flow = new Vue({ var widget_line_{{ $model->id }} = new Vue({
el: '#cashflow', el: '#widget-line-{{ $model->id }}',
}); });
</script> </script>
@endpush @endpush

View File

@ -1,4 +1,4 @@
<div id="widgets-total-expenses" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card bg-gradient-danger card-stats"> <div class="card bg-gradient-danger card-stats">
@include('partials.widgets.stats_header', ['header_class' => 'border-bottom-0']) @include('partials.widgets.stats_header', ['header_class' => 'border-bottom-0'])

View File

@ -1,4 +1,4 @@
<div id="widgets-total-income" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card bg-gradient-info card-stats"> <div class="card bg-gradient-info card-stats">
@include('partials.widgets.stats_header', ['header_class' => 'border-bottom-0']) @include('partials.widgets.stats_header', ['header_class' => 'border-bottom-0'])

View File

@ -1,4 +1,4 @@
<div id="widgets-total-profit" class="{{ $model->settings->width }}"> <div id="widget-{{ $model->id }}" class="{{ $model->settings->width }}">
<div class="card bg-gradient-success card-stats"> <div class="card bg-gradient-success card-stats">
@include('partials.widgets.stats_header', ['header_class' => 'border-bottom-0']) @include('partials.widgets.stats_header', ['header_class' => 'border-bottom-0'])