mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 20:22:42 +01:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
ed6c35cc8c
@ -10,6 +10,9 @@ use DatePeriod;
|
||||
use Session;
|
||||
use View;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Expense;
|
||||
|
||||
class ReportController extends BaseController
|
||||
{
|
||||
@ -47,6 +50,7 @@ class ReportController extends BaseController
|
||||
$groupBy = Input::get('group_by');
|
||||
$chartType = Input::get('chart_type');
|
||||
$reportType = Input::get('report_type');
|
||||
$dateField = Input::get('date_field');
|
||||
$startDate = Utils::toSqlDate(Input::get('start_date'), false);
|
||||
$endDate = Utils::toSqlDate(Input::get('end_date'), false);
|
||||
$enableReport = Input::get('enable_report') ? true : false;
|
||||
@ -55,6 +59,7 @@ class ReportController extends BaseController
|
||||
$groupBy = 'MONTH';
|
||||
$chartType = 'Bar';
|
||||
$reportType = ENTITY_INVOICE;
|
||||
$dateField = FILTER_INVOICE_DATE;
|
||||
$startDate = Utils::today(false)->modify('-3 month');
|
||||
$endDate = Utils::today(false);
|
||||
$enableReport = true;
|
||||
@ -76,6 +81,8 @@ class ReportController extends BaseController
|
||||
ENTITY_CLIENT => trans('texts.client'),
|
||||
ENTITY_INVOICE => trans('texts.invoice'),
|
||||
ENTITY_PAYMENT => trans('texts.payment'),
|
||||
ENTITY_EXPENSE => trans('texts.expenses'),
|
||||
ENTITY_TAX_RATE => trans('texts.taxes'),
|
||||
];
|
||||
|
||||
$params = [
|
||||
@ -94,10 +101,11 @@ class ReportController extends BaseController
|
||||
|
||||
if (Auth::user()->account->isPro()) {
|
||||
if ($enableReport) {
|
||||
$params = array_merge($params, self::generateReport($reportType, $groupBy, $startDate, $endDate));
|
||||
$isExport = $action == 'export';
|
||||
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));
|
||||
|
||||
if ($action == 'export') {
|
||||
self::export($params['exportData'], $params['reportTotals']);
|
||||
if ($isExport) {
|
||||
self::export($params['displayData'], $params['columns'], $params['reportTotals']);
|
||||
}
|
||||
}
|
||||
if ($enableChart) {
|
||||
@ -212,165 +220,310 @@ class ReportController extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
private function generateReport($reportType, $groupBy, $startDate, $endDate)
|
||||
private function generateReport($reportType, $startDate, $endDate, $dateField, $isExport)
|
||||
{
|
||||
if ($reportType == ENTITY_CLIENT) {
|
||||
$columns = ['client', 'amount', 'paid', 'balance'];
|
||||
return $this->generateClientReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_INVOICE) {
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'paid', 'balance'];
|
||||
} else {
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
||||
return $this->generateInvoiceReport($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);
|
||||
}
|
||||
}
|
||||
|
||||
$query = DB::table('invoices')
|
||||
->join('accounts', 'accounts.id', '=', 'invoices.account_id')
|
||||
->join('clients', 'clients.id', '=', 'invoices.client_id')
|
||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('invoices.account_id', '=', Auth::user()->account_id)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->where('invoices.invoice_date', '>=', $startDate->format('Y-m-d'))
|
||||
->where('invoices.invoice_date', '<=', $endDate->format('Y-m-d'))
|
||||
->where('invoices.is_quote', '=', false)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
->where('contacts.is_primary', '=', true);
|
||||
private function generateTaxRateReport($startDate, $endDate, $dateField, $isExport)
|
||||
{
|
||||
$columns = ['tax_name', 'tax_rate', 'amount', 'paid'];
|
||||
|
||||
$select = [
|
||||
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
|
||||
'accounts.country_id',
|
||||
'contacts.first_name',
|
||||
'contacts.last_name',
|
||||
'contacts.email',
|
||||
'clients.name as client_name',
|
||||
'clients.public_id as client_public_id',
|
||||
'invoices.public_id as invoice_public_id'
|
||||
];
|
||||
|
||||
if ($reportType == ENTITY_CLIENT) {
|
||||
$query->groupBy('clients.id');
|
||||
array_push($select, DB::raw('sum(invoices.amount) amount'), DB::raw('sum(invoices.balance) balance'), DB::raw('sum(invoices.amount - invoices.balance) paid'));
|
||||
} else {
|
||||
$query->orderBy('invoices.id');
|
||||
array_push($select, 'invoices.invoice_number', 'invoices.amount', 'invoices.balance', 'invoices.invoice_date');
|
||||
if ($reportType == ENTITY_INVOICE) {
|
||||
array_push($select, DB::raw('(invoices.amount - invoices.balance) paid'));
|
||||
} else {
|
||||
$query->join('payments', 'payments.invoice_id', '=', 'invoices.id')
|
||||
->leftJoin('payment_types', 'payment_types.id', '=', 'payments.payment_type_id')
|
||||
->leftJoin('account_gateways', 'account_gateways.id', '=', 'payments.account_gateway_id')
|
||||
->leftJoin('gateways', 'gateways.id', '=', 'account_gateways.gateway_id');
|
||||
array_push($select, 'payments.payment_date', 'payments.amount as paid', 'payment_types.name as payment_type', 'gateways.name as gateway');
|
||||
}
|
||||
}
|
||||
|
||||
$query->select($select);
|
||||
$data = $query->get();
|
||||
|
||||
$lastInvoiceId = null;
|
||||
$sameAsLast = false;
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$exportData = [];
|
||||
$reportTotals = [
|
||||
'amount' => [],
|
||||
'balance' => [],
|
||||
'paid' => [],
|
||||
];
|
||||
$clients = Client::scope()
|
||||
->withArchived()
|
||||
->with('contacts')
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate, $dateField) {
|
||||
$query->withArchived();
|
||||
if ($dateField == FILTER_PAYMENT_DATE) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->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()
|
||||
->with('payment_type', 'account_gateway.gateway');
|
||||
}, 'invoice_items']);
|
||||
}
|
||||
}]);
|
||||
|
||||
foreach ($data as $record) {
|
||||
$sameAsLast = ($lastInvoiceId == $record->invoice_public_id);
|
||||
$lastInvoiceId = $record->invoice_public_id;
|
||||
foreach ($clients->get() as $client) {
|
||||
$currencyId = $client->currency_id ?: Auth::user()->account->getCurrencyId();
|
||||
$amount = 0;
|
||||
$paid = 0;
|
||||
$taxTotals = [];
|
||||
|
||||
$displayRow = [];
|
||||
if ($sameAsLast) {
|
||||
array_push($displayRow, '', '', '', '');
|
||||
} else {
|
||||
array_push($displayRow, link_to('/clients/'.$record->client_public_id, Utils::getClientDisplayName($record)));
|
||||
if ($reportType != ENTITY_CLIENT) {
|
||||
array_push($displayRow,
|
||||
link_to('/invoices/'.$record->invoice_public_id, $record->invoice_number),
|
||||
Utils::fromSqlDate($record->invoice_date, true)
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
array_push($displayRow, Utils::formatMoney($record->amount, $record->currency_id, $record->country_id));
|
||||
}
|
||||
if ($reportType != ENTITY_PAYMENT) {
|
||||
array_push($displayRow, Utils::formatMoney($record->paid, $record->currency_id, $record->country_id));
|
||||
}
|
||||
if ($reportType == ENTITY_PAYMENT) {
|
||||
array_push($displayRow,
|
||||
Utils::fromSqlDate($record->payment_date, true),
|
||||
Utils::formatMoney($record->paid, $record->currency_id, $record->country_id),
|
||||
$record->gateway ?: $record->payment_type
|
||||
);
|
||||
} else {
|
||||
array_push($displayRow, Utils::formatMoney($record->balance, $record->currency_id, $record->country_id));
|
||||
|
||||
$amount += $invoice->amount;
|
||||
$paid += $invoice->getAmountPaid();
|
||||
}
|
||||
|
||||
// export data
|
||||
$exportRow = [];
|
||||
if ($sameAsLast) {
|
||||
$exportRow[trans('texts.client')] = ' ';
|
||||
$exportRow[trans('texts.invoice_number')] = ' ';
|
||||
$exportRow[trans('texts.invoice_date')] = ' ';
|
||||
$exportRow[trans('texts.amount')] = ' ';
|
||||
} else {
|
||||
$exportRow[trans('texts.client')] = Utils::getClientDisplayName($record);
|
||||
if ($reportType != ENTITY_CLIENT) {
|
||||
$exportRow[trans('texts.invoice_number')] = $record->invoice_number;
|
||||
$exportRow[trans('texts.invoice_date')] = Utils::fromSqlDate($record->invoice_date, true);
|
||||
foreach ($taxTotals as $currencyId => $taxes) {
|
||||
foreach ($taxes as $tax) {
|
||||
$displayData[] = [
|
||||
$tax['name'],
|
||||
$tax['rate'],
|
||||
$account->formatMoney($tax['amount'], $client),
|
||||
$account->formatMoney($tax['paid'], $client)
|
||||
];
|
||||
}
|
||||
$exportRow[trans('texts.amount')] = Utils::formatMoney($record->amount, $record->currency_id, $record->country_id);
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $tax['amount']);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $tax['paid']);
|
||||
}
|
||||
if ($reportType != ENTITY_PAYMENT) {
|
||||
$exportRow[trans('texts.paid')] = Utils::formatMoney($record->paid, $record->currency_id, $record->country_id);
|
||||
}
|
||||
if ($reportType == ENTITY_PAYMENT) {
|
||||
$exportRow[trans('texts.payment_date')] = Utils::fromSqlDate($record->payment_date, true);
|
||||
$exportRow[trans('texts.payment_amount')] = Utils::formatMoney($record->paid, $record->currency_id, $record->country_id);
|
||||
$exportRow[trans('texts.method')] = $record->gateway ?: $record->payment_type;
|
||||
} else {
|
||||
$exportRow[trans('texts.balance')] = Utils::formatMoney($record->balance, $record->currency_id, $record->country_id);
|
||||
}
|
||||
|
||||
$displayData[] = $displayRow;
|
||||
$exportData[] = $exportRow;
|
||||
|
||||
$accountCurrencyId = Auth::user()->account->currency_id;
|
||||
$currencyId = $record->currency_id ? $record->currency_id : ($accountCurrencyId ? $accountCurrencyId : DEFAULT_CURRENCY);
|
||||
if (!isset($reportTotals['amount'][$currencyId])) {
|
||||
$reportTotals['amount'][$currencyId] = 0;
|
||||
$reportTotals['balance'][$currencyId] = 0;
|
||||
$reportTotals['paid'][$currencyId] = 0;
|
||||
}
|
||||
if (!$sameAsLast) {
|
||||
$reportTotals['amount'][$currencyId] += $record->amount;
|
||||
$reportTotals['balance'][$currencyId] += $record->balance;
|
||||
}
|
||||
$reportTotals['paid'][$currencyId] += $record->paid;
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
'exportData' => $exportData
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->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->amount, $client),
|
||||
$payment->present()->method,
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment->amount);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
private function export($data, $totals)
|
||||
private function generateInvoiceReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'paid', 'balance'];
|
||||
|
||||
$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)
|
||||
->with(['payments' => function($query) {
|
||||
$query->withTrashed()
|
||||
->with('payment_type', 'account_gateway.gateway')
|
||||
->where('is_deleted', '=', false);
|
||||
}, 'invoice_items'])
|
||||
->withTrashed();
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
$currencyId = $client->currency_id ?: Auth::user()->account->getCurrencyId();
|
||||
|
||||
foreach ($client->invoices as $invoice) {
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
$account->formatMoney($invoice->amount, $client),
|
||||
$account->formatMoney($invoice->getAmountPaid(), $client),
|
||||
$account->formatMoney($invoice->balance, $client),
|
||||
];
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $invoice->getAmountPaid());
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
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)
|
||||
->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,
|
||||
];
|
||||
}
|
||||
|
||||
private function generateExpenseReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['vendor', 'client', 'date', 'expense_amount', 'invoiced_amount'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$expenses = Expense::scope()
|
||||
->withTrashed()
|
||||
->with('client.contacts', 'vendor')
|
||||
->where('expense_date', '>=', $startDate)
|
||||
->where('expense_date', '<=', $endDate);
|
||||
|
||||
|
||||
foreach ($expenses->get() as $expense) {
|
||||
$amount = $expense->amount;
|
||||
$invoiced = $expense->present()->invoiced_amount;
|
||||
|
||||
$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),
|
||||
Utils::formatMoney($invoiced, $expense->invoice_currency_id),
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->expense_currency_id, 'amount', $amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->invoice_currency_id, 'amount', 0);
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->invoice_currency_id, 'invoiced', $invoiced);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->expense_currency_id, 'invoiced', 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function export($data, $columns, $totals)
|
||||
{
|
||||
$output = fopen('php://output', 'w') or Utils::fatalError();
|
||||
header('Content-Type:application/csv');
|
||||
header('Content-Disposition:attachment;filename=ninja-report.csv');
|
||||
|
||||
Utils::exportData($output, $data);
|
||||
Utils::exportData($output, $data, Utils::trans($columns));
|
||||
|
||||
fwrite($output, trans('texts.totals'));
|
||||
foreach ($totals as $currencyId => $fields) {
|
||||
foreach ($fields as $key => $value) {
|
||||
fwrite($output, ',' . trans("texts.{$key}"));
|
||||
}
|
||||
fwrite($output, "\n");
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (['amount', 'paid', 'balance'] as $type) {
|
||||
$csv = trans("texts.{$type}").',';
|
||||
foreach ($totals[$type] as $currencyId => $amount) {
|
||||
$csv .= Utils::formatMoney($amount, $currencyId).',';
|
||||
foreach ($totals as $currencyId => $fields) {
|
||||
$csv = Utils::getFromCache($currencyId, 'currencies')->name . ',';
|
||||
foreach ($fields as $key => $value) {
|
||||
$csv .= '"' . Utils::formatMoney($value, $currencyId).'",';
|
||||
}
|
||||
fwrite($output, $csv."\n");
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Routes
|
||||
@ -572,6 +573,9 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('REMINDER_FIELD_DUE_DATE', 1);
|
||||
define('REMINDER_FIELD_INVOICE_DATE', 2);
|
||||
|
||||
define('FILTER_INVOICE_DATE', 'invoice_date');
|
||||
define('FILTER_PAYMENT_DATE', 'payment_date');
|
||||
|
||||
define('SOCIAL_GOOGLE', 'Google');
|
||||
define('SOCIAL_FACEBOOK', 'Facebook');
|
||||
define('SOCIAL_GITHUB', 'GitHub');
|
||||
|
@ -767,9 +767,11 @@ class Utils
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function exportData($output, $data)
|
||||
public static function exportData($output, $data, $headers = false)
|
||||
{
|
||||
if (count($data) > 0) {
|
||||
if ($headers) {
|
||||
fputcsv($output, $headers);
|
||||
} elseif (count($data) > 0) {
|
||||
fputcsv($output, array_keys($data[0]));
|
||||
}
|
||||
|
||||
|
@ -272,6 +272,11 @@ class Client extends EntityModel
|
||||
return $token ? "https://dashboard.stripe.com/customers/{$token}" : false;
|
||||
}
|
||||
|
||||
public function getAmount()
|
||||
{
|
||||
return $this->balance + $this->paid_to_date;
|
||||
}
|
||||
|
||||
public function getCurrencyId()
|
||||
{
|
||||
if ($this->currency_id) {
|
||||
|
@ -81,6 +81,11 @@ class EntityModel extends Eloquent
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function scopeWithArchived($query)
|
||||
{
|
||||
return $query->withTrashed()->where('is_deleted', '=', false);
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->public_id;
|
||||
|
@ -129,13 +129,21 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAmountPaid()
|
||||
public function getAmountPaid($calculate = false)
|
||||
{
|
||||
if ($this->is_quote || $this->is_recurring) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($this->amount - $this->balance);
|
||||
if ($calculate) {
|
||||
$amount = 0;
|
||||
foreach ($this->payments as $payment) {
|
||||
$amount += $payment->amount;
|
||||
}
|
||||
return $amount;
|
||||
} else {
|
||||
return ($this->amount - $this->balance);
|
||||
}
|
||||
}
|
||||
|
||||
public function trashed()
|
||||
@ -752,6 +760,98 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
|
||||
return Utils::decodePDF($pdfString);
|
||||
}
|
||||
|
||||
public function getItemTaxable($invoiceItem, $invoiceTotal)
|
||||
{
|
||||
$total = $invoiceItem->qty * $invoiceItem->cost;
|
||||
|
||||
if ($this->discount > 0) {
|
||||
if ($this->is_amount_discount) {
|
||||
$total -= $invoiceTotal ? ($total / $invoiceTotal * $this->discount) : 0;
|
||||
} else {
|
||||
$total *= (100 - $this->discount) / 100;
|
||||
$total = round($total, 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
public function getTaxable()
|
||||
{
|
||||
$total = 0;
|
||||
|
||||
foreach ($this->invoice_items as $invoiceItem) {
|
||||
$total += $invoiceItem->qty * $invoiceItem->cost;
|
||||
}
|
||||
|
||||
if ($this->discount > 0) {
|
||||
if ($this->is_amount_discount) {
|
||||
$total -= $this->discount;
|
||||
} else {
|
||||
$total *= (100 - $this->discount) / 100;
|
||||
$total = round($total, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->custom_value1 && $this->custom_taxes1) {
|
||||
$total += $this->custom_value1;
|
||||
}
|
||||
|
||||
if ($this->custom_value2 && $this->custom_taxes2) {
|
||||
$total += $this->custom_value2;
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
public function getTaxes($calculatePaid = false)
|
||||
{
|
||||
$taxes = [];
|
||||
$taxable = $this->getTaxable();
|
||||
|
||||
if ($this->tax_rate && $this->tax_name) {
|
||||
$taxAmount = $taxable * ($this->tax_rate / 100);
|
||||
$taxAmount = round($taxAmount, 2);
|
||||
|
||||
if ($taxAmount) {
|
||||
$taxes[$this->tax_name.$this->tax_rate] = [
|
||||
'name' => $this->tax_name,
|
||||
'rate' => $this->tax_rate,
|
||||
'amount' => $taxAmount,
|
||||
'paid' => round($this->getAmountPaid($calculatePaid) / $this->amount * $taxAmount, 2)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->invoice_items as $invoiceItem) {
|
||||
if ( ! $invoiceItem->tax_rate || ! $invoiceItem->tax_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$taxAmount = $this->getItemTaxable($invoiceItem, $taxable);
|
||||
$taxAmount = $taxable * ($invoiceItem->tax_rate / 100);
|
||||
$taxAmount = round($taxAmount, 2);
|
||||
|
||||
if ($taxAmount) {
|
||||
$key = $invoiceItem->tax_name.$invoiceItem->tax_rate;
|
||||
|
||||
if ( ! isset($taxes[$key])) {
|
||||
$taxes[$key] = [
|
||||
'amount' => 0,
|
||||
'paid' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$taxes[$key]['amount'] += $taxAmount;
|
||||
$taxes[$key]['paid'] += $this->amount && $taxAmount ? round($this->getAmountPaid($calculatePaid) / $this->amount * $taxAmount, 2) : 0;
|
||||
$taxes[$key]['name'] = $invoiceItem->tax_name;
|
||||
$taxes[$key]['rate'] = $invoiceItem->tax_rate;
|
||||
}
|
||||
}
|
||||
|
||||
return $taxes;
|
||||
}
|
||||
}
|
||||
|
||||
Invoice::creating(function ($invoice) {
|
||||
|
@ -26,6 +26,10 @@ class ClientPresenter extends Presenter {
|
||||
}
|
||||
|
||||
return "<span class=\"label label-{$class}\">{$text}</span>";
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/clients/' . $this->entity->public_id, $this->entity->getDisplayName());
|
||||
}
|
||||
}
|
@ -20,4 +20,14 @@ class ExpensePresenter extends Presenter {
|
||||
{
|
||||
return round($this->entity->amount * $this->entity->exchange_rate, 2);
|
||||
}
|
||||
|
||||
public function invoiced_amount()
|
||||
{
|
||||
return $this->entity->invoice_id ? $this->converted_amount() : 0;
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/expenses/' . $this->entity->public_id, $this->entity->name);
|
||||
}
|
||||
}
|
@ -55,4 +55,9 @@ class InvoicePresenter extends Presenter {
|
||||
return Utils::fromSqlDate($this->entity->due_date);
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/invoices/' . $this->entity->public_id, $this->entity->invoice_number);
|
||||
}
|
||||
|
||||
}
|
@ -9,4 +9,9 @@ class VendorPresenter extends Presenter {
|
||||
{
|
||||
return $this->entity->country ? $this->entity->country->name : '';
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/vendors/' . $this->entity->public_id, $this->entity->name);
|
||||
}
|
||||
}
|
@ -130,11 +130,6 @@ class AccountRepository
|
||||
|
||||
private function getNavigationSearchData()
|
||||
{
|
||||
$features = [
|
||||
['dashboard', '/dashboard'],
|
||||
['customize_design', '/settings/customize_design'],
|
||||
];
|
||||
|
||||
$entityTypes = [
|
||||
ENTITY_INVOICE,
|
||||
ENTITY_CLIENT,
|
||||
@ -157,6 +152,12 @@ class AccountRepository
|
||||
];
|
||||
}
|
||||
|
||||
$features[] = ['dashboard', '/dashboard'];
|
||||
$features[] = ['customize_design', '/settings/customize_design'];
|
||||
$features[] = ['new_tax_rate', '/tax_rates/create'];
|
||||
$features[] = ['new_product', '/products/create'];
|
||||
$features[] = ['new_user', '/users/create'];
|
||||
|
||||
$settings = array_merge(Account::$basicSettings, Account::$advancedSettings);
|
||||
|
||||
foreach ($settings as $setting) {
|
||||
|
@ -223,11 +223,6 @@ class AppServiceProvider extends ServiceProvider {
|
||||
'Illuminate\Contracts\Auth\Registrar',
|
||||
'App\Services\Registrar'
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
'App\Ninja\Import\DataImporterServiceInterface',
|
||||
'App\Ninja\Import\FreshBooks\FreshBooksDataImporterService'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1034,10 +1034,19 @@ $LANG = array(
|
||||
'list_clients' => 'List Clients',
|
||||
'list_quotes' => 'List Quotes',
|
||||
'list_tasks' => 'List Tasks',
|
||||
'list_expensess' => 'List Expenses',
|
||||
'list_expenses' => 'List Expenses',
|
||||
'list_recurring_invoices' => 'List Recurring Invoices',
|
||||
'list_payments' => 'List Payments',
|
||||
'list_credits' => 'List Credits',
|
||||
'tax_name' => 'Tax Name',
|
||||
'report_settings' => 'Report Settings',
|
||||
'search_hotkey' => 'shortcut is /',
|
||||
|
||||
'new_user' => 'New User',
|
||||
'new_product' => 'New Product',
|
||||
'new_tax_rate' => 'New Tax Rate',
|
||||
'invoiced_amount' => 'Invoiced Amount',
|
||||
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -498,7 +498,7 @@
|
||||
<form id="search-form" class="navbar-form navbar-right" role="search" style="display:none">
|
||||
<div class="form-group">
|
||||
<input type="text" id="search" style="width: 240px;padding-top:0px;padding-bottom:0px"
|
||||
class="form-control" placeholder="{{ trans('texts.search') }}">
|
||||
class="form-control" placeholder="{{ trans('texts.search') . ': ' . trans('texts.search_hotkey')}}">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.settings') !!}</h3>
|
||||
<h3 class="panel-title">{!! trans('texts.report_settings') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
@ -54,12 +54,17 @@
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::checkbox('enable_report')->text(trans('texts.enable')) !!}
|
||||
{!! Former::select('report_type')->options($reportTypes, $reportType)->label(trans('texts.group_by')) !!}
|
||||
{!! Former::checkbox('enable_report')->text(trans('texts.enable')) !!}
|
||||
{!! Former::select('report_type')->options($reportTypes, $reportType)->label(trans('texts.type')) !!}
|
||||
<div id="dateField" style="display:{{ $reportType == ENTITY_TAX_RATE ? 'block' : 'none' }}">
|
||||
{!! Former::select('date_field')->label(trans('texts.filter'))
|
||||
->addOption(trans('texts.invoice_date'), FILTER_INVOICE_DATE)
|
||||
->addOption(trans('texts.payment_date'), FILTER_PAYMENT_DATE) !!}
|
||||
</div>
|
||||
<p> </p>
|
||||
{!! Former::checkbox('enable_chart')->text(trans('texts.enable')) !!}
|
||||
{!! Former::checkbox('enable_chart')->text(trans('texts.enable')) !!}
|
||||
{!! Former::select('group_by')->options($dateTypes, $groupBy) !!}
|
||||
{!! Former::select('chart_type')->options($chartTypes, $chartType) !!}
|
||||
|
||||
@ -77,56 +82,48 @@
|
||||
<thead>
|
||||
<tr>
|
||||
@foreach ($columns as $column)
|
||||
<th>
|
||||
{{ trans("texts.{$column}") }}
|
||||
</th>
|
||||
<th>{{ trans("texts.{$column}") }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($displayData as $record)
|
||||
<tr>
|
||||
@foreach ($record as $field)
|
||||
<td>
|
||||
{!! $field !!}
|
||||
</td>
|
||||
<td>{!! $field !!}</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td><b>{{ trans('texts.totals') }}</b></td>
|
||||
@if ($reportType != ENTITY_CLIENT)
|
||||
<td></td>
|
||||
<td></td>
|
||||
@endif
|
||||
<td>
|
||||
@foreach ($reportTotals['amount'] as $currencyId => $total)
|
||||
<b>{{ Utils::formatMoney($total, $currencyId) }}</b><br/>
|
||||
@endforeach
|
||||
</td>
|
||||
@if ($reportType == ENTITY_PAYMENT)
|
||||
<td></td>
|
||||
@endif
|
||||
<td>
|
||||
@foreach ($reportTotals['paid'] as $currencyId => $total)
|
||||
<b>{{ Utils::formatMoney($total, $currencyId) }}</b><br/>
|
||||
@endforeach
|
||||
</td>
|
||||
@if ($reportType != ENTITY_PAYMENT)
|
||||
<td>
|
||||
@foreach ($reportTotals['balance'] as $currencyId => $total)
|
||||
<b>{{ Utils::formatMoney($total, $currencyId) }}</b><br/>
|
||||
@endforeach
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<p> </p>
|
||||
|
||||
@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
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($enableChart)
|
||||
@ -194,6 +191,15 @@
|
||||
$('.end_date .input-group-addon').click(function() {
|
||||
toggleDatePicker('end_date');
|
||||
});
|
||||
|
||||
$('#report_type').change(function() {
|
||||
var val = $('#report_type').val();
|
||||
if (val == '{{ ENTITY_TAX_RATE }}') {
|
||||
$('#dateField').fadeIn();
|
||||
} else {
|
||||
$('#dateField').fadeOut();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user