mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-31 15:34:35 -04:00
Improvements to reports
This commit is contained in:
parent
faa1889d19
commit
a63f694d09
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
Watch this [YouTube Video](https://www.youtube.com/watch?v=xHGKvadapbA) for an overview of the app's features.
|
Watch this [YouTube Video](https://www.youtube.com/watch?v=xHGKvadapbA) for an overview of the app's features.
|
||||||
|
|
||||||
All Pro and Enterprise features from the hosted app are included in the open-source code.
|
All Pro and Enterprise features from the hosted app are included in the open-source code. We offer a $20 per year white-label license to remove our branding.
|
||||||
|
|
||||||
The [self-host zip](https://www.invoiceninja.com/self-host/) includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies.
|
The [self-host zip](https://www.invoiceninja.com/self-host/) includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies.
|
||||||
|
|
||||||
|
@ -6,12 +6,9 @@ use Input;
|
|||||||
use Utils;
|
use Utils;
|
||||||
use DB;
|
use DB;
|
||||||
use Session;
|
use Session;
|
||||||
|
use Str;
|
||||||
use View;
|
use View;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\Client;
|
|
||||||
use App\Models\Payment;
|
|
||||||
use App\Models\Expense;
|
|
||||||
use App\Models\Task;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ReportController
|
* Class ReportController
|
||||||
@ -67,19 +64,22 @@ class ReportController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$reportTypes = [
|
$reportTypes = [
|
||||||
ENTITY_CLIENT => trans('texts.client'),
|
'client',
|
||||||
ENTITY_INVOICE => trans('texts.invoice'),
|
'product',
|
||||||
ENTITY_PRODUCT => trans('texts.product'),
|
'invoice',
|
||||||
ENTITY_PAYMENT => trans('texts.payment'),
|
'invoice_details',
|
||||||
ENTITY_EXPENSE => trans('texts.expense'),
|
'aging',
|
||||||
ENTITY_TASK => trans('texts.task'),
|
'profit_and_loss',
|
||||||
ENTITY_TAX_RATE => trans('texts.tax'),
|
'payment',
|
||||||
|
'expense',
|
||||||
|
'task',
|
||||||
|
'tax_rate',
|
||||||
];
|
];
|
||||||
|
|
||||||
$params = [
|
$params = [
|
||||||
'startDate' => $startDate->format('Y-m-d'),
|
'startDate' => $startDate->format('Y-m-d'),
|
||||||
'endDate' => $endDate->format('Y-m-d'),
|
'endDate' => $endDate->format('Y-m-d'),
|
||||||
'reportTypes' => $reportTypes,
|
'reportTypes' => array_combine($reportTypes, Utils::trans($reportTypes)),
|
||||||
'reportType' => $reportType,
|
'reportType' => $reportType,
|
||||||
'title' => trans('texts.charts_and_reports'),
|
'title' => trans('texts.charts_and_reports'),
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
@ -87,8 +87,18 @@ class ReportController extends BaseController
|
|||||||
|
|
||||||
if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
|
if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
|
||||||
$isExport = $action == 'export';
|
$isExport = $action == 'export';
|
||||||
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));
|
$reportClass = '\\App\\Ninja\\Reports\\' . Str::studly($reportType) . 'Report';
|
||||||
|
$options = [
|
||||||
|
'date_field' => $dateField,
|
||||||
|
'invoice_status' => request()->invoice_status,
|
||||||
|
'group_dates_by' => request()->group_dates_by,
|
||||||
|
];
|
||||||
|
$report = new $reportClass($startDate, $endDate, $isExport, $options);
|
||||||
|
if (Input::get('report_type')) {
|
||||||
|
$report->run();
|
||||||
|
}
|
||||||
|
$params['report'] = $report;
|
||||||
|
$params = array_merge($params, $report->results());
|
||||||
if ($isExport) {
|
if ($isExport) {
|
||||||
self::export($reportType, $params['displayData'], $params['columns'], $params['reportTotals']);
|
self::export($reportType, $params['displayData'], $params['columns'], $params['reportTotals']);
|
||||||
}
|
}
|
||||||
@ -96,427 +106,12 @@ class ReportController extends BaseController
|
|||||||
$params['columns'] = [];
|
$params['columns'] = [];
|
||||||
$params['displayData'] = [];
|
$params['displayData'] = [];
|
||||||
$params['reportTotals'] = [];
|
$params['reportTotals'] = [];
|
||||||
|
$params['report'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return View::make('reports.chart_builder', $params);
|
return View::make('reports.chart_builder', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $reportType
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $dateField
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generateReport($reportType, $startDate, $endDate, $dateField, $isExport)
|
|
||||||
{
|
|
||||||
if ($reportType == ENTITY_CLIENT) {
|
|
||||||
return $this->generateClientReport($startDate, $endDate, $isExport);
|
|
||||||
} elseif ($reportType == ENTITY_INVOICE) {
|
|
||||||
return $this->generateInvoiceReport($startDate, $endDate, $isExport);
|
|
||||||
} elseif ($reportType == ENTITY_PRODUCT) {
|
|
||||||
return $this->generateProductReport($startDate, $endDate, $isExport);
|
|
||||||
} elseif ($reportType == ENTITY_PAYMENT) {
|
|
||||||
return $this->generatePaymentReport($startDate, $endDate, $isExport);
|
|
||||||
} elseif ($reportType == ENTITY_TAX_RATE) {
|
|
||||||
return $this->generateTaxRateReport($startDate, $endDate, $dateField, $isExport);
|
|
||||||
} elseif ($reportType == ENTITY_EXPENSE) {
|
|
||||||
return $this->generateExpenseReport($startDate, $endDate, $isExport);
|
|
||||||
} elseif ($reportType == ENTITY_TASK) {
|
|
||||||
return $this->generateTaskReport($startDate, $endDate, $isExport);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generateTaskReport($startDate, $endDate, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['client', 'date', 'description', 'duration'];
|
|
||||||
$displayData = [];
|
|
||||||
|
|
||||||
$tasks = Task::scope()
|
|
||||||
->with('client.contacts')
|
|
||||||
->withArchived()
|
|
||||||
->dateRange($startDate, $endDate);
|
|
||||||
|
|
||||||
foreach ($tasks->get() as $task) {
|
|
||||||
$displayData[] = [
|
|
||||||
$task->client ? ($isExport ? $task->client->getDisplayName() : $task->client->present()->link) : trans('texts.unassigned'),
|
|
||||||
link_to($task->present()->url, $task->getStartTime()),
|
|
||||||
$task->present()->description,
|
|
||||||
Utils::formatTime($task->getDuration()),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $dateField
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generateTaxRateReport($startDate, $endDate, $dateField, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['tax_name', 'tax_rate', 'amount', 'paid'];
|
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
|
||||||
$displayData = [];
|
|
||||||
$reportTotals = [];
|
|
||||||
|
|
||||||
$clients = Client::scope()
|
|
||||||
->withArchived()
|
|
||||||
->with('contacts')
|
|
||||||
->with(['invoices' => function($query) use ($startDate, $endDate, $dateField) {
|
|
||||||
$query->with('invoice_items')->withArchived();
|
|
||||||
if ($dateField == FILTER_INVOICE_DATE) {
|
|
||||||
$query->where('invoice_date', '>=', $startDate)
|
|
||||||
->where('invoice_date', '<=', $endDate)
|
|
||||||
->with('payments');
|
|
||||||
} else {
|
|
||||||
$query->whereHas('payments', function($query) use ($startDate, $endDate) {
|
|
||||||
$query->where('payment_date', '>=', $startDate)
|
|
||||||
->where('payment_date', '<=', $endDate)
|
|
||||||
->withArchived();
|
|
||||||
})
|
|
||||||
->with(['payments' => function($query) use ($startDate, $endDate) {
|
|
||||||
$query->where('payment_date', '>=', $startDate)
|
|
||||||
->where('payment_date', '<=', $endDate)
|
|
||||||
->withArchived();
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
foreach ($clients->get() as $client) {
|
|
||||||
$currencyId = $client->currency_id ?: Auth::user()->account->getCurrencyId();
|
|
||||||
$amount = 0;
|
|
||||||
$paid = 0;
|
|
||||||
$taxTotals = [];
|
|
||||||
|
|
||||||
foreach ($client->invoices as $invoice) {
|
|
||||||
foreach ($invoice->getTaxes(true) as $key => $tax) {
|
|
||||||
if ( ! isset($taxTotals[$currencyId])) {
|
|
||||||
$taxTotals[$currencyId] = [];
|
|
||||||
}
|
|
||||||
if (isset($taxTotals[$currencyId][$key])) {
|
|
||||||
$taxTotals[$currencyId][$key]['amount'] += $tax['amount'];
|
|
||||||
$taxTotals[$currencyId][$key]['paid'] += $tax['paid'];
|
|
||||||
} else {
|
|
||||||
$taxTotals[$currencyId][$key] = $tax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$amount += $invoice->amount;
|
|
||||||
$paid += $invoice->getAmountPaid();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($taxTotals as $currencyId => $taxes) {
|
|
||||||
foreach ($taxes as $tax) {
|
|
||||||
$displayData[] = [
|
|
||||||
$tax['name'],
|
|
||||||
$tax['rate'] . '%',
|
|
||||||
$account->formatMoney($tax['amount'], $client),
|
|
||||||
$account->formatMoney($tax['paid'], $client)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $tax['amount']);
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $tax['paid']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => $reportTotals,
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generatePaymentReport($startDate, $endDate, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
|
||||||
$displayData = [];
|
|
||||||
$reportTotals = [];
|
|
||||||
|
|
||||||
$payments = Payment::scope()
|
|
||||||
->withArchived()
|
|
||||||
->excludeFailed()
|
|
||||||
->whereHas('client', function($query) {
|
|
||||||
$query->where('is_deleted', '=', false);
|
|
||||||
})
|
|
||||||
->whereHas('invoice', function($query) {
|
|
||||||
$query->where('is_deleted', '=', false);
|
|
||||||
})
|
|
||||||
->with('client.contacts', 'invoice', 'payment_type', 'account_gateway.gateway')
|
|
||||||
->where('payment_date', '>=', $startDate)
|
|
||||||
->where('payment_date', '<=', $endDate);
|
|
||||||
|
|
||||||
foreach ($payments->get() as $payment) {
|
|
||||||
$invoice = $payment->invoice;
|
|
||||||
$client = $payment->client;
|
|
||||||
$displayData[] = [
|
|
||||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
|
||||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
|
||||||
$invoice->present()->invoice_date,
|
|
||||||
$account->formatMoney($invoice->amount, $client),
|
|
||||||
$payment->present()->payment_date,
|
|
||||||
$account->formatMoney($payment->getCompletedAmount(), $client),
|
|
||||||
$payment->present()->method,
|
|
||||||
];
|
|
||||||
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment->getCompletedAmount());
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => $reportTotals,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generateInvoiceReport($startDate, $endDate, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
|
||||||
$displayData = [];
|
|
||||||
$reportTotals = [];
|
|
||||||
|
|
||||||
$clients = Client::scope()
|
|
||||||
->withTrashed()
|
|
||||||
->with('contacts')
|
|
||||||
->where('is_deleted', '=', false)
|
|
||||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
|
||||||
$query->invoices()
|
|
||||||
->withArchived()
|
|
||||||
->where('invoice_date', '>=', $startDate)
|
|
||||||
->where('invoice_date', '<=', $endDate)
|
|
||||||
->with(['payments' => function($query) {
|
|
||||||
$query->withArchived()
|
|
||||||
->excludeFailed()
|
|
||||||
->with('payment_type', 'account_gateway.gateway');
|
|
||||||
}, 'invoice_items'])
|
|
||||||
->withTrashed();
|
|
||||||
}]);
|
|
||||||
|
|
||||||
foreach ($clients->get() as $client) {
|
|
||||||
foreach ($client->invoices as $invoice) {
|
|
||||||
|
|
||||||
$payments = count($invoice->payments) ? $invoice->payments : [false];
|
|
||||||
foreach ($payments as $payment) {
|
|
||||||
$displayData[] = [
|
|
||||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
|
||||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
|
||||||
$invoice->present()->invoice_date,
|
|
||||||
$account->formatMoney($invoice->amount, $client),
|
|
||||||
$payment ? $payment->present()->payment_date : '',
|
|
||||||
$payment ? $account->formatMoney($payment->getCompletedAmount(), $client) : '',
|
|
||||||
$payment ? $payment->present()->method : '',
|
|
||||||
];
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment ? $payment->getCompletedAmount() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => $reportTotals,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generateProductReport($startDate, $endDate, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['client', 'invoice_number', 'invoice_date', 'quantity', 'product'];
|
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
|
||||||
$displayData = [];
|
|
||||||
$reportTotals = [];
|
|
||||||
|
|
||||||
$clients = Client::scope()
|
|
||||||
->withTrashed()
|
|
||||||
->with('contacts')
|
|
||||||
->where('is_deleted', '=', false)
|
|
||||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
|
||||||
$query->where('invoice_date', '>=', $startDate)
|
|
||||||
->where('invoice_date', '<=', $endDate)
|
|
||||||
->where('is_deleted', '=', false)
|
|
||||||
->where('is_recurring', '=', false)
|
|
||||||
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
|
||||||
->with(['invoice_items'])
|
|
||||||
->withTrashed();
|
|
||||||
}]);
|
|
||||||
|
|
||||||
foreach ($clients->get() as $client) {
|
|
||||||
foreach ($client->invoices as $invoice) {
|
|
||||||
|
|
||||||
foreach ($invoice->invoice_items as $invoiceItem) {
|
|
||||||
$displayData[] = [
|
|
||||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
|
||||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
|
||||||
$invoice->present()->invoice_date,
|
|
||||||
round($invoiceItem->qty, 2),
|
|
||||||
$invoiceItem->product_key,
|
|
||||||
];
|
|
||||||
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment ? $payment->amount : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
|
||||||
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generateClientReport($startDate, $endDate, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['client', 'amount', 'paid', 'balance'];
|
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
|
||||||
$displayData = [];
|
|
||||||
$reportTotals = [];
|
|
||||||
|
|
||||||
$clients = Client::scope()
|
|
||||||
->withArchived()
|
|
||||||
->with('contacts')
|
|
||||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
|
||||||
$query->where('invoice_date', '>=', $startDate)
|
|
||||||
->where('invoice_date', '<=', $endDate)
|
|
||||||
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
|
||||||
->where('is_recurring', '=', false)
|
|
||||||
->withArchived();
|
|
||||||
}]);
|
|
||||||
|
|
||||||
foreach ($clients->get() as $client) {
|
|
||||||
$amount = 0;
|
|
||||||
$paid = 0;
|
|
||||||
|
|
||||||
foreach ($client->invoices as $invoice) {
|
|
||||||
$amount += $invoice->amount;
|
|
||||||
$paid += $invoice->getAmountPaid();
|
|
||||||
}
|
|
||||||
|
|
||||||
$displayData[] = [
|
|
||||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
|
||||||
$account->formatMoney($amount, $client),
|
|
||||||
$account->formatMoney($paid, $client),
|
|
||||||
$account->formatMoney($amount - $paid, $client)
|
|
||||||
];
|
|
||||||
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $amount);
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $paid);
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $amount - $paid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => $reportTotals,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $startDate
|
|
||||||
* @param $endDate
|
|
||||||
* @param $isExport
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function generateExpenseReport($startDate, $endDate, $isExport)
|
|
||||||
{
|
|
||||||
$columns = ['vendor', 'client', 'date', 'expense_amount'];
|
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
|
||||||
$displayData = [];
|
|
||||||
$reportTotals = [];
|
|
||||||
|
|
||||||
$expenses = Expense::scope()
|
|
||||||
->withArchived()
|
|
||||||
->with('client.contacts', 'vendor')
|
|
||||||
->where('expense_date', '>=', $startDate)
|
|
||||||
->where('expense_date', '<=', $endDate);
|
|
||||||
|
|
||||||
|
|
||||||
foreach ($expenses->get() as $expense) {
|
|
||||||
$amount = $expense->amountWithTax();
|
|
||||||
|
|
||||||
$displayData[] = [
|
|
||||||
$expense->vendor ? ($isExport ? $expense->vendor->name : $expense->vendor->present()->link) : '',
|
|
||||||
$expense->client ? ($isExport ? $expense->client->getDisplayName() : $expense->client->present()->link) : '',
|
|
||||||
$expense->present()->expense_date,
|
|
||||||
Utils::formatMoney($amount, $expense->currency_id),
|
|
||||||
];
|
|
||||||
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $expense->expense_currency_id, 'amount', $amount);
|
|
||||||
$reportTotals = $this->addToTotals($reportTotals, $expense->invoice_currency_id, 'amount', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'columns' => $columns,
|
|
||||||
'displayData' => $displayData,
|
|
||||||
'reportTotals' => $reportTotals,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
* @param $currencyId
|
|
||||||
* @param $field
|
|
||||||
* @param $value
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
private function addToTotals($data, $currencyId, $field, $value) {
|
|
||||||
$currencyId = $currencyId ?: Auth::user()->account->getCurrencyId();
|
|
||||||
|
|
||||||
if (!isset($data[$currencyId][$field])) {
|
|
||||||
$data[$currencyId][$field] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data[$currencyId][$field] += $value;
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $reportType
|
* @param $reportType
|
||||||
* @param $data
|
* @param $data
|
||||||
@ -534,6 +129,7 @@ class ReportController extends BaseController
|
|||||||
|
|
||||||
Utils::exportData($output, $data, Utils::trans($columns));
|
Utils::exportData($output, $data, Utils::trans($columns));
|
||||||
|
|
||||||
|
/*
|
||||||
fwrite($output, trans('texts.totals'));
|
fwrite($output, trans('texts.totals'));
|
||||||
foreach ($totals as $currencyId => $fields) {
|
foreach ($totals as $currencyId => $fields) {
|
||||||
foreach ($fields as $key => $value) {
|
foreach ($fields as $key => $value) {
|
||||||
@ -550,6 +146,7 @@ class ReportController extends BaseController
|
|||||||
}
|
}
|
||||||
fwrite($output, $csv."\n");
|
fwrite($output, $csv."\n");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
fclose($output);
|
fclose($output);
|
||||||
exit;
|
exit;
|
||||||
|
@ -247,8 +247,8 @@ Route::group([
|
|||||||
Route::post('settings/email_settings', 'AccountController@saveEmailSettings');
|
Route::post('settings/email_settings', 'AccountController@saveEmailSettings');
|
||||||
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
|
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
|
||||||
Route::get('settings/data_visualizations', 'ReportController@d3');
|
Route::get('settings/data_visualizations', 'ReportController@d3');
|
||||||
Route::get('settings/reports', 'ReportController@showReports');
|
Route::get('reports', 'ReportController@showReports');
|
||||||
Route::post('settings/reports', 'ReportController@showReports');
|
Route::post('reports', 'ReportController@showReports');
|
||||||
|
|
||||||
Route::post('settings/change_plan', 'AccountController@changePlan');
|
Route::post('settings/change_plan', 'AccountController@changePlan');
|
||||||
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
|
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
|
||||||
|
@ -117,7 +117,7 @@ class Account extends Eloquent
|
|||||||
ACCOUNT_EMAIL_SETTINGS,
|
ACCOUNT_EMAIL_SETTINGS,
|
||||||
ACCOUNT_TEMPLATES_AND_REMINDERS,
|
ACCOUNT_TEMPLATES_AND_REMINDERS,
|
||||||
ACCOUNT_BANKS,
|
ACCOUNT_BANKS,
|
||||||
ACCOUNT_REPORTS,
|
//ACCOUNT_REPORTS,
|
||||||
ACCOUNT_DATA_VISUALIZATIONS,
|
ACCOUNT_DATA_VISUALIZATIONS,
|
||||||
ACCOUNT_API_TOKENS,
|
ACCOUNT_API_TOKENS,
|
||||||
ACCOUNT_USER_MANAGEMENT,
|
ACCOUNT_USER_MANAGEMENT,
|
||||||
|
@ -289,6 +289,7 @@ class EntityModel extends Eloquent
|
|||||||
'vendors' => 'building',
|
'vendors' => 'building',
|
||||||
'settings' => 'cog',
|
'settings' => 'cog',
|
||||||
'self-update' => 'download',
|
'self-update' => 'download',
|
||||||
|
'reports' => 'th-list',
|
||||||
];
|
];
|
||||||
|
|
||||||
return array_get($icons, $entityType);
|
return array_get($icons, $entityType);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php namespace App\Ninja\Presenters;
|
<?php namespace App\Ninja\Presenters;
|
||||||
|
|
||||||
use Utils;
|
use Utils;
|
||||||
|
use Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ExpensePresenter
|
* Class ExpensePresenter
|
||||||
@ -24,6 +25,11 @@ class ExpensePresenter extends EntityPresenter
|
|||||||
return Utils::fromSqlDate($this->entity->expense_date);
|
return Utils::fromSqlDate($this->entity->expense_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function month()
|
||||||
|
{
|
||||||
|
return Carbon::parse($this->entity->payment_date)->format('Y m');
|
||||||
|
}
|
||||||
|
|
||||||
public function amount()
|
public function amount()
|
||||||
{
|
{
|
||||||
return Utils::formatMoney($this->entity->amount, $this->entity->expense_currency_id);
|
return Utils::formatMoney($this->entity->amount, $this->entity->expense_currency_id);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace App\Ninja\Presenters;
|
<?php namespace App\Ninja\Presenters;
|
||||||
|
|
||||||
|
use Carbon;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Utils;
|
use Utils;
|
||||||
use DropdownButton;
|
use DropdownButton;
|
||||||
@ -44,6 +45,38 @@ class InvoicePresenter extends EntityPresenter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function age()
|
||||||
|
{
|
||||||
|
if ( ! $this->entity->due_date || $this->entity->date_date == '0000-00-00') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$date = Carbon::parse($this->entity->due_date);
|
||||||
|
|
||||||
|
if ($date->isFuture()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $date->diffInDays();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ageGroup()
|
||||||
|
{
|
||||||
|
$age = $this->age();
|
||||||
|
|
||||||
|
if ($age > 120) {
|
||||||
|
return 'age_group_120';
|
||||||
|
} elseif ($age > 90) {
|
||||||
|
return 'age_group_90';
|
||||||
|
} elseif ($age > 60) {
|
||||||
|
return 'age_group_60';
|
||||||
|
} elseif ($age > 30) {
|
||||||
|
return 'age_group_30';
|
||||||
|
} else {
|
||||||
|
return 'age_group_0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function dueDateLabel()
|
public function dueDateLabel()
|
||||||
{
|
{
|
||||||
if ($this->entity->isType(INVOICE_TYPE_STANDARD)) {
|
if ($this->entity->isType(INVOICE_TYPE_STANDARD)) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace App\Ninja\Presenters;
|
<?php namespace App\Ninja\Presenters;
|
||||||
|
|
||||||
|
use Carbon;
|
||||||
use Utils;
|
use Utils;
|
||||||
|
|
||||||
class PaymentPresenter extends EntityPresenter {
|
class PaymentPresenter extends EntityPresenter {
|
||||||
@ -19,6 +20,11 @@ class PaymentPresenter extends EntityPresenter {
|
|||||||
return Utils::fromSqlDate($this->entity->payment_date);
|
return Utils::fromSqlDate($this->entity->payment_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function month()
|
||||||
|
{
|
||||||
|
return Carbon::parse($this->entity->payment_date)->format('Y m');
|
||||||
|
}
|
||||||
|
|
||||||
public function method()
|
public function method()
|
||||||
{
|
{
|
||||||
if ($this->entity->account_gateway) {
|
if ($this->entity->account_gateway) {
|
||||||
|
84
app/Ninja/Reports/AbstractReport.php
Normal file
84
app/Ninja/Reports/AbstractReport.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
|
||||||
|
class AbstractReport
|
||||||
|
{
|
||||||
|
public $startDate;
|
||||||
|
public $endDate;
|
||||||
|
public $isExport;
|
||||||
|
public $options;
|
||||||
|
|
||||||
|
public $totals = [];
|
||||||
|
public $columns = [];
|
||||||
|
public $data = [];
|
||||||
|
|
||||||
|
public function __construct($startDate, $endDate, $isExport, $options = false)
|
||||||
|
{
|
||||||
|
$this->startDate = $startDate;
|
||||||
|
$this->endDate = $endDate;
|
||||||
|
$this->isExport = $isExport;
|
||||||
|
$this->options = $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function results()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'columns' => $this->columns,
|
||||||
|
'displayData' => $this->data,
|
||||||
|
'reportTotals' => $this->totals,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function addToTotals($currencyId, $field, $value, $dimension = false)
|
||||||
|
{
|
||||||
|
$currencyId = $currencyId ?: Auth::user()->account->getCurrencyId();
|
||||||
|
|
||||||
|
if ( ! isset($this->totals[$currencyId][$dimension])) {
|
||||||
|
$this->totals[$currencyId][$dimension] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! isset($this->totals[$currencyId][$dimension][$field])) {
|
||||||
|
$this->totals[$currencyId][$dimension][$field] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->totals[$currencyId][$dimension][$field] += $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tableHeader()
|
||||||
|
{
|
||||||
|
$str = '';
|
||||||
|
|
||||||
|
foreach ($this->columns as $key => $val) {
|
||||||
|
if (is_array($val)) {
|
||||||
|
$field = $key;
|
||||||
|
$class = $val;
|
||||||
|
} else {
|
||||||
|
$field = $val;
|
||||||
|
$class = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($field, 'date') !== false) {
|
||||||
|
//$class[] = 'group-date-monthyear';
|
||||||
|
$class[] = 'group-date-' . (isset($this->options['group_dates_by']) ? $this->options['group_dates_by'] : 'monthyear');
|
||||||
|
} elseif (in_array($field, ['client', 'method'])) {
|
||||||
|
$class[] = 'group-letter-100';
|
||||||
|
} elseif (in_array($field, ['amount', 'paid', 'balance'])) {
|
||||||
|
$class[] = 'group-number-50';
|
||||||
|
}
|
||||||
|
|
||||||
|
$class = count($class) ? implode(' ', $class) : 'group-false';
|
||||||
|
$label = trans("texts.{$field}");
|
||||||
|
$str .= "<th class=\"{$class}\">{$label}</th>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
}
|
58
app/Ninja/Reports/AgingReport.php
Normal file
58
app/Ninja/Reports/AgingReport.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Client;
|
||||||
|
|
||||||
|
class AgingReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'invoice_number',
|
||||||
|
'invoice_date',
|
||||||
|
'due_date',
|
||||||
|
'age' => ['group-number-30'],
|
||||||
|
'amount',
|
||||||
|
'balance',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$clients = Client::scope()
|
||||||
|
->withArchived()
|
||||||
|
->with('contacts')
|
||||||
|
->with(['invoices' => function($query) {
|
||||||
|
$query->invoices()
|
||||||
|
->whereIsPublic(true)
|
||||||
|
->withArchived()
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->where('invoice_date', '>=', $this->startDate)
|
||||||
|
->where('invoice_date', '<=', $this->endDate)
|
||||||
|
->with(['invoice_items']);
|
||||||
|
}]);
|
||||||
|
|
||||||
|
foreach ($clients->get() as $client) {
|
||||||
|
foreach ($client->invoices as $invoice) {
|
||||||
|
|
||||||
|
$this->data[] = [
|
||||||
|
$this->isExport ? $client->getDisplayName() : $client->present()->link,
|
||||||
|
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||||
|
$invoice->present()->invoice_date,
|
||||||
|
$invoice->present()->due_date,
|
||||||
|
$invoice->present()->age,
|
||||||
|
$account->formatMoney($invoice->amount, $client),
|
||||||
|
$account->formatMoney($invoice->balance, $client),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, $invoice->present()->ageGroup, $invoice->balance);
|
||||||
|
|
||||||
|
//$this->addToTotals($client->currency_id, 'paid', $payment ? $payment->getCompletedAmount() : 0);
|
||||||
|
//$this->addToTotals($client->currency_id, 'amount', $invoice->amount);
|
||||||
|
//$this->addToTotals($client->currency_id, 'balance', $invoice->balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
app/Ninja/Reports/ClientReport.php
Normal file
53
app/Ninja/Reports/ClientReport.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Client;
|
||||||
|
|
||||||
|
class ClientReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'amount',
|
||||||
|
'paid',
|
||||||
|
'balance',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$clients = Client::scope()
|
||||||
|
->withArchived()
|
||||||
|
->with('contacts')
|
||||||
|
->with(['invoices' => function($query) {
|
||||||
|
$query->where('invoice_date', '>=', $this->startDate)
|
||||||
|
->where('invoice_date', '<=', $this->endDate)
|
||||||
|
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
|
->where('is_recurring', '=', false)
|
||||||
|
->withArchived();
|
||||||
|
}]);
|
||||||
|
|
||||||
|
foreach ($clients->get() as $client) {
|
||||||
|
$amount = 0;
|
||||||
|
$paid = 0;
|
||||||
|
|
||||||
|
foreach ($client->invoices as $invoice) {
|
||||||
|
$amount += $invoice->amount;
|
||||||
|
$paid += $invoice->getAmountPaid();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->data[] = [
|
||||||
|
$this->isExport ? $client->getDisplayName() : $client->present()->link,
|
||||||
|
$account->formatMoney($amount, $client),
|
||||||
|
$account->formatMoney($paid, $client),
|
||||||
|
$account->formatMoney($amount - $paid, $client)
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'amount', $amount);
|
||||||
|
$this->addToTotals($client->currency_id, 'paid', $paid);
|
||||||
|
$this->addToTotals($client->currency_id, 'balance', $amount - $paid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
app/Ninja/Reports/ExpenseReport.php
Normal file
44
app/Ninja/Reports/ExpenseReport.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Utils;
|
||||||
|
use App\Models\Expense;
|
||||||
|
|
||||||
|
class ExpenseReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'vendor',
|
||||||
|
'client',
|
||||||
|
'date',
|
||||||
|
'category',
|
||||||
|
'expense_amount',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$expenses = Expense::scope()
|
||||||
|
->withArchived()
|
||||||
|
->with('client.contacts', 'vendor')
|
||||||
|
->where('expense_date', '>=', $this->startDate)
|
||||||
|
->where('expense_date', '<=', $this->endDate);
|
||||||
|
|
||||||
|
foreach ($expenses->get() as $expense) {
|
||||||
|
$amount = $expense->amountWithTax();
|
||||||
|
|
||||||
|
$this->data[] = [
|
||||||
|
$expense->vendor ? ($this->isExport ? $expense->vendor->name : $expense->vendor->present()->link) : '',
|
||||||
|
$expense->client ? ($this->isExport ? $expense->client->getDisplayName() : $expense->client->present()->link) : '',
|
||||||
|
$expense->present()->expense_date,
|
||||||
|
$expense->present()->category,
|
||||||
|
Utils::formatMoney($amount, $expense->currency_id),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($expense->expense_currency_id, 'amount', $amount);
|
||||||
|
$this->addToTotals($expense->invoice_currency_id, 'amount', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
app/Ninja/Reports/InvoiceDetailsReport.php
Normal file
61
app/Ninja/Reports/InvoiceDetailsReport.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Client;
|
||||||
|
|
||||||
|
class InvoiceDetailsReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'invoice_number',
|
||||||
|
'invoice_date',
|
||||||
|
'product',
|
||||||
|
'qty',
|
||||||
|
'cost',
|
||||||
|
//'tax_rate1',
|
||||||
|
//'tax_rate2',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
$status = $this->options['invoice_status'];
|
||||||
|
|
||||||
|
$clients = Client::scope()
|
||||||
|
->withArchived()
|
||||||
|
->with('contacts')
|
||||||
|
->with(['invoices' => function($query) use ($status) {
|
||||||
|
if ($status == 'draft') {
|
||||||
|
$query->whereIsPublic(false);
|
||||||
|
} elseif ($status == 'unpaid' || $status == 'paid') {
|
||||||
|
$query->whereIsPublic(true);
|
||||||
|
}
|
||||||
|
$query->invoices()
|
||||||
|
->withArchived()
|
||||||
|
->where('invoice_date', '>=', $this->startDate)
|
||||||
|
->where('invoice_date', '<=', $this->endDate)
|
||||||
|
->with(['invoice_items']);
|
||||||
|
}]);
|
||||||
|
|
||||||
|
foreach ($clients->get() as $client) {
|
||||||
|
foreach ($client->invoices as $invoice) {
|
||||||
|
foreach ($invoice->invoice_items as $item) {
|
||||||
|
$this->data[] = [
|
||||||
|
$this->isExport ? $client->getDisplayName() : $client->present()->link,
|
||||||
|
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||||
|
$invoice->present()->invoice_date,
|
||||||
|
$item->product_key,
|
||||||
|
$item->qty,
|
||||||
|
$account->formatMoney($item->cost, $client),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
//$this->addToTotals($client->currency_id, 'paid', $payment ? $payment->getCompletedAmount() : 0);
|
||||||
|
//$this->addToTotals($client->currency_id, 'amount', $invoice->amount);
|
||||||
|
//$this->addToTotals($client->currency_id, 'balance', $invoice->balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
app/Ninja/Reports/InvoiceReport.php
Normal file
73
app/Ninja/Reports/InvoiceReport.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Client;
|
||||||
|
|
||||||
|
class InvoiceReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'invoice_number',
|
||||||
|
'invoice_date',
|
||||||
|
'amount',
|
||||||
|
'payment_date',
|
||||||
|
'paid',
|
||||||
|
'method'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
$status = $this->options['invoice_status'];
|
||||||
|
|
||||||
|
$clients = Client::scope()
|
||||||
|
->withArchived()
|
||||||
|
->with('contacts')
|
||||||
|
->with(['invoices' => function($query) use ($status) {
|
||||||
|
if ($status == 'draft') {
|
||||||
|
$query->whereIsPublic(false);
|
||||||
|
} elseif ($status == 'unpaid' || $status == 'paid') {
|
||||||
|
$query->whereIsPublic(true);
|
||||||
|
}
|
||||||
|
$query->invoices()
|
||||||
|
->withArchived()
|
||||||
|
->where('invoice_date', '>=', $this->startDate)
|
||||||
|
->where('invoice_date', '<=', $this->endDate)
|
||||||
|
->with(['payments' => function($query) {
|
||||||
|
$query->withArchived()
|
||||||
|
->excludeFailed()
|
||||||
|
->with('payment_type', 'account_gateway.gateway');
|
||||||
|
}, 'invoice_items']);
|
||||||
|
}]);
|
||||||
|
|
||||||
|
foreach ($clients->get() as $client) {
|
||||||
|
foreach ($client->invoices as $invoice) {
|
||||||
|
|
||||||
|
$payments = count($invoice->payments) ? $invoice->payments : [false];
|
||||||
|
foreach ($payments as $payment) {
|
||||||
|
if ( ! $payment && $status == 'paid') {
|
||||||
|
continue;
|
||||||
|
} elseif ($payment && $status == 'unpaid') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->data[] = [
|
||||||
|
$this->isExport ? $client->getDisplayName() : $client->present()->link,
|
||||||
|
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||||
|
$invoice->present()->invoice_date,
|
||||||
|
$account->formatMoney($invoice->amount, $client),
|
||||||
|
$payment ? $payment->present()->payment_date : '',
|
||||||
|
$payment ? $account->formatMoney($payment->getCompletedAmount(), $client) : '',
|
||||||
|
$payment ? $payment->present()->method : '',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'paid', $payment ? $payment->getCompletedAmount() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'amount', $invoice->amount);
|
||||||
|
$this->addToTotals($client->currency_id, 'balance', $invoice->balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
app/Ninja/Reports/PaymentReport.php
Normal file
54
app/Ninja/Reports/PaymentReport.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Payment;
|
||||||
|
|
||||||
|
class PaymentReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'invoice_number',
|
||||||
|
'invoice_date',
|
||||||
|
'amount',
|
||||||
|
'payment_date',
|
||||||
|
'paid',
|
||||||
|
'method',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$payments = Payment::scope()
|
||||||
|
->withArchived()
|
||||||
|
->excludeFailed()
|
||||||
|
->whereHas('client', function($query) {
|
||||||
|
$query->where('is_deleted', '=', false);
|
||||||
|
})
|
||||||
|
->whereHas('invoice', function($query) {
|
||||||
|
$query->where('is_deleted', '=', false);
|
||||||
|
})
|
||||||
|
->with('client.contacts', 'invoice', 'payment_type', 'account_gateway.gateway')
|
||||||
|
->where('payment_date', '>=', $this->startDate)
|
||||||
|
->where('payment_date', '<=', $this->endDate);
|
||||||
|
|
||||||
|
foreach ($payments->get() as $payment) {
|
||||||
|
$invoice = $payment->invoice;
|
||||||
|
$client = $payment->client;
|
||||||
|
$this->data[] = [
|
||||||
|
$this->isExport ? $client->getDisplayName() : $client->present()->link,
|
||||||
|
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||||
|
$invoice->present()->invoice_date,
|
||||||
|
$account->formatMoney($invoice->amount, $client),
|
||||||
|
$payment->present()->payment_date,
|
||||||
|
$account->formatMoney($payment->getCompletedAmount(), $client),
|
||||||
|
$payment->present()->method,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'amount', $invoice->amount);
|
||||||
|
$this->addToTotals($client->currency_id, 'paid', $payment->getCompletedAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
app/Ninja/Reports/ProductReport.php
Normal file
55
app/Ninja/Reports/ProductReport.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Client;
|
||||||
|
|
||||||
|
class ProductReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'invoice_number',
|
||||||
|
'invoice_date',
|
||||||
|
'quantity',
|
||||||
|
'product',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$clients = Client::scope()
|
||||||
|
->withTrashed()
|
||||||
|
->with('contacts')
|
||||||
|
->where('is_deleted', '=', false)
|
||||||
|
->with(['invoices' => function($query) {
|
||||||
|
$query->where('invoice_date', '>=', $this->startDate)
|
||||||
|
->where('invoice_date', '<=', $this->endDate)
|
||||||
|
->where('is_deleted', '=', false)
|
||||||
|
->where('is_recurring', '=', false)
|
||||||
|
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
|
->with(['invoice_items'])
|
||||||
|
->withTrashed();
|
||||||
|
}]);
|
||||||
|
|
||||||
|
foreach ($clients->get() as $client) {
|
||||||
|
foreach ($client->invoices as $invoice) {
|
||||||
|
|
||||||
|
foreach ($invoice->invoice_items as $invoiceItem) {
|
||||||
|
$this->data[] = [
|
||||||
|
$this->isExport ? $client->getDisplayName() : $client->present()->link,
|
||||||
|
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||||
|
$invoice->present()->invoice_date,
|
||||||
|
round($invoiceItem->qty, 2),
|
||||||
|
$invoiceItem->product_key,
|
||||||
|
];
|
||||||
|
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment ? $payment->amount : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||||
|
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
app/Ninja/Reports/ProfitAndLossReport.php
Normal file
68
app/Ninja/Reports/ProfitAndLossReport.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\Expense;
|
||||||
|
|
||||||
|
class ProfitAndLossReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'type',
|
||||||
|
'client',
|
||||||
|
'amount',
|
||||||
|
'date',
|
||||||
|
'notes',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$payments = Payment::scope()
|
||||||
|
->with('client.contacts')
|
||||||
|
->withArchived()
|
||||||
|
->excludeFailed();
|
||||||
|
|
||||||
|
foreach ($payments->get() as $payment) {
|
||||||
|
$client = $payment->client;
|
||||||
|
$this->data[] = [
|
||||||
|
trans('texts.payment'),
|
||||||
|
$client ? ($this->isExport ? $client->getDisplayName() : $client->present()->link) : '',
|
||||||
|
$account->formatMoney($payment->getCompletedAmount(), $client),
|
||||||
|
$payment->present()->payment_date,
|
||||||
|
$payment->present()->method,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'revenue', $payment->getCompletedAmount(), $payment->present()->month);
|
||||||
|
$this->addToTotals($client->currency_id, 'expenses', 0, $payment->present()->month);
|
||||||
|
$this->addToTotals($client->currency_id, 'profit', $payment->getCompletedAmount(), $payment->present()->month);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$expenses = Expense::scope()
|
||||||
|
->with('client.contacts')
|
||||||
|
->withArchived();
|
||||||
|
|
||||||
|
foreach ($expenses->get() as $expense) {
|
||||||
|
$client = $expense->client;
|
||||||
|
$this->data[] = [
|
||||||
|
trans('texts.expense'),
|
||||||
|
$client ? ($this->isExport ? $client->getDisplayName() : $client->present()->link) : '',
|
||||||
|
$expense->present()->amount,
|
||||||
|
$expense->present()->expense_date,
|
||||||
|
$expense->present()->category,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'revenue', 0, $expense->present()->month);
|
||||||
|
$this->addToTotals($client->currency_id, 'expenses', $expense->amount, $expense->present()->month);
|
||||||
|
$this->addToTotals($client->currency_id, 'profit', $expense->amount * -1, $expense->present()->month);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//$this->addToTotals($client->currency_id, 'paid', $payment ? $payment->getCompletedAmount() : 0);
|
||||||
|
//$this->addToTotals($client->currency_id, 'amount', $invoice->amount);
|
||||||
|
//$this->addToTotals($client->currency_id, 'balance', $invoice->balance);
|
||||||
|
}
|
||||||
|
}
|
36
app/Ninja/Reports/TaskReport.php
Normal file
36
app/Ninja/Reports/TaskReport.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Utils;
|
||||||
|
use App\Models\Task;
|
||||||
|
|
||||||
|
class TaskReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'client',
|
||||||
|
'date',
|
||||||
|
'project',
|
||||||
|
'description',
|
||||||
|
'duration',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$tasks = Task::scope()
|
||||||
|
->with('client.contacts')
|
||||||
|
->withArchived()
|
||||||
|
->dateRange($this->startDate, $this->endDate);
|
||||||
|
|
||||||
|
foreach ($tasks->get() as $task) {
|
||||||
|
$this->data[] = [
|
||||||
|
$task->client ? ($this->isExport ? $task->client->getDisplayName() : $task->client->present()->link) : trans('texts.unassigned'),
|
||||||
|
link_to($task->present()->url, $task->getStartTime()),
|
||||||
|
$task->present()->project,
|
||||||
|
$task->present()->description,
|
||||||
|
Utils::formatTime($task->getDuration()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
app/Ninja/Reports/TaxRateReport.php
Normal file
82
app/Ninja/Reports/TaxRateReport.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Reports;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use App\Models\Client;
|
||||||
|
|
||||||
|
class TaxRateReport extends AbstractReport
|
||||||
|
{
|
||||||
|
public $columns = [
|
||||||
|
'tax_name',
|
||||||
|
'tax_rate',
|
||||||
|
'amount',
|
||||||
|
'paid',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$clients = Client::scope()
|
||||||
|
->withArchived()
|
||||||
|
->with('contacts')
|
||||||
|
->with(['invoices' => function($query) {
|
||||||
|
$query->with('invoice_items')->withArchived();
|
||||||
|
if ($this->options['date_field'] == FILTER_INVOICE_DATE) {
|
||||||
|
$query->where('invoice_date', '>=', $this->startDate)
|
||||||
|
->where('invoice_date', '<=', $this->endDate)
|
||||||
|
->with('payments');
|
||||||
|
} else {
|
||||||
|
$query->whereHas('payments', function($query) {
|
||||||
|
$query->where('payment_date', '>=', $this->startDate)
|
||||||
|
->where('payment_date', '<=', $this->endDate)
|
||||||
|
->withArchived();
|
||||||
|
})
|
||||||
|
->with(['payments' => function($query) {
|
||||||
|
$query->where('payment_date', '>=', $this->startDate)
|
||||||
|
->where('payment_date', '<=', $this->endDate)
|
||||||
|
->withArchived();
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
foreach ($clients->get() as $client) {
|
||||||
|
$currencyId = $client->currency_id ?: Auth::user()->account->getCurrencyId();
|
||||||
|
$amount = 0;
|
||||||
|
$paid = 0;
|
||||||
|
$taxTotals = [];
|
||||||
|
|
||||||
|
foreach ($client->invoices as $invoice) {
|
||||||
|
foreach ($invoice->getTaxes(true) as $key => $tax) {
|
||||||
|
if ( ! isset($taxTotals[$currencyId])) {
|
||||||
|
$taxTotals[$currencyId] = [];
|
||||||
|
}
|
||||||
|
if (isset($taxTotals[$currencyId][$key])) {
|
||||||
|
$taxTotals[$currencyId][$key]['amount'] += $tax['amount'];
|
||||||
|
$taxTotals[$currencyId][$key]['paid'] += $tax['paid'];
|
||||||
|
} else {
|
||||||
|
$taxTotals[$currencyId][$key] = $tax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$amount += $invoice->amount;
|
||||||
|
$paid += $invoice->getAmountPaid();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($taxTotals as $currencyId => $taxes) {
|
||||||
|
foreach ($taxes as $tax) {
|
||||||
|
$this->data[] = [
|
||||||
|
$tax['name'],
|
||||||
|
$tax['rate'] . '%',
|
||||||
|
$account->formatMoney($tax['amount'], $client),
|
||||||
|
$account->formatMoney($tax['paid'], $client)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addToTotals($client->currency_id, 'amount', $tax['amount']);
|
||||||
|
$this->addToTotals($client->currency_id, 'paid', $tax['paid']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -195,6 +195,7 @@ class AccountRepository
|
|||||||
|
|
||||||
$features = array_merge($features, [
|
$features = array_merge($features, [
|
||||||
['dashboard', '/dashboard'],
|
['dashboard', '/dashboard'],
|
||||||
|
['reports', '/reports'],
|
||||||
['customize_design', '/settings/customize_design'],
|
['customize_design', '/settings/customize_design'],
|
||||||
['new_tax_rate', '/tax_rates/create'],
|
['new_tax_rate', '/tax_rates/create'],
|
||||||
['new_product', '/products/create'],
|
['new_product', '/products/create'],
|
||||||
|
@ -772,7 +772,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
*/
|
*/
|
||||||
public function markPaid(Invoice $invoice)
|
public function markPaid(Invoice $invoice)
|
||||||
{
|
{
|
||||||
if (floatval($invoice->balance) <= 0) {
|
if ( ! $invoice->canBePaid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,12 +825,13 @@ class InvoiceRepository extends BaseRepository
|
|||||||
->invoiceType(INVOICE_TYPE_STANDARD)
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->whereClientId($clientId)
|
->whereClientId($clientId)
|
||||||
->whereIsRecurring(false)
|
->whereIsRecurring(false)
|
||||||
->whereDeletedAt(null);
|
->whereDeletedAt(null)
|
||||||
|
->where('balance', '>', 0);
|
||||||
|
|
||||||
if ($entityType == ENTITY_TASK) {
|
if ($entityType == ENTITY_TASK) {
|
||||||
$query->whereHasTasks(true);
|
$query->whereHasTasks(true);
|
||||||
} elseif ($entityType == ENTITY_EXPENSE) {
|
} elseif ($entityType == ENTITY_EXPENSE) {
|
||||||
$query->whereHasExpenses(true);
|
$query->whereHasTasks(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->where('invoice_status_id', '<', 5)
|
return $query->where('invoice_status_id', '<', 5)
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"sweetalert2": "^5.3.8",
|
"sweetalert2": "^5.3.8",
|
||||||
"jSignature": "brinley/jSignature#^2.1.0",
|
"jSignature": "brinley/jSignature#^2.1.0",
|
||||||
"select2": "select2-dist#^4.0.3",
|
"select2": "select2-dist#^4.0.3",
|
||||||
"mousetrap": "^1.6.0"
|
"mousetrap": "^1.6.0",
|
||||||
|
"tablesorter": "jquery.tablesorter#^2.28.4"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"jquery": "~1.11"
|
"jquery": "~1.11"
|
||||||
|
13
gulpfile.js
13
gulpfile.js
@ -66,6 +66,12 @@ elixir(function(mix) {
|
|||||||
bowerDir + '/select2/dist/css/select2.css'
|
bowerDir + '/select2/dist/css/select2.css'
|
||||||
], 'public/css/select2.css');
|
], 'public/css/select2.css');
|
||||||
|
|
||||||
|
mix.styles([
|
||||||
|
bowerDir + '/tablesorter/dist/css/theme.bootstrap_3.min.css',
|
||||||
|
bowerDir + '/tablesorter/dist/css/theme.bootstrap.min.css',
|
||||||
|
bowerDir + '/tablesorter/dist/css/widget.grouping.min.css'
|
||||||
|
], 'public/css/tablesorter.css');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JS configuration
|
* JS configuration
|
||||||
@ -84,6 +90,13 @@ elixir(function(mix) {
|
|||||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
||||||
], 'public/js/daterangepicker.min.js');
|
], 'public/js/daterangepicker.min.js');
|
||||||
|
|
||||||
|
mix.scripts([
|
||||||
|
bowerDir + '/tablesorter/dist/js/jquery.tablesorter.combined.js',
|
||||||
|
bowerDir + '/tablesorter/dist/js/widgets/widget-grouping.min.js',
|
||||||
|
bowerDir + '/tablesorter/dist/js/widgets/widget-uitheme.min.js',
|
||||||
|
bowerDir + '/tablesorter/dist/js/widgets/widget-filter.min.js',
|
||||||
|
], 'public/js/tablesorter.min.js');
|
||||||
|
|
||||||
mix.scripts([
|
mix.scripts([
|
||||||
bowerDir + '/select2/dist/js/select2.js',
|
bowerDir + '/select2/dist/js/select2.js',
|
||||||
'resources/assets/js/maximize-select2-height.js',
|
'resources/assets/js/maximize-select2-height.js',
|
||||||
|
2
public/css/tablesorter.css
vendored
Normal file
2
public/css/tablesorter.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/css/tablesorter.css.map
Normal file
1
public/css/tablesorter.css.map
Normal file
File diff suppressed because one or more lines are too long
6
public/js/tablesorter.min.js
vendored
Normal file
6
public/js/tablesorter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/tablesorter.min.js.map
Normal file
1
public/js/tablesorter.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -2318,6 +2318,29 @@ $LANG = array(
|
|||||||
'domain_help_website' => 'Used when sending emails.',
|
'domain_help_website' => 'Used when sending emails.',
|
||||||
'preview' => 'Preview',
|
'preview' => 'Preview',
|
||||||
'import_invoices' => 'Import Invoices',
|
'import_invoices' => 'Import Invoices',
|
||||||
|
'new_report' => 'New Report',
|
||||||
|
'edit_report' => 'Edit Report',
|
||||||
|
'columns' => 'Columns',
|
||||||
|
'filters' => 'Filters',
|
||||||
|
'sort_by' => 'Sort By',
|
||||||
|
'draft' => 'Draft',
|
||||||
|
'unpaid' => 'Unpaid',
|
||||||
|
'aging' => 'Aging',
|
||||||
|
'age' => 'Age',
|
||||||
|
'days' => 'Days',
|
||||||
|
'age_group_0' => '0 - 30 Days',
|
||||||
|
'age_group_30' => '30 - 60 Days',
|
||||||
|
'age_group_60' => '60 - 90 Days',
|
||||||
|
'age_group_90' => '90 - 120 Days',
|
||||||
|
'age_group_120' => '120+ Days',
|
||||||
|
'invoice_details' => 'Invoice Details',
|
||||||
|
'qty' => 'Quantity',
|
||||||
|
'profit_and_loss' => 'Profit and Loss',
|
||||||
|
'revenue' => 'Revenue',
|
||||||
|
'profit' => 'Profit',
|
||||||
|
'group_when_sorted' => 'Group When Sorted',
|
||||||
|
'group_dates_by' => 'Group Dates By',
|
||||||
|
'year' => 'Year',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -473,6 +473,7 @@
|
|||||||
'tasks' => false,
|
'tasks' => false,
|
||||||
'expenses' => false,
|
'expenses' => false,
|
||||||
'vendors' => false,
|
'vendors' => false,
|
||||||
|
'reports' => false,
|
||||||
'settings' => false,
|
'settings' => false,
|
||||||
] as $key => $value)
|
] as $key => $value)
|
||||||
{!! Form::nav_link($key, $value ?: $key) !!}
|
{!! Form::nav_link($key, $value ?: $key) !!}
|
||||||
@ -514,6 +515,7 @@
|
|||||||
])
|
])
|
||||||
@endforeach
|
@endforeach
|
||||||
@endif
|
@endif
|
||||||
|
@include('partials.navigation_option', ['option' => 'reports'])
|
||||||
@include('partials.navigation_option', ['option' => 'settings'])
|
@include('partials.navigation_option', ['option' => 'settings'])
|
||||||
<li style="width:100%;">
|
<li style="width:100%;">
|
||||||
<div class="nav-footer">
|
<div class="nav-footer">
|
||||||
|
@ -6,11 +6,20 @@
|
|||||||
<script src="{{ asset('js/daterangepicker.min.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('js/daterangepicker.min.js') }}" type="text/javascript"></script>
|
||||||
<link href="{{ asset('css/daterangepicker.css') }}" rel="stylesheet" type="text/css"/>
|
<link href="{{ asset('css/daterangepicker.css') }}" rel="stylesheet" type="text/css"/>
|
||||||
|
|
||||||
|
<link href="{{ asset('css/tablesorter.css') }}" rel="stylesheet" type="text/css"/>
|
||||||
|
<script src="{{ asset('js/tablesorter.min.js') }}" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
table.tablesorter th {
|
||||||
|
color: white;
|
||||||
|
background-color: #777 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@parent
|
|
||||||
@include('accounts.nav', ['selected' => ACCOUNT_REPORTS, 'advanced' => true])
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
@ -58,6 +67,11 @@
|
|||||||
{!! Former::populateField('start_date', $startDate) !!}
|
{!! Former::populateField('start_date', $startDate) !!}
|
||||||
{!! Former::populateField('end_date', $endDate) !!}
|
{!! Former::populateField('end_date', $endDate) !!}
|
||||||
|
|
||||||
|
@if ( ! request()->report_type)
|
||||||
|
{!! Former::populateField('group_when_sorted', 1) !!}
|
||||||
|
{!! Former::populateField('group_dates_by', 'monthyear') !!}
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
@ -68,12 +82,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
{!! Former::select('report_type')->options($reportTypes, $reportType)->label(trans('texts.type')) !!}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="reportrange" class="control-label col-lg-4 col-sm-4">
|
<label for="reportrange" class="control-label col-lg-4 col-sm-4">
|
||||||
{{ trans('texts.date_range') }}
|
{{ trans('texts.date_range') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-lg-8 col-sm-8">
|
<div class="col-lg-8 col-sm-8">
|
||||||
<div id="reportrange" style="background: #f9f9f9; cursor: pointer; padding: 9px 14px; border: 1px solid #dfe0e1; margin-top: 0px; margin-left:18px">
|
<div id="reportrange" style="background: #f9f9f9; cursor: pointer; padding: 9px 14px; border: 1px solid #dfe0e1; margin-top: 0px;">
|
||||||
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>
|
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>
|
||||||
<span></span> <b class="caret"></b>
|
<span></span> <b class="caret"></b>
|
||||||
</div>
|
</div>
|
||||||
@ -85,13 +101,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="statusField" style="display:{{ in_array($reportType, ['invoice', 'invoice_details']) ? 'block' : 'none' }}">
|
||||||
|
{!! Former::select('invoice_status')->label('status')
|
||||||
|
->addOption(trans('texts.all'), 'all')
|
||||||
|
->addOption(trans('texts.draft'), 'draft')
|
||||||
|
->addOption(trans('texts.unpaid'), 'unpaid')
|
||||||
|
->addOption(trans('texts.paid'), 'paid') !!}
|
||||||
|
</div>
|
||||||
|
|
||||||
<p> </p>
|
<div id="dateField" style="display:{{ $reportType == ENTITY_TAX_RATE ? 'block' : 'none' }}">
|
||||||
<p> </p>
|
{!! Former::select('date_field')->label(trans('texts.filter'))
|
||||||
{!! Former::actions(
|
->addOption(trans('texts.invoice_date'), FILTER_INVOICE_DATE)
|
||||||
Button::primary(trans('texts.export'))->withAttributes(array('onclick' => 'onExportClick()'))->appendIcon(Icon::create('export')),
|
->addOption(trans('texts.payment_date'), FILTER_PAYMENT_DATE) !!}
|
||||||
Button::success(trans('texts.run'))->withAttributes(array('id' => 'submitButton'))->submit()->appendIcon(Icon::create('play'))
|
</div>
|
||||||
) !!}
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
{!! Former::checkbox('group_when_sorted')->text('enable') !!}
|
||||||
|
{!! Former::select('group_dates_by')
|
||||||
|
->addOption(trans('texts.day'), 'day')
|
||||||
|
->addOption(trans('texts.month'), 'monthyear')
|
||||||
|
->addOption(trans('texts.year'), 'year') !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (!Auth::user()->hasFeature(FEATURE_REPORTS))
|
@if (!Auth::user()->hasFeature(FEATURE_REPORTS))
|
||||||
<script>
|
<script>
|
||||||
@ -102,31 +138,72 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
</div>
|
<center>
|
||||||
<div class="col-md-6">
|
{!! Button::primary(trans('texts.export'))
|
||||||
{!! Former::select('report_type')->options($reportTypes, $reportType)->label(trans('texts.type')) !!}
|
->withAttributes(array('onclick' => 'onExportClick()'))
|
||||||
<div id="dateField" style="display:{{ $reportType == ENTITY_TAX_RATE ? 'block' : 'none' }}">
|
->appendIcon(Icon::create('export'))
|
||||||
{!! Former::select('date_field')->label(trans('texts.filter'))
|
->large() !!}
|
||||||
->addOption(trans('texts.invoice_date'), FILTER_INVOICE_DATE)
|
{!! Button::success(trans('texts.run'))
|
||||||
->addOption(trans('texts.payment_date'), FILTER_PAYMENT_DATE) !!}
|
->withAttributes(array('id' => 'submitButton'))
|
||||||
</div>
|
->submit()
|
||||||
|
->appendIcon(Icon::create('play'))
|
||||||
|
->large() !!}
|
||||||
|
</center><br/>
|
||||||
|
|
||||||
|
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
@if (request()->report_type)
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<table class="table table-striped invoice-table">
|
|
||||||
|
@if (count(array_values($reportTotals)))
|
||||||
|
<table class="tablesorter tablesorter-totals" style="display:none">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@foreach ($columns as $column)
|
<th>{{ trans("texts.totals") }}</th>
|
||||||
<th>{{ trans("texts.{$column}") }}</th>
|
@foreach (array_values(array_values($reportTotals)[0])[0] as $key => $val)
|
||||||
|
<th>{{ trans("texts.{$key}") }}</th>
|
||||||
@endforeach
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($reportTotals as $currencyId => $each)
|
||||||
|
@foreach ($each as $dimension => $val)
|
||||||
|
<tr>
|
||||||
|
<td>{!! Utils::getFromCache($currencyId, 'currencies')->name !!}
|
||||||
|
@if ($dimension)
|
||||||
|
- {{ $dimension }}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
@foreach ($val as $id => $field)
|
||||||
|
<td>{!! Utils::formatMoney($field, $currencyId) !!}</td>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p> </p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div class="columnSelectorWrapper">
|
||||||
|
<input id="colSelect1" type="checkbox" class="hidden">
|
||||||
|
<label class="columnSelectorButton" for="colSelect1">Column</label>
|
||||||
|
|
||||||
|
<div id="columnSelector" class="columnSelector">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<table class="tablesorter tablesorter-data" style="display:none">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{!! $report->tableHeader() !!}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@if (count($displayData))
|
@if (count($displayData))
|
||||||
@foreach ($displayData as $record)
|
@foreach ($displayData as $record)
|
||||||
@ -144,36 +221,13 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p> </p>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (count(array_values($reportTotals)))
|
|
||||||
<table class="table table-striped invoice-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{ trans("texts.totals") }}</th>
|
|
||||||
@foreach (array_values($reportTotals)[0] as $key => $val)
|
|
||||||
<th>{{ trans("texts.{$key}") }}</th>
|
|
||||||
@endforeach
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach ($reportTotals as $currencyId => $val)
|
|
||||||
<tr>
|
|
||||||
<td>{!! Utils::getFromCache($currencyId, 'currencies')->name !!}</td>
|
|
||||||
@foreach ($val as $id => $field)
|
|
||||||
<td>{!! Utils::formatMoney($field, $currencyId) !!}</td>
|
|
||||||
@endforeach
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function onExportClick() {
|
function onExportClick() {
|
||||||
@ -182,6 +236,11 @@
|
|||||||
$('#action').val('');
|
$('#action').val('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sumColumns = [];
|
||||||
|
@foreach ($columns as $column)
|
||||||
|
sumColumns.push("{{ in_array($column, ['amount', 'paid', 'balance']) ? trans("texts.{$column}") : false }}");
|
||||||
|
@endforeach
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
$('.start_date .input-group-addon').click(function() {
|
$('.start_date .input-group-addon').click(function() {
|
||||||
toggleDatePicker('start_date');
|
toggleDatePicker('start_date');
|
||||||
@ -197,6 +256,45 @@
|
|||||||
} else {
|
} else {
|
||||||
$('#dateField').fadeOut();
|
$('#dateField').fadeOut();
|
||||||
}
|
}
|
||||||
|
if (val == '{{ ENTITY_INVOICE }}' || val == 'invoice_details') {
|
||||||
|
$('#statusField').fadeIn();
|
||||||
|
} else {
|
||||||
|
$('#statusField').fadeOut();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function(){
|
||||||
|
$(".tablesorter-data").tablesorter({
|
||||||
|
theme: 'bootstrap',
|
||||||
|
widgets: ['zebra', 'uitheme', 'filter'{!! request()->group_when_sorted ? ", 'group'" : "" !!}, 'columnSelector'],
|
||||||
|
headerTemplate : '{content} {icon}',
|
||||||
|
widgetOptions : {
|
||||||
|
columnSelector_container : $('#columnSelector'),
|
||||||
|
filter_cssFilter: 'form-control',
|
||||||
|
group_collapsed: true,
|
||||||
|
group_saveGroups: false,
|
||||||
|
//group_formatter : function(txt, col, table, c, wo, data) {},
|
||||||
|
group_callback: function ($cell, $rows, column, table) {
|
||||||
|
for (var i=0; i<sumColumns.length; i++) {
|
||||||
|
var label = sumColumns[i];
|
||||||
|
if (!label) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var subtotal = 0;
|
||||||
|
$rows.each(function() {
|
||||||
|
var txt = $(this).find("td").eq(i).text().replace(/[$,]/g, '');
|
||||||
|
subtotal += parseFloat(txt || 0);
|
||||||
|
});
|
||||||
|
$cell.find(".group-count").append(' - ' + label + ': ' + roundToTwo(subtotal));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
|
||||||
|
$(".tablesorter-totals").tablesorter({
|
||||||
|
theme: 'bootstrap',
|
||||||
|
widgets: ['zebra', 'uitheme'],
|
||||||
|
}).show();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user