mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 10:44:29 -04:00
Merge branch 'release-4.2.0'
This commit is contained in:
commit
1633f30a38
@ -73,7 +73,7 @@ class ChargeRenewalInvoices extends Command
|
|||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$this->info(count($invoices).' invoices found');
|
$this->info($invoices->count() . ' invoices found');
|
||||||
|
|
||||||
foreach ($invoices as $invoice) {
|
foreach ($invoices as $invoice) {
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Console\Commands;
|
|||||||
use Carbon;
|
use Carbon;
|
||||||
use App\Libraries\CurlUtils;
|
use App\Libraries\CurlUtils;
|
||||||
use DB;
|
use DB;
|
||||||
|
use App;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Mail;
|
use Mail;
|
||||||
@ -81,6 +82,7 @@ class CheckData extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
//$this->checkInvoices();
|
//$this->checkInvoices();
|
||||||
|
$this->checkTranslations();
|
||||||
$this->checkInvoiceBalances();
|
$this->checkInvoiceBalances();
|
||||||
$this->checkClientBalances();
|
$this->checkClientBalances();
|
||||||
$this->checkContacts();
|
$this->checkContacts();
|
||||||
@ -115,6 +117,40 @@ class CheckData extends Command
|
|||||||
$this->log .= $str . "\n";
|
$this->log .= $str . "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkTranslations()
|
||||||
|
{
|
||||||
|
$invalid = 0;
|
||||||
|
|
||||||
|
foreach (cache('languages') as $language) {
|
||||||
|
App::setLocale($language->locale);
|
||||||
|
foreach (trans('texts') as $text) {
|
||||||
|
if (strpos($text, '=') !== false) {
|
||||||
|
$invalid++;
|
||||||
|
$this->logMessage($language->locale . ' is invalid: ' . $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match('/(.script)/', strtolower($text), $matches);
|
||||||
|
if (count($matches)) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
if (in_array($match, ['escript', 'bscript', 'nscript'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$invalid++;
|
||||||
|
$this->logMessage(sprintf('%s is invalid: %s', $language->locale, $text));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invalid > 0) {
|
||||||
|
$this->isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
App::setLocale('en');
|
||||||
|
$this->logMessage($invalid . ' invalid text strings');
|
||||||
|
}
|
||||||
|
|
||||||
private function checkDraftSentInvoices()
|
private function checkDraftSentInvoices()
|
||||||
{
|
{
|
||||||
$invoices = Invoice::whereInvoiceStatusId(INVOICE_STATUS_SENT)
|
$invoices = Invoice::whereInvoiceStatusId(INVOICE_STATUS_SENT)
|
||||||
@ -122,9 +158,9 @@ class CheckData extends Command
|
|||||||
->withTrashed()
|
->withTrashed()
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$this->logMessage(count($invoices) . ' draft sent invoices');
|
$this->logMessage($invoices->count() . ' draft sent invoices');
|
||||||
|
|
||||||
if (count($invoices) > 0) {
|
if ($invoices->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,9 +226,9 @@ class CheckData extends Command
|
|||||||
->havingRaw('count(users.id) > 1')
|
->havingRaw('count(users.id) > 1')
|
||||||
->get(['users.oauth_user_id']);
|
->get(['users.oauth_user_id']);
|
||||||
|
|
||||||
$this->logMessage(count($users) . ' users with duplicate oauth ids');
|
$this->logMessage($users->count() . ' users with duplicate oauth ids');
|
||||||
|
|
||||||
if (count($users) > 0) {
|
if ($users->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,9 +344,9 @@ class CheckData extends Command
|
|||||||
->whereNull('contact_key')
|
->whereNull('contact_key')
|
||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->get(['id']);
|
->get(['id']);
|
||||||
$this->logMessage(count($contacts) . ' contacts without a contact_key');
|
$this->logMessage($contacts->count() . ' contacts without a contact_key');
|
||||||
|
|
||||||
if (count($contacts) > 0) {
|
if ($contacts->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,9 +375,9 @@ class CheckData extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
$clients = $clients->get(['clients.id', 'clients.user_id', 'clients.account_id']);
|
$clients = $clients->get(['clients.id', 'clients.user_id', 'clients.account_id']);
|
||||||
$this->logMessage(count($clients) . ' clients without any contacts');
|
$this->logMessage($clients->count() . ' clients without any contacts');
|
||||||
|
|
||||||
if (count($clients) > 0) {
|
if ($clients->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,9 +410,9 @@ class CheckData extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
$clients = $clients->get(['clients.id', DB::raw('count(contacts.id)')]);
|
$clients = $clients->get(['clients.id', DB::raw('count(contacts.id)')]);
|
||||||
$this->logMessage(count($clients) . ' clients without a single primary contact');
|
$this->logMessage($clients->count() . ' clients without a single primary contact');
|
||||||
|
|
||||||
if (count($clients) > 0) {
|
if ($clients->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -423,9 +459,9 @@ class CheckData extends Command
|
|||||||
->havingRaw('count(invitations.id) = 0')
|
->havingRaw('count(invitations.id) = 0')
|
||||||
->get(['invoices.id', 'invoices.user_id', 'invoices.account_id', 'invoices.client_id']);
|
->get(['invoices.id', 'invoices.user_id', 'invoices.account_id', 'invoices.client_id']);
|
||||||
|
|
||||||
$this->logMessage(count($invoices) . ' invoices without any invitations');
|
$this->logMessage($invoices->count() . ' invoices without any invitations');
|
||||||
|
|
||||||
if (count($invoices) > 0) {
|
if ($invoices->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,6 +505,10 @@ class CheckData extends Command
|
|||||||
ENTITY_INVOICE,
|
ENTITY_INVOICE,
|
||||||
ENTITY_CLIENT,
|
ENTITY_CLIENT,
|
||||||
ENTITY_USER,
|
ENTITY_USER,
|
||||||
|
ENTITY_TASK_STATUS,
|
||||||
|
],
|
||||||
|
'task_statuses' => [
|
||||||
|
ENTITY_USER,
|
||||||
],
|
],
|
||||||
'credits' => [
|
'credits' => [
|
||||||
ENTITY_CLIENT,
|
ENTITY_CLIENT,
|
||||||
@ -496,6 +536,25 @@ class CheckData extends Command
|
|||||||
ENTITY_USER,
|
ENTITY_USER,
|
||||||
ENTITY_CLIENT,
|
ENTITY_CLIENT,
|
||||||
],
|
],
|
||||||
|
'proposals' => [
|
||||||
|
ENTITY_USER,
|
||||||
|
ENTITY_INVOICE,
|
||||||
|
ENTITY_PROPOSAL_TEMPLATE,
|
||||||
|
],
|
||||||
|
'proposal_categories' => [
|
||||||
|
ENTITY_USER,
|
||||||
|
],
|
||||||
|
'proposal_templates' => [
|
||||||
|
ENTITY_USER,
|
||||||
|
],
|
||||||
|
'proposal_snippets' => [
|
||||||
|
ENTITY_USER,
|
||||||
|
ENTITY_PROPOSAL_CATEGORY,
|
||||||
|
],
|
||||||
|
'proposal_invitations' => [
|
||||||
|
ENTITY_USER,
|
||||||
|
ENTITY_PROPOSAL,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($tables as $table => $entityTypes) {
|
foreach ($tables as $table => $entityTypes) {
|
||||||
@ -512,9 +571,9 @@ class CheckData extends Command
|
|||||||
->where("{$table}.{$accountId}", '!=', DB::raw("{$tableName}.account_id"))
|
->where("{$table}.{$accountId}", '!=', DB::raw("{$tableName}.account_id"))
|
||||||
->get(["{$table}.id"]);
|
->get(["{$table}.id"]);
|
||||||
|
|
||||||
if (count($records)) {
|
if ($records->count()) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
$this->logMessage(count($records) . " {$table} records with incorrect {$entityType} account id");
|
$this->logMessage($records->count() . " {$table} records with incorrect {$entityType} account id");
|
||||||
|
|
||||||
if ($this->option('fix') == 'true') {
|
if ($this->option('fix') == 'true') {
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
@ -549,9 +608,9 @@ class CheckData extends Command
|
|||||||
->groupBy('clients.id')
|
->groupBy('clients.id')
|
||||||
->havingRaw('clients.paid_to_date != sum(coalesce(payments.amount - payments.refunded, 0)) and clients.paid_to_date != 999999999.9999')
|
->havingRaw('clients.paid_to_date != sum(coalesce(payments.amount - payments.refunded, 0)) and clients.paid_to_date != 999999999.9999')
|
||||||
->get(['clients.id', 'clients.paid_to_date', DB::raw('sum(coalesce(payments.amount - payments.refunded, 0)) as amount')]);
|
->get(['clients.id', 'clients.paid_to_date', DB::raw('sum(coalesce(payments.amount - payments.refunded, 0)) as amount')]);
|
||||||
$this->logMessage(count($clients) . ' clients with incorrect paid to date');
|
$this->logMessage($clients->count() . ' clients with incorrect paid to date');
|
||||||
|
|
||||||
if (count($clients) > 0) {
|
if ($clients->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,9 +639,9 @@ class CheckData extends Command
|
|||||||
->havingRaw('(invoices.amount - invoices.balance) != coalesce(sum(payments.amount - payments.refunded), 0)')
|
->havingRaw('(invoices.amount - invoices.balance) != coalesce(sum(payments.amount - payments.refunded), 0)')
|
||||||
->get(['invoices.id', 'invoices.amount', 'invoices.balance', DB::raw('coalesce(sum(payments.amount - payments.refunded), 0)')]);
|
->get(['invoices.id', 'invoices.amount', 'invoices.balance', DB::raw('coalesce(sum(payments.amount - payments.refunded), 0)')]);
|
||||||
|
|
||||||
$this->logMessage(count($invoices) . ' invoices with incorrect balances');
|
$this->logMessage($invoices->count() . ' invoices with incorrect balances');
|
||||||
|
|
||||||
if (count($invoices) > 0) {
|
if ($invoices->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,9 +667,9 @@ class CheckData extends Command
|
|||||||
$clients = $clients->groupBy('clients.id', 'clients.balance')
|
$clients = $clients->groupBy('clients.id', 'clients.balance')
|
||||||
->orderBy('accounts.company_id', 'DESC')
|
->orderBy('accounts.company_id', 'DESC')
|
||||||
->get(['accounts.company_id', 'clients.account_id', 'clients.id', 'clients.balance', 'clients.paid_to_date', DB::raw('sum(invoices.balance) actual_balance')]);
|
->get(['accounts.company_id', 'clients.account_id', 'clients.id', 'clients.balance', 'clients.paid_to_date', DB::raw('sum(invoices.balance) actual_balance')]);
|
||||||
$this->logMessage(count($clients) . ' clients with incorrect balance/activities');
|
$this->logMessage($clients->count() . ' clients with incorrect balance/activities');
|
||||||
|
|
||||||
if (count($clients) > 0) {
|
if ($clients->count() > 0) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,6 +360,7 @@ class InitLookup extends Command
|
|||||||
DB::statement('truncate lookup_users');
|
DB::statement('truncate lookup_users');
|
||||||
DB::statement('truncate lookup_contacts');
|
DB::statement('truncate lookup_contacts');
|
||||||
DB::statement('truncate lookup_invitations');
|
DB::statement('truncate lookup_invitations');
|
||||||
|
DB::statement('truncate lookup_proposal_invitations');
|
||||||
DB::statement('truncate lookup_account_tokens');
|
DB::statement('truncate lookup_account_tokens');
|
||||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class RemoveOrphanedDocuments extends Command
|
|||||||
$documents = Document::whereRaw('invoice_id IS NULL AND expense_id IS NULL AND updated_at <= ?', [new DateTime('-1 hour')])
|
$documents = Document::whereRaw('invoice_id IS NULL AND expense_id IS NULL AND updated_at <= ?', [new DateTime('-1 hour')])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$this->info(count($documents).' orphaned document(s) found');
|
$this->info($documents->count() . ' orphaned document(s) found');
|
||||||
|
|
||||||
foreach ($documents as $document) {
|
foreach ($documents as $document) {
|
||||||
$document->delete();
|
$document->delete();
|
||||||
|
@ -98,7 +98,7 @@ class SendRecurringInvoices extends Command
|
|||||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||||
->orderBy('id', 'asc')
|
->orderBy('id', 'asc')
|
||||||
->get();
|
->get();
|
||||||
$this->info(count($invoices).' recurring invoice(s) found');
|
$this->info($invoices->count() . ' recurring invoice(s) found');
|
||||||
|
|
||||||
foreach ($invoices as $recurInvoice) {
|
foreach ($invoices as $recurInvoice) {
|
||||||
$shouldSendToday = $recurInvoice->shouldSendToday();
|
$shouldSendToday = $recurInvoice->shouldSendToday();
|
||||||
@ -140,7 +140,7 @@ class SendRecurringInvoices extends Command
|
|||||||
[$today->format('Y-m-d')])
|
[$today->format('Y-m-d')])
|
||||||
->orderBy('invoices.id', 'asc')
|
->orderBy('invoices.id', 'asc')
|
||||||
->get();
|
->get();
|
||||||
$this->info(count($delayedAutoBillInvoices).' due recurring invoice instance(s) found');
|
$this->info($delayedAutoBillInvoices->count() . ' due recurring invoice instance(s) found');
|
||||||
|
|
||||||
/** @var Invoice $invoice */
|
/** @var Invoice $invoice */
|
||||||
foreach ($delayedAutoBillInvoices as $invoice) {
|
foreach ($delayedAutoBillInvoices as $invoice) {
|
||||||
@ -165,7 +165,7 @@ class SendRecurringInvoices extends Command
|
|||||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||||
->orderBy('id', 'asc')
|
->orderBy('id', 'asc')
|
||||||
->get();
|
->get();
|
||||||
$this->info(count($expenses).' recurring expenses(s) found');
|
$this->info($expenses->count() . ' recurring expenses(s) found');
|
||||||
|
|
||||||
foreach ($expenses as $expense) {
|
foreach ($expenses as $expense) {
|
||||||
$shouldSendToday = $expense->shouldSendToday();
|
$shouldSendToday = $expense->shouldSendToday();
|
||||||
|
@ -92,7 +92,7 @@ class SendReminders extends Command
|
|||||||
private function chargeLateFees()
|
private function chargeLateFees()
|
||||||
{
|
{
|
||||||
$accounts = $this->accountRepo->findWithFees();
|
$accounts = $this->accountRepo->findWithFees();
|
||||||
$this->info(count($accounts) . ' accounts found with fees');
|
$this->info($accounts->count() . ' accounts found with fees');
|
||||||
|
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||||
@ -100,7 +100,7 @@ class SendReminders extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
$invoices = $this->invoiceRepo->findNeedingReminding($account, false);
|
$invoices = $this->invoiceRepo->findNeedingReminding($account, false);
|
||||||
$this->info($account->name . ': ' . count($invoices) . ' invoices found');
|
$this->info($account->name . ': ' . $invoices->count() . ' invoices found');
|
||||||
|
|
||||||
foreach ($invoices as $invoice) {
|
foreach ($invoices as $invoice) {
|
||||||
if ($reminder = $account->getInvoiceReminder($invoice, false)) {
|
if ($reminder = $account->getInvoiceReminder($invoice, false)) {
|
||||||
@ -128,7 +128,7 @@ class SendReminders extends Command
|
|||||||
|
|
||||||
// standard reminders
|
// standard reminders
|
||||||
$invoices = $this->invoiceRepo->findNeedingReminding($account);
|
$invoices = $this->invoiceRepo->findNeedingReminding($account);
|
||||||
$this->info($account->name . ': ' . count($invoices) . ' invoices found');
|
$this->info($account->name . ': ' . $invoices->count() . ' invoices found');
|
||||||
|
|
||||||
foreach ($invoices as $invoice) {
|
foreach ($invoices as $invoice) {
|
||||||
if ($reminder = $account->getInvoiceReminder($invoice)) {
|
if ($reminder = $account->getInvoiceReminder($invoice)) {
|
||||||
@ -142,7 +142,7 @@ class SendReminders extends Command
|
|||||||
|
|
||||||
// endless reminders
|
// endless reminders
|
||||||
$invoices = $this->invoiceRepo->findNeedingEndlessReminding($account);
|
$invoices = $this->invoiceRepo->findNeedingEndlessReminding($account);
|
||||||
$this->info($account->name . ': ' . count($invoices) . ' endless invoices found');
|
$this->info($account->name . ': ' . $invoices->count() . ' endless invoices found');
|
||||||
|
|
||||||
foreach ($invoices as $invoice) {
|
foreach ($invoices as $invoice) {
|
||||||
if ($invoice->last_sent_date == date('Y-m-d')) {
|
if ($invoice->last_sent_date == date('Y-m-d')) {
|
||||||
@ -159,7 +159,7 @@ class SendReminders extends Command
|
|||||||
$scheduledReports = ScheduledReport::where('send_date', '<=', date('Y-m-d'))
|
$scheduledReports = ScheduledReport::where('send_date', '<=', date('Y-m-d'))
|
||||||
->with('user', 'account.company')
|
->with('user', 'account.company')
|
||||||
->get();
|
->get();
|
||||||
$this->info(count($scheduledReports) . ' scheduled reports');
|
$this->info($scheduledReports->count() . ' scheduled reports');
|
||||||
|
|
||||||
foreach ($scheduledReports as $scheduledReport) {
|
foreach ($scheduledReports as $scheduledReport) {
|
||||||
$user = $scheduledReport->user;
|
$user = $scheduledReport->user;
|
||||||
|
@ -60,10 +60,10 @@ class SendRenewalInvoices extends Command
|
|||||||
$companies = Company::whereRaw("datediff(plan_expires, curdate()) = 10 and (plan = 'pro' or plan = 'enterprise')")
|
$companies = Company::whereRaw("datediff(plan_expires, curdate()) = 10 and (plan = 'pro' or plan = 'enterprise')")
|
||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->get();
|
->get();
|
||||||
$this->info(count($companies).' companies found renewing in 10 days');
|
$this->info($companies->count() . ' companies found renewing in 10 days');
|
||||||
|
|
||||||
foreach ($companies as $company) {
|
foreach ($companies as $company) {
|
||||||
if (! count($company->accounts)) {
|
if (! $company->accounts->count()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ class $CLASS$ extends AuthServiceProvider
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function boot(GateContract $gate)
|
public function boot()
|
||||||
{
|
{
|
||||||
parent::boot($gate);
|
parent::boot();
|
||||||
|
|
||||||
$this->registerTranslations();
|
$this->registerTranslations();
|
||||||
$this->registerConfig();
|
$this->registerConfig();
|
||||||
|
@ -53,17 +53,5 @@ class Kernel extends ConsoleKernel
|
|||||||
->command('ninja:send-reminders --force')
|
->command('ninja:send-reminders --force')
|
||||||
->sendOutputTo($logFile)
|
->sendOutputTo($logFile)
|
||||||
->daily();
|
->daily();
|
||||||
|
|
||||||
if (Utils::isNinja()) {
|
|
||||||
$schedule
|
|
||||||
->command('ninja:send-renewals --force')
|
|
||||||
->sendOutputTo($logFile)
|
|
||||||
->daily();
|
|
||||||
}
|
|
||||||
|
|
||||||
$schedule
|
|
||||||
->command('updater:check-for-update --prefixVersionWith=v')
|
|
||||||
->sendOutputTo($logFile)
|
|
||||||
->daily();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,11 @@ if (! defined('APP_NAME')) {
|
|||||||
define('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
|
define('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
|
||||||
define('ENTITY_CUSTOMER', 'customer');
|
define('ENTITY_CUSTOMER', 'customer');
|
||||||
define('ENTITY_SUBSCRIPTION', 'subscription');
|
define('ENTITY_SUBSCRIPTION', 'subscription');
|
||||||
|
define('ENTITY_PROPOSAL', 'proposal');
|
||||||
|
define('ENTITY_PROPOSAL_TEMPLATE', 'proposal_template');
|
||||||
|
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
|
||||||
|
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
|
||||||
|
define('ENTITY_PROPOSAL_INVITATION', 'proposal_invitation');
|
||||||
|
|
||||||
define('INVOICE_TYPE_STANDARD', 1);
|
define('INVOICE_TYPE_STANDARD', 1);
|
||||||
define('INVOICE_TYPE_QUOTE', 2);
|
define('INVOICE_TYPE_QUOTE', 2);
|
||||||
@ -153,6 +158,7 @@ if (! defined('APP_NAME')) {
|
|||||||
define('MAX_DOCUMENT_SIZE', env('MAX_DOCUMENT_SIZE', 10000)); // KB
|
define('MAX_DOCUMENT_SIZE', env('MAX_DOCUMENT_SIZE', 10000)); // KB
|
||||||
define('MAX_EMAIL_DOCUMENTS_SIZE', env('MAX_EMAIL_DOCUMENTS_SIZE', 10000)); // Total KB
|
define('MAX_EMAIL_DOCUMENTS_SIZE', env('MAX_EMAIL_DOCUMENTS_SIZE', 10000)); // Total KB
|
||||||
define('MAX_ZIP_DOCUMENTS_SIZE', env('MAX_EMAIL_DOCUMENTS_SIZE', 30000)); // Total KB (uncompressed)
|
define('MAX_ZIP_DOCUMENTS_SIZE', env('MAX_EMAIL_DOCUMENTS_SIZE', 30000)); // Total KB (uncompressed)
|
||||||
|
define('MAX_EMAILS_SENT_PER_HOUR', 90);
|
||||||
define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300)); // pixels
|
define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300)); // pixels
|
||||||
define('DEFAULT_FONT_SIZE', 9);
|
define('DEFAULT_FONT_SIZE', 9);
|
||||||
define('DEFAULT_HEADER_FONT', 1); // Roboto
|
define('DEFAULT_HEADER_FONT', 1); // Roboto
|
||||||
@ -290,6 +296,7 @@ if (! defined('APP_NAME')) {
|
|||||||
define('GATEWAY_DWOLLA', 43);
|
define('GATEWAY_DWOLLA', 43);
|
||||||
define('GATEWAY_CHECKOUT_COM', 47);
|
define('GATEWAY_CHECKOUT_COM', 47);
|
||||||
define('GATEWAY_CYBERSOURCE', 49);
|
define('GATEWAY_CYBERSOURCE', 49);
|
||||||
|
define('GATEWAY_PAYTRACE', 56);
|
||||||
define('GATEWAY_WEPAY', 60);
|
define('GATEWAY_WEPAY', 60);
|
||||||
define('GATEWAY_BRAINTREE', 61);
|
define('GATEWAY_BRAINTREE', 61);
|
||||||
define('GATEWAY_CUSTOM', 62);
|
define('GATEWAY_CUSTOM', 62);
|
||||||
@ -331,7 +338,7 @@ if (! defined('APP_NAME')) {
|
|||||||
define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com'));
|
define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com'));
|
||||||
define('NINJA_DOCS_URL', env('NINJA_DOCS_URL', 'http://docs.invoiceninja.com/en/latest'));
|
define('NINJA_DOCS_URL', env('NINJA_DOCS_URL', 'http://docs.invoiceninja.com/en/latest'));
|
||||||
define('NINJA_DATE', '2000-01-01');
|
define('NINJA_DATE', '2000-01-01');
|
||||||
define('NINJA_VERSION', '4.1.5' . env('NINJA_VERSION_SUFFIX'));
|
define('NINJA_VERSION', '4.2.0' . env('NINJA_VERSION_SUFFIX'));
|
||||||
|
|
||||||
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
||||||
define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'));
|
define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'));
|
||||||
@ -452,6 +459,7 @@ if (! defined('APP_NAME')) {
|
|||||||
|
|
||||||
define('TEMPLATE_INVOICE', 'invoice');
|
define('TEMPLATE_INVOICE', 'invoice');
|
||||||
define('TEMPLATE_QUOTE', 'quote');
|
define('TEMPLATE_QUOTE', 'quote');
|
||||||
|
define('TEMPLATE_PROPOSAL', 'proposal');
|
||||||
define('TEMPLATE_PARTIAL', 'partial');
|
define('TEMPLATE_PARTIAL', 'partial');
|
||||||
define('TEMPLATE_PAYMENT', 'payment');
|
define('TEMPLATE_PAYMENT', 'payment');
|
||||||
define('TEMPLATE_REMINDER1', 'reminder1');
|
define('TEMPLATE_REMINDER1', 'reminder1');
|
||||||
@ -517,6 +525,9 @@ if (! defined('APP_NAME')) {
|
|||||||
define('PLAN_TERM_MONTHLY', 'month');
|
define('PLAN_TERM_MONTHLY', 'month');
|
||||||
define('PLAN_TERM_YEARLY', 'year');
|
define('PLAN_TERM_YEARLY', 'year');
|
||||||
|
|
||||||
|
define('SUBSCRIPTION_FORMAT_JSON', 'JSON');
|
||||||
|
define('SUBSCRIPTION_FORMAT_UBL', 'UBL');
|
||||||
|
|
||||||
// Pro
|
// Pro
|
||||||
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
|
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
|
||||||
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
|
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
|
||||||
@ -602,7 +613,6 @@ if (! defined('APP_NAME')) {
|
|||||||
'dateFormats' => 'App\Models\DateFormat',
|
'dateFormats' => 'App\Models\DateFormat',
|
||||||
'datetimeFormats' => 'App\Models\DatetimeFormat',
|
'datetimeFormats' => 'App\Models\DatetimeFormat',
|
||||||
'languages' => 'App\Models\Language',
|
'languages' => 'App\Models\Language',
|
||||||
'paymentTerms' => 'App\Models\PaymentTerm',
|
|
||||||
'paymentTypes' => 'App\Models\PaymentType',
|
'paymentTypes' => 'App\Models\PaymentType',
|
||||||
'countries' => 'App\Models\Country',
|
'countries' => 'App\Models\Country',
|
||||||
'invoiceDesigns' => 'App\Models\InvoiceDesign',
|
'invoiceDesigns' => 'App\Models\InvoiceDesign',
|
||||||
|
21
app/Events/SubdomainWasRemoved.php
Normal file
21
app/Events/SubdomainWasRemoved.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class SubdomainWasRemoved extends Event
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
public $account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param $account
|
||||||
|
*/
|
||||||
|
public function __construct($account)
|
||||||
|
{
|
||||||
|
$this->account = $account;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Crawler;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Auth\AuthenticationException;
|
use Illuminate\Auth\AuthenticationException;
|
||||||
use Illuminate\Auth\Access\AuthorizationException;
|
use Illuminate\Auth\Access\AuthorizationException;
|
||||||
@ -51,11 +50,12 @@ class Handler extends ExceptionHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! class_exists('Utils')) {
|
// if these classes don't exist the install is broken, maybe due to permissions
|
||||||
|
if (! class_exists('Utils') || ! class_exists('Crawler')) {
|
||||||
return parent::report($e);
|
return parent::report($e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Crawler::isCrawler()) {
|
if (\Crawler::isCrawler()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,12 +184,10 @@ class AccountApiController extends BaseAPIController
|
|||||||
|
|
||||||
$devices = json_decode($account->devices, true);
|
$devices = json_decode($account->devices, true);
|
||||||
|
|
||||||
foreach($devices as $key => $value)
|
for($x=0; $x<count($devices); $x++)
|
||||||
{
|
{
|
||||||
|
if($request->token == $devices[$x]['token'])
|
||||||
if($request->token == $value['token'])
|
unset($devices[$x]);
|
||||||
unset($devices[$key]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$account->devices = json_encode(array_values($devices));
|
$account->devices = json_encode(array_values($devices));
|
||||||
|
File diff suppressed because one or more lines are too long
@ -152,7 +152,7 @@ class AccountGatewayController extends BaseController
|
|||||||
'config' => false,
|
'config' => false,
|
||||||
'gateways' => $gateways,
|
'gateways' => $gateways,
|
||||||
'creditCardTypes' => $creditCards,
|
'creditCardTypes' => $creditCards,
|
||||||
'countGateways' => count($currentGateways),
|
'countGateways' => $currentGateways->count(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,14 +175,18 @@ class AppController extends BaseController
|
|||||||
$_ENV['DB_PASSWORD'] = $db['type']['password'];
|
$_ENV['DB_PASSWORD'] = $db['type']['password'];
|
||||||
|
|
||||||
if ($mail) {
|
if ($mail) {
|
||||||
$_ENV['MAIL_DRIVER'] = $mail['driver'];
|
$prefix = '';
|
||||||
$_ENV['MAIL_PORT'] = $mail['port'];
|
if (($user = auth()->user()) && Account::count() > 1) {
|
||||||
$_ENV['MAIL_ENCRYPTION'] = $mail['encryption'];
|
$prefix = $user->account_id . '_';
|
||||||
$_ENV['MAIL_HOST'] = $mail['host'];
|
}
|
||||||
$_ENV['MAIL_USERNAME'] = $mail['username'];
|
$_ENV[$prefix . 'MAIL_DRIVER'] = $mail['driver'];
|
||||||
$_ENV['MAIL_FROM_NAME'] = $mail['from']['name'];
|
$_ENV[$prefix . 'MAIL_PORT'] = $mail['port'];
|
||||||
$_ENV['MAIL_FROM_ADDRESS'] = $mail['from']['address'];
|
$_ENV[$prefix . 'MAIL_ENCRYPTION'] = $mail['encryption'];
|
||||||
$_ENV['MAIL_PASSWORD'] = $mail['password'];
|
$_ENV[$prefix . 'MAIL_HOST'] = $mail['host'];
|
||||||
|
$_ENV[$prefix . 'MAIL_USERNAME'] = $mail['username'];
|
||||||
|
$_ENV[$prefix . 'MAIL_FROM_NAME'] = $mail['from']['name'];
|
||||||
|
$_ENV[$prefix . 'MAIL_FROM_ADDRESS'] = $mail['from']['address'];
|
||||||
|
$_ENV[$prefix . 'MAIL_PASSWORD'] = $mail['password'];
|
||||||
$_ENV['MAILGUN_DOMAIN'] = $mail['mailgun_domain'];
|
$_ENV['MAILGUN_DOMAIN'] = $mail['mailgun_domain'];
|
||||||
$_ENV['MAILGUN_SECRET'] = $mail['mailgun_secret'];
|
$_ENV['MAILGUN_SECRET'] = $mail['mailgun_secret'];
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ namespace App\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use Event;
|
use Event;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Models\PasswordReset;
|
|
||||||
use App\Events\UserLoggedIn;
|
use App\Events\UserLoggedIn;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
|
@ -92,9 +92,11 @@ class BankAccountController extends BaseController
|
|||||||
if ($publicId) {
|
if ($publicId) {
|
||||||
$bankAccount = BankAccount::scope($publicId)->firstOrFail();
|
$bankAccount = BankAccount::scope($publicId)->firstOrFail();
|
||||||
if ($username != $bankAccount->username) {
|
if ($username != $bankAccount->username) {
|
||||||
// TODO update username
|
$bankAccount->setUsername($username);
|
||||||
}
|
$bankAccount->save();
|
||||||
|
} else {
|
||||||
$username = Crypt::decrypt($username);
|
$username = Crypt::decrypt($username);
|
||||||
|
}
|
||||||
$bankId = $bankAccount->bank_id;
|
$bankId = $bankAccount->bank_id;
|
||||||
} else {
|
} else {
|
||||||
$bankAccount = new BankAccount;
|
$bankAccount = new BankAccount;
|
||||||
|
@ -99,11 +99,13 @@ class BaseAPIController extends Controller
|
|||||||
|
|
||||||
$query->with($includes);
|
$query->with($includes);
|
||||||
|
|
||||||
if ($updatedAt = intval(Input::get('updated_at'))) {
|
if (Input::get('updated_at') > 0) {
|
||||||
|
$updatedAt = intval(Input::get('updated_at'));
|
||||||
$query->where('updated_at', '>=', date('Y-m-d H:i:s', $updatedAt));
|
$query->where('updated_at', '>=', date('Y-m-d H:i:s', $updatedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($clientPublicId = Input::get('client_id')) {
|
if (Input::get('client_id') > 0) {
|
||||||
|
$clientPublicId = Input::get('client_id');
|
||||||
$filter = function ($query) use ($clientPublicId) {
|
$filter = function ($query) use ($clientPublicId) {
|
||||||
$query->where('public_id', '=', $clientPublicId);
|
$query->where('public_id', '=', $clientPublicId);
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,7 @@ class CalendarController extends BaseController
|
|||||||
public function showCalendar()
|
public function showCalendar()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
|
'title' => trans('texts.calendar'),
|
||||||
'account' => auth()->user()->account,
|
'account' => auth()->user()->account,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class LoginController extends Controller
|
|||||||
public function showLoginForm()
|
public function showLoginForm()
|
||||||
{
|
{
|
||||||
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
|
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
|
||||||
$hasAccountIndentifier = request()->account_key || ($subdomain && $subdomain != 'app');
|
$hasAccountIndentifier = request()->account_key || ($subdomain && ! in_array($subdomain, ['www', 'app']));
|
||||||
|
|
||||||
if (! session('contact_key')) {
|
if (! session('contact_key')) {
|
||||||
if (Utils::isNinja()) {
|
if (Utils::isNinja()) {
|
||||||
|
@ -4,7 +4,6 @@ namespace App\Http\Controllers\ClientAuth;
|
|||||||
|
|
||||||
use Password;
|
use Password;
|
||||||
use Config;
|
use Config;
|
||||||
use App\Models\PasswordReset;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -55,14 +54,8 @@ class ResetPasswordController extends Controller
|
|||||||
|
|
||||||
public function showResetForm(Request $request, $token = null)
|
public function showResetForm(Request $request, $token = null)
|
||||||
{
|
{
|
||||||
$passwordReset = PasswordReset::whereToken($token)->first();
|
|
||||||
|
|
||||||
if (! $passwordReset) {
|
|
||||||
return redirect('login')->withMessage(trans('texts.invalid_code'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('clientauth.passwords.reset')->with(
|
return view('clientauth.passwords.reset')->with(
|
||||||
['token' => $token, 'email' => $passwordReset->email]
|
['token' => $token]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Events\InvoiceInvitationWasViewed;
|
use App\Events\InvoiceInvitationWasViewed;
|
||||||
use App\Events\QuoteInvitationWasViewed;
|
use App\Events\QuoteInvitationWasViewed;
|
||||||
|
use App\Models\Account;
|
||||||
use App\Models\Contact;
|
use App\Models\Contact;
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use App\Models\Gateway;
|
use App\Models\Gateway;
|
||||||
@ -55,7 +56,7 @@ class ClientPortalController extends BaseController
|
|||||||
$this->taskRepo = $taskRepo;
|
$this->taskRepo = $taskRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function view($invitationKey)
|
public function viewInvoice($invitationKey)
|
||||||
{
|
{
|
||||||
if (! $invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
|
if (! $invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -76,8 +77,6 @@ class ClientPortalController extends BaseController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$account->loadLocalizationSettings($client);
|
|
||||||
|
|
||||||
if (! Input::has('phantomjs') && ! session('silent:' . $client->id) && ! Session::has($invitation->invitation_key)
|
if (! Input::has('phantomjs') && ! session('silent:' . $client->id) && ! Session::has($invitation->invitation_key)
|
||||||
&& (! Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
&& (! Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||||
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
@ -117,10 +116,10 @@ class ClientPortalController extends BaseController
|
|||||||
|
|
||||||
// translate the country names
|
// translate the country names
|
||||||
if ($invoice->client->country) {
|
if ($invoice->client->country) {
|
||||||
$invoice->client->country->name = trans('texts.country_' . $invoice->client->country->name);
|
$invoice->client->country->name = $invoice->client->country->getName();
|
||||||
}
|
}
|
||||||
if ($invoice->account->country) {
|
if ($invoice->account->country) {
|
||||||
$invoice->account->country->name = trans('texts.country_' . $invoice->account->country->name);
|
$invoice->account->country->name = $invoice->account->country->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
@ -226,7 +225,7 @@ class ClientPortalController extends BaseController
|
|||||||
return $pdfString;
|
return $pdfString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sign($invitationKey)
|
public function authorizeInvoice($invitationKey)
|
||||||
{
|
{
|
||||||
if (! $invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
|
if (! $invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
|
||||||
return RESULT_FAILURE;
|
return RESULT_FAILURE;
|
||||||
@ -262,13 +261,13 @@ class ClientPortalController extends BaseController
|
|||||||
return redirect(request()->url());
|
return redirect(request()->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
$account->loadLocalizationSettings($client);
|
|
||||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||||
$customer = false;
|
$customer = false;
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
} elseif (! $account->enable_client_portal_dashboard) {
|
} elseif (! $account->enable_client_portal_dashboard) {
|
||||||
|
session()->reflash();
|
||||||
return redirect()->to('/client/invoices/');
|
return redirect()->to('/client/invoices/');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +333,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -368,7 +366,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -414,7 +411,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -499,7 +495,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -535,7 +530,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -571,7 +565,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $contact->client->show_tasks_in_portal) {
|
if (! $contact->client->show_tasks_in_portal) {
|
||||||
return redirect()->to($account->enable_client_portal_dashboard ? '/client/dashboard' : '/client/payment_methods/');
|
return redirect()->to($account->enable_client_portal_dashboard ? '/client/dashboard' : '/client/payment_methods/');
|
||||||
@ -611,7 +604,6 @@ class ClientPortalController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $contact->account;
|
$account = $contact->account;
|
||||||
$account->loadLocalizationSettings($contact->client);
|
|
||||||
|
|
||||||
if (! $account->enable_client_portal) {
|
if (! $account->enable_client_portal) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
@ -962,4 +954,65 @@ class ClientPortalController extends BaseController
|
|||||||
|
|
||||||
return Redirect::to('client/invoices/recurring');
|
return Redirect::to('client/invoices/recurring');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function showDetails()
|
||||||
|
{
|
||||||
|
if (! $contact = $this->getContact()) {
|
||||||
|
return $this->returnError();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'contact' => $contact,
|
||||||
|
'client' => $contact->client,
|
||||||
|
'account' => $contact->account,
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('invited.details', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateDetails(\Illuminate\Http\Request $request)
|
||||||
|
{
|
||||||
|
if (! $contact = $this->getContact()) {
|
||||||
|
return $this->returnError();
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $contact->client;
|
||||||
|
$account = $contact->account;
|
||||||
|
|
||||||
|
if (! $account->enable_client_portal) {
|
||||||
|
return $this->returnError();
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'email' => 'required',
|
||||||
|
'address1' => 'required',
|
||||||
|
'city' => 'required',
|
||||||
|
'state' => 'required',
|
||||||
|
'postal_code' => 'required',
|
||||||
|
'country_id' => 'required',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($client->name) {
|
||||||
|
$rules['name'] = 'required';
|
||||||
|
} else {
|
||||||
|
$rules['first_name'] = 'required';
|
||||||
|
$rules['last_name'] = 'required';
|
||||||
|
}
|
||||||
|
if ($account->vat_number || $account->isNinjaAccount()) {
|
||||||
|
$rules['vat_number'] = 'required';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->validate($request, $rules);
|
||||||
|
|
||||||
|
$contact->fill(request()->all());
|
||||||
|
$contact->save();
|
||||||
|
|
||||||
|
$client->fill(request()->all());
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
event(new \App\Events\ClientWasUpdated($client));
|
||||||
|
|
||||||
|
return redirect($account->enable_client_portal_dashboard ? '/client/dashboard' : '/client/payment_methods')
|
||||||
|
->withMessage(trans('texts.updated_client_details'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
70
app/Http/Controllers/ClientPortalProposalController.php
Normal file
70
app/Http/Controllers/ClientPortalProposalController.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use mPDF;
|
||||||
|
use App\Models\Account;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\Invitation;
|
||||||
|
use App\Ninja\Repositories\ProposalRepository;
|
||||||
|
|
||||||
|
class ClientPortalProposalController extends BaseController
|
||||||
|
{
|
||||||
|
private $invoiceRepo;
|
||||||
|
private $paymentRepo;
|
||||||
|
private $documentRepo;
|
||||||
|
private $propoosalRepo;
|
||||||
|
|
||||||
|
public function __construct(ProposalRepository $propoosalRepo)
|
||||||
|
{
|
||||||
|
$this->propoosalRepo = $propoosalRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function viewProposal($invitationKey)
|
||||||
|
{
|
||||||
|
if (! $invitation = $this->propoosalRepo->findInvitationByKey($invitationKey)) {
|
||||||
|
return $this->returnError(trans('texts.proposal_not_found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = $invitation->account;
|
||||||
|
$proposal = $invitation->proposal;
|
||||||
|
$invoiceInvitation = Invitation::whereContactId($invitation->contact_id)
|
||||||
|
->whereInvoiceId($proposal->invoice_id)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'proposal' => $proposal,
|
||||||
|
'account' => $account,
|
||||||
|
'invoiceInvitation' => $invoiceInvitation,
|
||||||
|
'proposalInvitation' => $invitation,
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('invited.proposal', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadProposal($invitationKey)
|
||||||
|
{
|
||||||
|
if (! $invitation = $this->propoosalRepo->findInvitationByKey($invitationKey)) {
|
||||||
|
return $this->returnError(trans('texts.proposal_not_found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$proposal = $invitation->proposal;
|
||||||
|
|
||||||
|
$mpdf = new mPDF();
|
||||||
|
$mpdf->WriteHTML($proposal->present()->htmlDocument);
|
||||||
|
$mpdf->Output($proposal->present()->filename, 'D');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProposalImage($accountKey, $documentKey)
|
||||||
|
{
|
||||||
|
$account = Account::whereAccountKey($accountKey)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$document = Document::whereAccountId($account->id)
|
||||||
|
->whereDocumentKey($documentKey)
|
||||||
|
->whereIsProposal(true)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
return DocumentController::getDownloadResponse($document);
|
||||||
|
}
|
||||||
|
}
|
@ -43,12 +43,12 @@ class DashboardApiController extends BaseAPIController
|
|||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
'paidToDate' => count($paidToDate) && $paidToDate[0]->value ? $paidToDate[0]->value : 0,
|
'paidToDate' => $paidToDate->count() && $paidToDate[0]->value ? $paidToDate[0]->value : 0,
|
||||||
'paidToDateCurrency' => count($paidToDate) && $paidToDate[0]->currency_id ? $paidToDate[0]->currency_id : 0,
|
'paidToDateCurrency' => $paidToDate->count() && $paidToDate[0]->currency_id ? $paidToDate[0]->currency_id : 0,
|
||||||
'balances' => count($balances) && $balances[0]->value ? $balances[0]->value : 0,
|
'balances' => $balances->count() && $balances[0]->value ? $balances[0]->value : 0,
|
||||||
'balancesCurrency' => count($balances) && $balances[0]->currency_id ? $balances[0]->currency_id : 0,
|
'balancesCurrency' => $balances->count() && $balances[0]->currency_id ? $balances[0]->currency_id : 0,
|
||||||
'averageInvoice' => count($averageInvoice) && $averageInvoice[0]->invoice_avg ? $averageInvoice[0]->invoice_avg : 0,
|
'averageInvoice' => $averageInvoice->count() && $averageInvoice[0]->invoice_avg ? $averageInvoice[0]->invoice_avg : 0,
|
||||||
'averageInvoiceCurrency' => count($averageInvoice) && $averageInvoice[0]->currency_id ? $averageInvoice[0]->currency_id : 0,
|
'averageInvoiceCurrency' => $averageInvoice->count() && $averageInvoice[0]->currency_id ? $averageInvoice[0]->currency_id : 0,
|
||||||
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
|
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
|
||||||
'activeClients' => $metrics ? $metrics->active_clients : 0,
|
'activeClients' => $metrics ? $metrics->active_clients : 0,
|
||||||
'activities' => $this->createCollection($activities, new ActivityTransformer(), ENTITY_ACTIVITY),
|
'activities' => $this->createCollection($activities, new ActivityTransformer(), ENTITY_ACTIVITY),
|
||||||
|
@ -83,7 +83,7 @@ class DashboardController extends BaseController
|
|||||||
'tasks' => $tasks,
|
'tasks' => $tasks,
|
||||||
'showBlueVinePromo' => $showBlueVinePromo,
|
'showBlueVinePromo' => $showBlueVinePromo,
|
||||||
'showWhiteLabelExpired' => $showWhiteLabelExpired,
|
'showWhiteLabelExpired' => $showWhiteLabelExpired,
|
||||||
'showExpenses' => count($expenses) && $account->isModuleEnabled(ENTITY_EXPENSE),
|
'showExpenses' => $expenses->count() && $account->isModuleEnabled(ENTITY_EXPENSE),
|
||||||
'headerClass' => in_array(\App::getLocale(), ['lt', 'pl', 'cs', 'sl', 'tr_TR']) ? 'in-large' : 'in-thin',
|
'headerClass' => in_array(\App::getLocale(), ['lt', 'pl', 'cs', 'sl', 'tr_TR']) ? 'in-large' : 'in-thin',
|
||||||
'footerClass' => in_array(\App::getLocale(), ['lt', 'pl', 'cs', 'sl', 'tr_TR']) ? '' : 'in-thin',
|
'footerClass' => in_array(\App::getLocale(), ['lt', 'pl', 'cs', 'sl', 'tr_TR']) ? '' : 'in-thin',
|
||||||
];
|
];
|
||||||
|
@ -105,11 +105,20 @@ class DocumentController extends BaseController
|
|||||||
'code' => 400,
|
'code' => 400,
|
||||||
], 400);
|
], 400);
|
||||||
} else {
|
} else {
|
||||||
return Response::json([
|
if ($request->grapesjs) {
|
||||||
|
$response = [
|
||||||
|
'data' => [
|
||||||
|
$result->getProposalUrl()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$response = [
|
||||||
'error' => false,
|
'error' => false,
|
||||||
'document' => $doc_array,
|
'document' => $doc_array,
|
||||||
'code' => 200,
|
'code' => 200,
|
||||||
], 200);
|
];
|
||||||
|
}
|
||||||
|
return Response::json($response, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ class HomeController extends BaseController
|
|||||||
}
|
}
|
||||||
$subject .= '] ';
|
$subject .= '] ';
|
||||||
} else {
|
} else {
|
||||||
$subject .= 'Self-Host | ';
|
$subject .= 'Self-Host] | ';
|
||||||
}
|
}
|
||||||
$subject .= date('M jS, g:ia');
|
$subject .= date('M jS, g:ia');
|
||||||
$message->to(env('CONTACT_EMAIL', 'contact@invoiceninja.com'))
|
$message->to(env('CONTACT_EMAIL', 'contact@invoiceninja.com'))
|
||||||
|
@ -68,6 +68,11 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
$invoices->whereInvoiceNumber($invoiceNumber);
|
$invoices->whereInvoiceNumber($invoiceNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fllter by status
|
||||||
|
if ($statusId = Input::get('status_id')) {
|
||||||
|
$invoices->where('invoice_status_id', '>=', $statusId);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->listResponse($invoices);
|
return $this->listResponse($invoices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +403,11 @@ class InvoiceController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! Auth::user()->confirmed) {
|
if (! Auth::user()->confirmed) {
|
||||||
$errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required');
|
if (Auth::user()->registered) {
|
||||||
|
$errorMessage = trans('texts.confirmation_required', ['link' => link_to('/resend_confirmation', trans('texts.click_here'))]);
|
||||||
|
} else {
|
||||||
|
$errorMessage = trans('texts.registration_required');
|
||||||
|
}
|
||||||
Session::flash('error', $errorMessage);
|
Session::flash('error', $errorMessage);
|
||||||
|
|
||||||
return Redirect::to('invoices/'.$invoice->public_id.'/edit');
|
return Redirect::to('invoices/'.$invoice->public_id.'/edit');
|
||||||
|
@ -294,6 +294,7 @@ class NinjaController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
$account = $user->account;
|
||||||
$url = NINJA_APP_URL . '/buy_now';
|
$url = NINJA_APP_URL . '/buy_now';
|
||||||
$contactKey = $user->primaryAccount()->account_key;
|
$contactKey = $user->primaryAccount()->account_key;
|
||||||
|
|
||||||
@ -301,9 +302,17 @@ class NinjaController extends BaseController
|
|||||||
'account_key' => NINJA_LICENSE_ACCOUNT_KEY,
|
'account_key' => NINJA_LICENSE_ACCOUNT_KEY,
|
||||||
'contact_key' => $contactKey,
|
'contact_key' => $contactKey,
|
||||||
'product_id' => PRODUCT_WHITE_LABEL,
|
'product_id' => PRODUCT_WHITE_LABEL,
|
||||||
'first_name' => Auth::user()->first_name,
|
'first_name' => $user->first_name,
|
||||||
'last_name' => Auth::user()->last_name,
|
'last_name' => $user->last_name,
|
||||||
'email' => Auth::user()->email,
|
'email' => $user->email,
|
||||||
|
'name' => $account->name,
|
||||||
|
'address1' => $account->address1,
|
||||||
|
'address2' => $account->address2,
|
||||||
|
'city' => $account->city,
|
||||||
|
'state' => $account->state,
|
||||||
|
'postal_code' => $account->postal_code,
|
||||||
|
'country_id' => $account->country_id,
|
||||||
|
'vat_number' => $account->vat_number,
|
||||||
'return_link' => true,
|
'return_link' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -356,12 +356,14 @@ class OnlinePaymentController extends BaseController
|
|||||||
return redirect()->to("{$failureUrl}/?error=" . $validator->errors()->first());
|
return redirect()->to("{$failureUrl}/?error=" . $validator->errors()->first());
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = request()->all();
|
||||||
'currency_id' => $account->currency_id,
|
$data['currency_id'] = $account->currency_id;
|
||||||
'contact' => Input::all(),
|
$data['custom_value1'] = request()->custom_client1;
|
||||||
'custom_value1' => Input::get('custom_client1'),
|
$data['custom_value2'] = request()->custom_client2;
|
||||||
'custom_value2' => Input::get('custom_client2'),
|
$data['contact'] = request()->all();
|
||||||
];
|
$data['contact']['custom_value1'] = request()->custom_contact1;
|
||||||
|
$data['contact']['custom_value2'] = request()->custom_contact2;
|
||||||
|
|
||||||
if (request()->currency_code) {
|
if (request()->currency_code) {
|
||||||
$data['currency_code'] = request()->currency_code;
|
$data['currency_code'] = request()->currency_code;
|
||||||
}
|
}
|
||||||
@ -425,20 +427,23 @@ class OnlinePaymentController extends BaseController
|
|||||||
{
|
{
|
||||||
if (Utils::isNinja()) {
|
if (Utils::isNinja()) {
|
||||||
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
|
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
|
||||||
|
if (! $subdomain || $subdomain == 'app') {
|
||||||
|
exit('Invalid subdomain');
|
||||||
|
}
|
||||||
$account = Account::whereSubdomain($subdomain)->first();
|
$account = Account::whereSubdomain($subdomain)->first();
|
||||||
} else {
|
} else {
|
||||||
$account = Account::first();
|
$account = Account::first();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $account) {
|
if (! $account) {
|
||||||
exit("Account not found");
|
exit('Account not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$accountGateway = $account->account_gateways()
|
$accountGateway = $account->account_gateways()
|
||||||
->whereGatewayId(GATEWAY_STRIPE)->first();
|
->whereGatewayId(GATEWAY_STRIPE)->first();
|
||||||
|
|
||||||
if (! $account) {
|
if (! $account) {
|
||||||
exit("Apple merchant id not set");
|
exit('Apple merchant id not set');
|
||||||
}
|
}
|
||||||
|
|
||||||
echo $accountGateway->getConfigField('appleMerchantId');
|
echo $accountGateway->getConfigField('appleMerchantId');
|
||||||
|
@ -89,16 +89,29 @@ class PaymentController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function create(PaymentRequest $request)
|
public function create(PaymentRequest $request)
|
||||||
{
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
$account = $user->account;
|
||||||
|
|
||||||
$invoices = Invoice::scope()
|
$invoices = Invoice::scope()
|
||||||
->invoices()
|
->invoices()
|
||||||
->where('invoices.invoice_status_id', '!=', INVOICE_STATUS_PAID)
|
->where('invoices.invoice_status_id', '!=', INVOICE_STATUS_PAID)
|
||||||
->with('client', 'invoice_status')
|
->with('client', 'invoice_status')
|
||||||
->orderBy('invoice_number')->get();
|
->orderBy('invoice_number')->get();
|
||||||
|
|
||||||
|
$clientPublicId = Input::old('client') ? Input::old('client') : ($request->client_id ?: 0);
|
||||||
|
$invoicePublicId = Input::old('invoice') ? Input::old('invoice') : ($request->invoice_id ?: 0);
|
||||||
|
|
||||||
|
$totalCredit = false;
|
||||||
|
if ($clientPublicId && $client = Client::scope($clientPublicId)->first()) {
|
||||||
|
$totalCredit = $account->formatMoney($client->getTotalCredit(), $client);
|
||||||
|
} elseif ($invoicePublicId && $invoice = Invoice::scope($invoicePublicId)->first()) {
|
||||||
|
$totalCredit = $account->formatMoney($invoice->client->getTotalCredit(), $client);
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0),
|
'clientPublicId' => $clientPublicId,
|
||||||
'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : ($request->invoice_id ?: 0),
|
'invoicePublicId' => $invoicePublicId,
|
||||||
'invoice' => null,
|
'invoice' => null,
|
||||||
'invoices' => $invoices,
|
'invoices' => $invoices,
|
||||||
'payment' => null,
|
'payment' => null,
|
||||||
@ -106,7 +119,9 @@ class PaymentController extends BaseController
|
|||||||
'url' => 'payments',
|
'url' => 'payments',
|
||||||
'title' => trans('texts.new_payment'),
|
'title' => trans('texts.new_payment'),
|
||||||
'paymentTypeId' => Input::get('paymentTypeId'),
|
'paymentTypeId' => Input::get('paymentTypeId'),
|
||||||
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ];
|
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||||
|
'totalCredit' => $totalCredit,
|
||||||
|
];
|
||||||
|
|
||||||
return View::make('payments.edit', $data);
|
return View::make('payments.edit', $data);
|
||||||
}
|
}
|
||||||
|
160
app/Http/Controllers/PaymentTermApiController.php
Normal file
160
app/Http/Controllers/PaymentTermApiController.php
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\CreatePaymentTermAPIRequest;
|
||||||
|
use App\Http\Requests\PaymentTermRequest;
|
||||||
|
use App\Http\Requests\UpdatePaymentTermRequest;
|
||||||
|
use App\Libraries\Utils;
|
||||||
|
use App\Models\PaymentTerm;
|
||||||
|
use App\Ninja\Repositories\PaymentTermRepository;
|
||||||
|
use Illuminate\Support\Facades\Input;
|
||||||
|
|
||||||
|
class PaymentTermApiController extends BaseAPIController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var PaymentTermRepository
|
||||||
|
*/
|
||||||
|
protected $paymentTermRepo;
|
||||||
|
protected $entityType = ENTITY_PAYMENT_TERM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PaymentTermApiController constructor.
|
||||||
|
*
|
||||||
|
* @param PaymentTermRepository $paymentTermRepo
|
||||||
|
*/
|
||||||
|
public function __construct(PaymentTermRepository $paymentTermRepo)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->paymentTermRepo = $paymentTermRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SWG\Get(
|
||||||
|
* path="/paymentTerms",
|
||||||
|
* summary="List payment terms",
|
||||||
|
* operationId="listPaymentTerms",
|
||||||
|
* tags={"payment terms"},
|
||||||
|
* @SWG\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="A list of payment terms",
|
||||||
|
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/PaymentTerms"))
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="an ""unexpected"" error"
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
|
||||||
|
$paymentTerms = PaymentTerm::scope()
|
||||||
|
->orWhere('account_id',0)
|
||||||
|
->orderBy('num_days', 'asc');
|
||||||
|
|
||||||
|
return $this->listResponse($paymentTerms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SWG\Get(
|
||||||
|
* path="/paymentTerms/{payment_term_id}",
|
||||||
|
* summary="Retrieve a payment term",
|
||||||
|
* operationId="getPaymentTermId",
|
||||||
|
* tags={"payment term"},
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* in="path",
|
||||||
|
* name="payment_term_id",
|
||||||
|
* type="integer",
|
||||||
|
* required=true
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="A single payment term",
|
||||||
|
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/PaymentTerms"))
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="an ""unexpected"" error"
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function show(PaymentTermRequest $request)
|
||||||
|
{
|
||||||
|
return $this->itemResponse($request->entity());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SWG\Post(
|
||||||
|
* path="/paymentTerms",
|
||||||
|
* summary="Create a payment Term",
|
||||||
|
* operationId="createPaymentTerm",
|
||||||
|
* tags={"payment term"},
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* in="body",
|
||||||
|
* name="payment term",
|
||||||
|
* @SWG\Schema(ref="#/definitions/PaymentTerm")
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="New payment Term",
|
||||||
|
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/PaymentTerm"))
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="an ""unexpected"" error"
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function store(CreatePaymentTermAPIRequest $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$paymentTerm = PaymentTerm::createNew();
|
||||||
|
|
||||||
|
$paymentTerm->num_days = Utils::parseInt(Input::get('num_days'));
|
||||||
|
$paymentTerm->name = 'Net ' . $paymentTerm->num_days;
|
||||||
|
$paymentTerm->save();
|
||||||
|
|
||||||
|
return $this->itemResponse($paymentTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SWG\Delete(
|
||||||
|
* path="/paymentTerm/{num_days}",
|
||||||
|
* summary="Delete a payment term",
|
||||||
|
* operationId="deletePaymentTerm",
|
||||||
|
* tags={"payment term"},
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* in="path",
|
||||||
|
* name="num_days",
|
||||||
|
* type="integer",
|
||||||
|
* required=true
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="Deleted payment Term",
|
||||||
|
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/PaymentTerm"))
|
||||||
|
* ),
|
||||||
|
* @SWG\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="an ""unexpected"" error"
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function destroy($numDays)
|
||||||
|
{
|
||||||
|
|
||||||
|
$paymentTerm = PaymentTerm::where('num_days', $numDays)->first();
|
||||||
|
|
||||||
|
if(!$paymentTerm || $paymentTerm->account_id == 0)
|
||||||
|
return $this->errorResponse(['message'=>'Cannot delete a default or non existent Payment Term'], 400);
|
||||||
|
|
||||||
|
$this->paymentTermRepo->archive($paymentTerm);
|
||||||
|
|
||||||
|
return $this->itemResponse($paymentTerm);
|
||||||
|
}
|
||||||
|
}
|
128
app/Http/Controllers/ProposalCategoryController.php
Normal file
128
app/Http/Controllers/ProposalCategoryController.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\CreateProposalCategoryRequest;
|
||||||
|
use App\Http\Requests\ProposalCategoryRequest;
|
||||||
|
use App\Http\Requests\UpdateProposalCategoryRequest;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\ProposalCategory;
|
||||||
|
use App\Ninja\Datatables\ProposalCategoryDatatable;
|
||||||
|
use App\Ninja\Repositories\ProposalCategoryRepository;
|
||||||
|
use App\Services\ProposalCategoryService;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Session;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
class ProposalCategoryController extends BaseController
|
||||||
|
{
|
||||||
|
protected $proposalCategoryRepo;
|
||||||
|
protected $proposalCategoryService;
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
|
||||||
|
public function __construct(ProposalCategoryRepository $proposalCategoryRepo, ProposalCategoryService $proposalCategoryService)
|
||||||
|
{
|
||||||
|
$this->proposalCategoryRepo = $proposalCategoryRepo;
|
||||||
|
$this->proposalCategoryService = $proposalCategoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return View::make('list_wrapper', [
|
||||||
|
'entityType' => ENTITY_PROPOSAL_CATEGORY,
|
||||||
|
'datatable' => new ProposalCategoryDatatable(),
|
||||||
|
'title' => trans('texts.proposal_categories'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatatable($expensePublicId = null)
|
||||||
|
{
|
||||||
|
$search = Input::get('sSearch');
|
||||||
|
$userId = Auth::user()->filterId();
|
||||||
|
|
||||||
|
return $this->proposalCategoryService->getDatatable($search, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(ProposalCategoryRequest $request)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'category' => null,
|
||||||
|
'method' => 'POST',
|
||||||
|
'url' => 'proposals/categories',
|
||||||
|
'title' => trans('texts.new_proposal_category'),
|
||||||
|
'quotes' => Invoice::scope()->with('client.contacts')->quotes()->orderBy('id')->get(),
|
||||||
|
'templates' => ProposalCategory::scope()->orderBy('name')->get(),
|
||||||
|
'quotePublicId' => $request->quote_id,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals/categories.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($publicId)
|
||||||
|
{
|
||||||
|
Session::reflash();
|
||||||
|
|
||||||
|
return redirect("proposals/categories/$publicId/edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(ProposalCategoryRequest $request)
|
||||||
|
{
|
||||||
|
$proposalCategory = $request->entity();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'category' => $proposalCategory,
|
||||||
|
'method' => 'PUT',
|
||||||
|
'url' => 'proposals/categories/' . $proposalCategory->public_id,
|
||||||
|
'title' => trans('texts.edit_proposal_category'),
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals/categories.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(CreateProposalCategoryRequest $request)
|
||||||
|
{
|
||||||
|
$proposalCategory = $this->proposalCategoryService->save($request->input());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.created_proposal_category'));
|
||||||
|
|
||||||
|
return redirect()->to($proposalCategory->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProposalCategoryRequest $request)
|
||||||
|
{
|
||||||
|
$proposalCategory = $this->proposalCategoryService->save($request->input(), $request->entity());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.updated_proposal_category'));
|
||||||
|
|
||||||
|
$action = Input::get('action');
|
||||||
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposalCategory->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bulk()
|
||||||
|
{
|
||||||
|
$action = Input::get('action');
|
||||||
|
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||||
|
|
||||||
|
$count = $this->proposalCategoryService->bulk($ids, $action);
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$field = $count == 1 ? "{$action}d_proposal_category" : "{$action}d_proposal_categories";
|
||||||
|
$message = trans("texts.$field", ['count' => $count]);
|
||||||
|
Session::flash('message', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('/proposals/categories');
|
||||||
|
}
|
||||||
|
}
|
178
app/Http/Controllers/ProposalController.php
Normal file
178
app/Http/Controllers/ProposalController.php
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\CreateProposalRequest;
|
||||||
|
use App\Http\Requests\ProposalRequest;
|
||||||
|
use App\Http\Requests\UpdateProposalRequest;
|
||||||
|
use App\Jobs\SendInvoiceEmail;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Proposal;
|
||||||
|
use App\Models\ProposalTemplate;
|
||||||
|
use App\Ninja\Mailers\ContactMailer;
|
||||||
|
use App\Ninja\Datatables\ProposalDatatable;
|
||||||
|
use App\Ninja\Repositories\ProposalRepository;
|
||||||
|
use App\Services\ProposalService;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Session;
|
||||||
|
use View;
|
||||||
|
use mPDF;
|
||||||
|
|
||||||
|
class ProposalController extends BaseController
|
||||||
|
{
|
||||||
|
protected $proposalRepo;
|
||||||
|
protected $proposalService;
|
||||||
|
protected $contactMailer;
|
||||||
|
|
||||||
|
protected $entityType = ENTITY_PROPOSAL;
|
||||||
|
|
||||||
|
public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService, ContactMailer $contactMailer)
|
||||||
|
{
|
||||||
|
$this->proposalRepo = $proposalRepo;
|
||||||
|
$this->proposalService = $proposalService;
|
||||||
|
$this->contactMailer = $contactMailer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return View::make('list_wrapper', [
|
||||||
|
'entityType' => ENTITY_PROPOSAL,
|
||||||
|
'datatable' => new ProposalDatatable(),
|
||||||
|
'title' => trans('texts.proposals'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatatable($expensePublicId = null)
|
||||||
|
{
|
||||||
|
$search = Input::get('sSearch');
|
||||||
|
$userId = Auth::user()->filterId();
|
||||||
|
|
||||||
|
return $this->proposalService->getDatatable($search, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$data = array_merge($this->getViewmodel(), [
|
||||||
|
'proposal' => null,
|
||||||
|
'method' => 'POST',
|
||||||
|
'url' => 'proposals',
|
||||||
|
'title' => trans('texts.new_proposal'),
|
||||||
|
'invoices' => Invoice::scope()->with('client.contacts', 'client.country')->unapprovedQuotes()->orderBy('id')->get(),
|
||||||
|
'invoicePublicId' => $request->invoice_id,
|
||||||
|
'templatePublicId' => $request->proposal_template_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return View::make('proposals.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($publicId)
|
||||||
|
{
|
||||||
|
Session::reflash();
|
||||||
|
|
||||||
|
return redirect("proposals/$publicId/edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $request->entity();
|
||||||
|
|
||||||
|
$data = array_merge($this->getViewmodel(), [
|
||||||
|
'proposal' => $proposal,
|
||||||
|
'entity' => $proposal,
|
||||||
|
'method' => 'PUT',
|
||||||
|
'url' => 'proposals/' . $proposal->public_id,
|
||||||
|
'title' => trans('texts.edit_proposal'),
|
||||||
|
'invoices' => Invoice::scope()->with('client.contacts', 'client.country')->unapprovedQuotes($proposal->invoice_id)->orderBy('id')->get(),
|
||||||
|
'invoicePublicId' => $proposal->invoice ? $proposal->invoice->public_id : null,
|
||||||
|
'templatePublicId' => $proposal->proposal_template ? $proposal->proposal_template->public_id : null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return View::make('proposals.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getViewmodel()
|
||||||
|
{
|
||||||
|
$account = auth()->user()->account;
|
||||||
|
$templates = ProposalTemplate::whereAccountId($account->id)->orderBy('name')->get();
|
||||||
|
|
||||||
|
if (! $templates->count()) {
|
||||||
|
$templates = ProposalTemplate::whereNull('account_id')->orderBy('name')->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'templates' => $templates,
|
||||||
|
'account' => $account,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(CreateProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $this->proposalService->save($request->input());
|
||||||
|
$action = Input::get('action');
|
||||||
|
|
||||||
|
if ($action == 'email') {
|
||||||
|
$this->dispatch(new SendInvoiceEmail($proposal->invoice, auth()->user()->id, false, false, $proposal));
|
||||||
|
Session::flash('message', trans('texts.emailed_proposal'));
|
||||||
|
} else {
|
||||||
|
Session::flash('message', trans('texts.created_proposal'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposal->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $this->proposalService->save($request->input(), $request->entity());
|
||||||
|
$action = Input::get('action');
|
||||||
|
|
||||||
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action == 'email') {
|
||||||
|
$this->dispatch(new SendInvoiceEmail($proposal->invoice, auth()->user()->id, false, false, $proposal));
|
||||||
|
Session::flash('message', trans('texts.emailed_proposal'));
|
||||||
|
} else {
|
||||||
|
Session::flash('message', trans('texts.updated_proposal'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposal->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bulk()
|
||||||
|
{
|
||||||
|
$action = Input::get('bulk_action') ?: Input::get('action');
|
||||||
|
$ids = Input::get('bulk_public_id') ?: (Input::get('public_id') ?: Input::get('ids'));
|
||||||
|
|
||||||
|
$count = $this->proposalService->bulk($ids, $action);
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$field = $count == 1 ? "{$action}d_proposal" : "{$action}d_proposals";
|
||||||
|
$message = trans("texts.$field", ['count' => $count]);
|
||||||
|
Session::flash('message', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('/proposals');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function download(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $request->entity();
|
||||||
|
|
||||||
|
$mpdf = new mPDF();
|
||||||
|
$mpdf->showImageErrors = true;
|
||||||
|
$mpdf->WriteHTML($proposal->present()->htmlDocument);
|
||||||
|
|
||||||
|
//$mpdf->Output();
|
||||||
|
|
||||||
|
$mpdf->Output($proposal->present()->filename, 'D');
|
||||||
|
}
|
||||||
|
}
|
732
app/Http/Controllers/ProposalSnippetController.php
Normal file
732
app/Http/Controllers/ProposalSnippetController.php
Normal file
@ -0,0 +1,732 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\CreateProposalSnippetRequest;
|
||||||
|
use App\Http\Requests\ProposalSnippetRequest;
|
||||||
|
use App\Http\Requests\UpdateProposalSnippetRequest;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\ProposalSnippet;
|
||||||
|
use App\Models\ProposalCategory;
|
||||||
|
use App\Ninja\Datatables\ProposalSnippetDatatable;
|
||||||
|
use App\Ninja\Repositories\ProposalSnippetRepository;
|
||||||
|
use App\Services\ProposalSnippetService;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Session;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
class ProposalSnippetController extends BaseController
|
||||||
|
{
|
||||||
|
protected $proposalSnippetRepo;
|
||||||
|
protected $proposalSnippetService;
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
|
||||||
|
public function __construct(ProposalSnippetRepository $proposalSnippetRepo, ProposalSnippetService $proposalSnippetService)
|
||||||
|
{
|
||||||
|
$this->proposalSnippetRepo = $proposalSnippetRepo;
|
||||||
|
$this->proposalSnippetService = $proposalSnippetService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return View::make('list_wrapper', [
|
||||||
|
'entityType' => ENTITY_PROPOSAL_SNIPPET,
|
||||||
|
'datatable' => new ProposalSnippetDatatable(),
|
||||||
|
'title' => trans('texts.proposal_snippets'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatatable($expensePublicId = null)
|
||||||
|
{
|
||||||
|
$search = Input::get('sSearch');
|
||||||
|
$userId = Auth::user()->filterId();
|
||||||
|
|
||||||
|
return $this->proposalSnippetService->getDatatable($search, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(ProposalSnippetRequest $request)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'snippet' => null,
|
||||||
|
'method' => 'POST',
|
||||||
|
'url' => 'proposals/snippets',
|
||||||
|
'title' => trans('texts.new_proposal_snippet'),
|
||||||
|
'categories' => ProposalCategory::scope()->orderBy('name')->get(),
|
||||||
|
'categoryPublicId' => 0,
|
||||||
|
'icons' => $this->getIcons(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals/snippets/edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($publicId)
|
||||||
|
{
|
||||||
|
Session::reflash();
|
||||||
|
|
||||||
|
return redirect("proposals/snippets/$publicId/edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(ProposalSnippetRequest $request)
|
||||||
|
{
|
||||||
|
$proposalSnippet = $request->entity();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'snippet' => $proposalSnippet,
|
||||||
|
'entity' => $proposalSnippet,
|
||||||
|
'method' => 'PUT',
|
||||||
|
'url' => 'proposals/snippets/' . $proposalSnippet->public_id,
|
||||||
|
'title' => trans('texts.edit_proposal_snippet'),
|
||||||
|
'categories' => ProposalCategory::scope()->orderBy('name')->get(),
|
||||||
|
'categoryPublicId' => $proposalSnippet->proposal_category ? $proposalSnippet->proposal_category->public_id : null,
|
||||||
|
'icons' => $this->getIcons(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals/snippets.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(CreateProposalSnippetRequest $request)
|
||||||
|
{
|
||||||
|
$proposalSnippet = $this->proposalSnippetService->save($request->input());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.created_proposal_snippet'));
|
||||||
|
|
||||||
|
return redirect()->to($proposalSnippet->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProposalSnippetRequest $request)
|
||||||
|
{
|
||||||
|
$proposalSnippet = $this->proposalSnippetService->save($request->input(), $request->entity());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.updated_proposal_snippet'));
|
||||||
|
|
||||||
|
$action = Input::get('action');
|
||||||
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposalSnippet->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bulk()
|
||||||
|
{
|
||||||
|
$action = Input::get('action');
|
||||||
|
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||||
|
|
||||||
|
$count = $this->proposalSnippetService->bulk($ids, $action);
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$field = $count == 1 ? "{$action}d_proposal_snippet" : "{$action}d_proposal_snippets";
|
||||||
|
$message = trans("texts.$field", ['count' => $count]);
|
||||||
|
Session::flash('message', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('/proposals/snippets');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getIcons() {
|
||||||
|
$data = [];
|
||||||
|
$icons = [
|
||||||
|
['name'=>'glass','code'=>'f000'],
|
||||||
|
['name'=>'music','code'=>'f001'],
|
||||||
|
['name'=>'search','code'=>'f002'],
|
||||||
|
['name'=>'envelope-o','code'=>'f003'],
|
||||||
|
['name'=>'heart','code'=>'f004'],
|
||||||
|
['name'=>'star','code'=>'f005'],
|
||||||
|
['name'=>'star-o','code'=>'f006'],
|
||||||
|
['name'=>'user','code'=>'f007'],
|
||||||
|
['name'=>'film','code'=>'f008'],
|
||||||
|
['name'=>'th-large','code'=>'f009'],
|
||||||
|
['name'=>'th','code'=>'f00a'],
|
||||||
|
['name'=>'th-list','code'=>'f00b'],
|
||||||
|
['name'=>'check','code'=>'f00c'],
|
||||||
|
['name'=>'times','code'=>'f00d'],
|
||||||
|
['name'=>'search-plus','code'=>'f00e'],
|
||||||
|
['name'=>'search-minus','code'=>'f010'],
|
||||||
|
['name'=>'power-off','code'=>'f011'],
|
||||||
|
['name'=>'signal','code'=>'f012'],
|
||||||
|
['name'=>'cog','code'=>'f013'],
|
||||||
|
['name'=>'trash-o','code'=>'f014'],
|
||||||
|
['name'=>'home','code'=>'f015'],
|
||||||
|
['name'=>'file-o','code'=>'f016'],
|
||||||
|
['name'=>'clock-o','code'=>'f017'],
|
||||||
|
['name'=>'road','code'=>'f018'],
|
||||||
|
['name'=>'download','code'=>'f019'],
|
||||||
|
['name'=>'arrow-circle-o-down','code'=>'f01a'],
|
||||||
|
['name'=>'arrow-circle-o-up','code'=>'f01b'],
|
||||||
|
['name'=>'inbox','code'=>'f01c'],
|
||||||
|
['name'=>'play-circle-o','code'=>'f01d'],
|
||||||
|
['name'=>'repeat','code'=>'f01e'],
|
||||||
|
['name'=>'refresh','code'=>'f021'],
|
||||||
|
['name'=>'list-alt','code'=>'f022'],
|
||||||
|
['name'=>'lock','code'=>'f023'],
|
||||||
|
['name'=>'flag','code'=>'f024'],
|
||||||
|
['name'=>'headphones','code'=>'f025'],
|
||||||
|
['name'=>'volume-off','code'=>'f026'],
|
||||||
|
['name'=>'volume-down','code'=>'f027'],
|
||||||
|
['name'=>'volume-up','code'=>'f028'],
|
||||||
|
['name'=>'qrcode','code'=>'f029'],
|
||||||
|
['name'=>'barcode','code'=>'f02a'],
|
||||||
|
['name'=>'tag','code'=>'f02b'],
|
||||||
|
['name'=>'tags','code'=>'f02c'],
|
||||||
|
['name'=>'book','code'=>'f02d'],
|
||||||
|
['name'=>'bookmark','code'=>'f02e'],
|
||||||
|
['name'=>'print','code'=>'f02f'],
|
||||||
|
['name'=>'camera','code'=>'f030'],
|
||||||
|
['name'=>'font','code'=>'f031'],
|
||||||
|
['name'=>'bold','code'=>'f032'],
|
||||||
|
['name'=>'italic','code'=>'f033'],
|
||||||
|
['name'=>'text-height','code'=>'f034'],
|
||||||
|
['name'=>'text-width','code'=>'f035'],
|
||||||
|
['name'=>'align-left','code'=>'f036'],
|
||||||
|
['name'=>'align-center','code'=>'f037'],
|
||||||
|
['name'=>'align-right','code'=>'f038'],
|
||||||
|
['name'=>'align-justify','code'=>'f039'],
|
||||||
|
['name'=>'list','code'=>'f03a'],
|
||||||
|
['name'=>'outdent','code'=>'f03b'],
|
||||||
|
['name'=>'indent','code'=>'f03c'],
|
||||||
|
['name'=>'video-camera','code'=>'f03d'],
|
||||||
|
['name'=>'picture-o','code'=>'f03e'],
|
||||||
|
['name'=>'pencil','code'=>'f040'],
|
||||||
|
['name'=>'map-marker','code'=>'f041'],
|
||||||
|
['name'=>'adjust','code'=>'f042'],
|
||||||
|
['name'=>'tint','code'=>'f043'],
|
||||||
|
['name'=>'pencil-square-o','code'=>'f044'],
|
||||||
|
['name'=>'share-square-o','code'=>'f045'],
|
||||||
|
['name'=>'check-square-o','code'=>'f046'],
|
||||||
|
['name'=>'arrows','code'=>'f047'],
|
||||||
|
['name'=>'step-backward','code'=>'f048'],
|
||||||
|
['name'=>'fast-backward','code'=>'f049'],
|
||||||
|
['name'=>'backward','code'=>'f04a'],
|
||||||
|
['name'=>'play','code'=>'f04b'],
|
||||||
|
['name'=>'pause','code'=>'f04c'],
|
||||||
|
['name'=>'stop','code'=>'f04d'],
|
||||||
|
['name'=>'forward','code'=>'f04e'],
|
||||||
|
['name'=>'fast-forward','code'=>'f050'],
|
||||||
|
['name'=>'step-forward','code'=>'f051'],
|
||||||
|
['name'=>'eject','code'=>'f052'],
|
||||||
|
['name'=>'chevron-left','code'=>'f053'],
|
||||||
|
['name'=>'chevron-right','code'=>'f054'],
|
||||||
|
['name'=>'plus-circle','code'=>'f055'],
|
||||||
|
['name'=>'minus-circle','code'=>'f056'],
|
||||||
|
['name'=>'times-circle','code'=>'f057'],
|
||||||
|
['name'=>'check-circle','code'=>'f058'],
|
||||||
|
['name'=>'question-circle','code'=>'f059'],
|
||||||
|
['name'=>'info-circle','code'=>'f05a'],
|
||||||
|
['name'=>'crosshairs','code'=>'f05b'],
|
||||||
|
['name'=>'times-circle-o','code'=>'f05c'],
|
||||||
|
['name'=>'check-circle-o','code'=>'f05d'],
|
||||||
|
['name'=>'ban','code'=>'f05e'],
|
||||||
|
['name'=>'arrow-left','code'=>'f060'],
|
||||||
|
['name'=>'arrow-right','code'=>'f061'],
|
||||||
|
['name'=>'arrow-up','code'=>'f062'],
|
||||||
|
['name'=>'arrow-down','code'=>'f063'],
|
||||||
|
['name'=>'share','code'=>'f064'],
|
||||||
|
['name'=>'expand','code'=>'f065'],
|
||||||
|
['name'=>'compress','code'=>'f066'],
|
||||||
|
['name'=>'plus','code'=>'f067'],
|
||||||
|
['name'=>'minus','code'=>'f068'],
|
||||||
|
['name'=>'asterisk','code'=>'f069'],
|
||||||
|
['name'=>'exclamation-circle','code'=>'f06a'],
|
||||||
|
['name'=>'gift','code'=>'f06b'],
|
||||||
|
['name'=>'leaf','code'=>'f06c'],
|
||||||
|
['name'=>'fire','code'=>'f06d'],
|
||||||
|
['name'=>'eye','code'=>'f06e'],
|
||||||
|
['name'=>'eye-slash','code'=>'f070'],
|
||||||
|
['name'=>'exclamation-triangle','code'=>'f071'],
|
||||||
|
['name'=>'plane','code'=>'f072'],
|
||||||
|
['name'=>'calendar','code'=>'f073'],
|
||||||
|
['name'=>'random','code'=>'f074'],
|
||||||
|
['name'=>'comment','code'=>'f075'],
|
||||||
|
['name'=>'magnet','code'=>'f076'],
|
||||||
|
['name'=>'chevron-up','code'=>'f077'],
|
||||||
|
['name'=>'chevron-down','code'=>'f078'],
|
||||||
|
['name'=>'retweet','code'=>'f079'],
|
||||||
|
['name'=>'shopping-cart','code'=>'f07a'],
|
||||||
|
['name'=>'folder','code'=>'f07b'],
|
||||||
|
['name'=>'folder-open','code'=>'f07c'],
|
||||||
|
['name'=>'arrows-v','code'=>'f07d'],
|
||||||
|
['name'=>'arrows-h','code'=>'f07e'],
|
||||||
|
['name'=>'bar-chart','code'=>'f080'],
|
||||||
|
['name'=>'twitter-square','code'=>'f081'],
|
||||||
|
['name'=>'facebook-square','code'=>'f082'],
|
||||||
|
['name'=>'camera-retro','code'=>'f083'],
|
||||||
|
['name'=>'key','code'=>'f084'],
|
||||||
|
['name'=>'cogs','code'=>'f085'],
|
||||||
|
['name'=>'comments','code'=>'f086'],
|
||||||
|
['name'=>'thumbs-o-up','code'=>'f087'],
|
||||||
|
['name'=>'thumbs-o-down','code'=>'f088'],
|
||||||
|
['name'=>'star-half','code'=>'f089'],
|
||||||
|
['name'=>'heart-o','code'=>'f08a'],
|
||||||
|
['name'=>'sign-out','code'=>'f08b'],
|
||||||
|
['name'=>'linkedin-square','code'=>'f08c'],
|
||||||
|
['name'=>'thumb-tack','code'=>'f08d'],
|
||||||
|
['name'=>'external-link','code'=>'f08e'],
|
||||||
|
['name'=>'sign-in','code'=>'f090'],
|
||||||
|
['name'=>'trophy','code'=>'f091'],
|
||||||
|
['name'=>'github-square','code'=>'f092'],
|
||||||
|
['name'=>'upload','code'=>'f093'],
|
||||||
|
['name'=>'lemon-o','code'=>'f094'],
|
||||||
|
['name'=>'phone','code'=>'f095'],
|
||||||
|
['name'=>'square-o','code'=>'f096'],
|
||||||
|
['name'=>'bookmark-o','code'=>'f097'],
|
||||||
|
['name'=>'phone-square','code'=>'f098'],
|
||||||
|
['name'=>'twitter','code'=>'f099'],
|
||||||
|
['name'=>'facebook','code'=>'f09a'],
|
||||||
|
['name'=>'github','code'=>'f09b'],
|
||||||
|
['name'=>'unlock','code'=>'f09c'],
|
||||||
|
['name'=>'credit-card','code'=>'f09d'],
|
||||||
|
['name'=>'rss','code'=>'f09e'],
|
||||||
|
['name'=>'hdd-o','code'=>'f0a0'],
|
||||||
|
['name'=>'bullhorn','code'=>'f0a1'],
|
||||||
|
['name'=>'bell','code'=>'f0f3'],
|
||||||
|
['name'=>'certificate','code'=>'f0a3'],
|
||||||
|
['name'=>'hand-o-right','code'=>'f0a4'],
|
||||||
|
['name'=>'hand-o-left','code'=>'f0a5'],
|
||||||
|
['name'=>'hand-o-up','code'=>'f0a6'],
|
||||||
|
['name'=>'hand-o-down','code'=>'f0a7'],
|
||||||
|
['name'=>'arrow-circle-left','code'=>'f0a8'],
|
||||||
|
['name'=>'arrow-circle-right','code'=>'f0a9'],
|
||||||
|
['name'=>'arrow-circle-up','code'=>'f0aa'],
|
||||||
|
['name'=>'arrow-circle-down','code'=>'f0ab'],
|
||||||
|
['name'=>'globe','code'=>'f0ac'],
|
||||||
|
['name'=>'wrench','code'=>'f0ad'],
|
||||||
|
['name'=>'tasks','code'=>'f0ae'],
|
||||||
|
['name'=>'filter','code'=>'f0b0'],
|
||||||
|
['name'=>'briefcase','code'=>'f0b1'],
|
||||||
|
['name'=>'arrows-alt','code'=>'f0b2'],
|
||||||
|
['name'=>'users','code'=>'f0c0'],
|
||||||
|
['name'=>'link','code'=>'f0c1'],
|
||||||
|
['name'=>'cloud','code'=>'f0c2'],
|
||||||
|
['name'=>'flask','code'=>'f0c3'],
|
||||||
|
['name'=>'scissors','code'=>'f0c4'],
|
||||||
|
['name'=>'files-o','code'=>'f0c5'],
|
||||||
|
['name'=>'paperclip','code'=>'f0c6'],
|
||||||
|
['name'=>'floppy-o','code'=>'f0c7'],
|
||||||
|
['name'=>'square','code'=>'f0c8'],
|
||||||
|
['name'=>'bars','code'=>'f0c9'],
|
||||||
|
['name'=>'list-ul','code'=>'f0ca'],
|
||||||
|
['name'=>'list-ol','code'=>'f0cb'],
|
||||||
|
['name'=>'strikethrough','code'=>'f0cc'],
|
||||||
|
['name'=>'underline','code'=>'f0cd'],
|
||||||
|
['name'=>'table','code'=>'f0ce'],
|
||||||
|
['name'=>'magic','code'=>'f0d0'],
|
||||||
|
['name'=>'truck','code'=>'f0d1'],
|
||||||
|
['name'=>'pinterest','code'=>'f0d2'],
|
||||||
|
['name'=>'pinterest-square','code'=>'f0d3'],
|
||||||
|
['name'=>'google-plus-square','code'=>'f0d4'],
|
||||||
|
['name'=>'google-plus','code'=>'f0d5'],
|
||||||
|
['name'=>'money','code'=>'f0d6'],
|
||||||
|
['name'=>'caret-down','code'=>'f0d7'],
|
||||||
|
['name'=>'caret-up','code'=>'f0d8'],
|
||||||
|
['name'=>'caret-left','code'=>'f0d9'],
|
||||||
|
['name'=>'caret-right','code'=>'f0da'],
|
||||||
|
['name'=>'columns','code'=>'f0db'],
|
||||||
|
['name'=>'sort','code'=>'f0dc'],
|
||||||
|
['name'=>'sort-desc','code'=>'f0dd'],
|
||||||
|
['name'=>'sort-asc','code'=>'f0de'],
|
||||||
|
['name'=>'envelope','code'=>'f0e0'],
|
||||||
|
['name'=>'linkedin','code'=>'f0e1'],
|
||||||
|
['name'=>'undo','code'=>'f0e2'],
|
||||||
|
['name'=>'gavel','code'=>'f0e3'],
|
||||||
|
['name'=>'tachometer','code'=>'f0e4'],
|
||||||
|
['name'=>'comment-o','code'=>'f0e5'],
|
||||||
|
['name'=>'comments-o','code'=>'f0e6'],
|
||||||
|
['name'=>'bolt','code'=>'f0e7'],
|
||||||
|
['name'=>'sitemap','code'=>'f0e8'],
|
||||||
|
['name'=>'umbrella','code'=>'f0e9'],
|
||||||
|
['name'=>'clipboard','code'=>'f0ea'],
|
||||||
|
['name'=>'lightbulb-o','code'=>'f0eb'],
|
||||||
|
['name'=>'exchange','code'=>'f0ec'],
|
||||||
|
['name'=>'cloud-download','code'=>'f0ed'],
|
||||||
|
['name'=>'cloud-upload','code'=>'f0ee'],
|
||||||
|
['name'=>'user-md','code'=>'f0f0'],
|
||||||
|
['name'=>'stethoscope','code'=>'f0f1'],
|
||||||
|
['name'=>'suitcase','code'=>'f0f2'],
|
||||||
|
['name'=>'bell-o','code'=>'f0a2'],
|
||||||
|
['name'=>'coffee','code'=>'f0f4'],
|
||||||
|
['name'=>'cutlery','code'=>'f0f5'],
|
||||||
|
['name'=>'file-text-o','code'=>'f0f6'],
|
||||||
|
['name'=>'building-o','code'=>'f0f7'],
|
||||||
|
['name'=>'hospital-o','code'=>'f0f8'],
|
||||||
|
['name'=>'ambulance','code'=>'f0f9'],
|
||||||
|
['name'=>'medkit','code'=>'f0fa'],
|
||||||
|
['name'=>'fighter-jet','code'=>'f0fb'],
|
||||||
|
['name'=>'beer','code'=>'f0fc'],
|
||||||
|
['name'=>'h-square','code'=>'f0fd'],
|
||||||
|
['name'=>'plus-square','code'=>'f0fe'],
|
||||||
|
['name'=>'angle-double-left','code'=>'f100'],
|
||||||
|
['name'=>'angle-double-right','code'=>'f101'],
|
||||||
|
['name'=>'angle-double-up','code'=>'f102'],
|
||||||
|
['name'=>'angle-double-down','code'=>'f103'],
|
||||||
|
['name'=>'angle-left','code'=>'f104'],
|
||||||
|
['name'=>'angle-right','code'=>'f105'],
|
||||||
|
['name'=>'angle-up','code'=>'f106'],
|
||||||
|
['name'=>'angle-down','code'=>'f107'],
|
||||||
|
['name'=>'desktop','code'=>'f108'],
|
||||||
|
['name'=>'laptop','code'=>'f109'],
|
||||||
|
['name'=>'tablet','code'=>'f10a'],
|
||||||
|
['name'=>'mobile','code'=>'f10b'],
|
||||||
|
['name'=>'circle-o','code'=>'f10c'],
|
||||||
|
['name'=>'quote-left','code'=>'f10d'],
|
||||||
|
['name'=>'quote-right','code'=>'f10e'],
|
||||||
|
['name'=>'spinner','code'=>'f110'],
|
||||||
|
['name'=>'circle','code'=>'f111'],
|
||||||
|
['name'=>'reply','code'=>'f112'],
|
||||||
|
['name'=>'github-alt','code'=>'f113'],
|
||||||
|
['name'=>'folder-o','code'=>'f114'],
|
||||||
|
['name'=>'folder-open-o','code'=>'f115'],
|
||||||
|
['name'=>'smile-o','code'=>'f118'],
|
||||||
|
['name'=>'frown-o','code'=>'f119'],
|
||||||
|
['name'=>'meh-o','code'=>'f11a'],
|
||||||
|
['name'=>'gamepad','code'=>'f11b'],
|
||||||
|
['name'=>'keyboard-o','code'=>'f11c'],
|
||||||
|
['name'=>'flag-o','code'=>'f11d'],
|
||||||
|
['name'=>'flag-checkered','code'=>'f11e'],
|
||||||
|
['name'=>'terminal','code'=>'f120'],
|
||||||
|
['name'=>'code','code'=>'f121'],
|
||||||
|
['name'=>'reply-all','code'=>'f122'],
|
||||||
|
['name'=>'star-half-o','code'=>'f123'],
|
||||||
|
['name'=>'location-arrow','code'=>'f124'],
|
||||||
|
['name'=>'crop','code'=>'f125'],
|
||||||
|
['name'=>'code-fork','code'=>'f126'],
|
||||||
|
['name'=>'chain-broken','code'=>'f127'],
|
||||||
|
['name'=>'question','code'=>'f128'],
|
||||||
|
['name'=>'info','code'=>'f129'],
|
||||||
|
['name'=>'exclamation','code'=>'f12a'],
|
||||||
|
['name'=>'superscript','code'=>'f12b'],
|
||||||
|
['name'=>'subscript','code'=>'f12c'],
|
||||||
|
['name'=>'eraser','code'=>'f12d'],
|
||||||
|
['name'=>'puzzle-piece','code'=>'f12e'],
|
||||||
|
['name'=>'microphone','code'=>'f130'],
|
||||||
|
['name'=>'microphone-slash','code'=>'f131'],
|
||||||
|
['name'=>'shield','code'=>'f132'],
|
||||||
|
['name'=>'calendar-o','code'=>'f133'],
|
||||||
|
['name'=>'fire-extinguisher','code'=>'f134'],
|
||||||
|
['name'=>'rocket','code'=>'f135'],
|
||||||
|
['name'=>'maxcdn','code'=>'f136'],
|
||||||
|
['name'=>'chevron-circle-left','code'=>'f137'],
|
||||||
|
['name'=>'chevron-circle-right','code'=>'f138'],
|
||||||
|
['name'=>'chevron-circle-up','code'=>'f139'],
|
||||||
|
['name'=>'chevron-circle-down','code'=>'f13a'],
|
||||||
|
['name'=>'html5','code'=>'f13b'],
|
||||||
|
['name'=>'css3','code'=>'f13c'],
|
||||||
|
['name'=>'anchor','code'=>'f13d'],
|
||||||
|
['name'=>'unlock-alt','code'=>'f13e'],
|
||||||
|
['name'=>'bullseye','code'=>'f140'],
|
||||||
|
['name'=>'ellipsis-h','code'=>'f141'],
|
||||||
|
['name'=>'ellipsis-v','code'=>'f142'],
|
||||||
|
['name'=>'rss-square','code'=>'f143'],
|
||||||
|
['name'=>'play-circle','code'=>'f144'],
|
||||||
|
['name'=>'ticket','code'=>'f145'],
|
||||||
|
['name'=>'minus-square','code'=>'f146'],
|
||||||
|
['name'=>'minus-square-o','code'=>'f147'],
|
||||||
|
['name'=>'level-up','code'=>'f148'],
|
||||||
|
['name'=>'level-down','code'=>'f149'],
|
||||||
|
['name'=>'check-square','code'=>'f14a'],
|
||||||
|
['name'=>'pencil-square','code'=>'f14b'],
|
||||||
|
['name'=>'external-link-square','code'=>'f14c'],
|
||||||
|
['name'=>'share-square','code'=>'f14d'],
|
||||||
|
['name'=>'compass','code'=>'f14e'],
|
||||||
|
['name'=>'caret-square-o-down','code'=>'f150'],
|
||||||
|
['name'=>'caret-square-o-up','code'=>'f151'],
|
||||||
|
['name'=>'caret-square-o-right','code'=>'f152'],
|
||||||
|
['name'=>'eur','code'=>'f153'],
|
||||||
|
['name'=>'gbp','code'=>'f154'],
|
||||||
|
['name'=>'usd','code'=>'f155'],
|
||||||
|
['name'=>'inr','code'=>'f156'],
|
||||||
|
['name'=>'jpy','code'=>'f157'],
|
||||||
|
['name'=>'rub','code'=>'f158'],
|
||||||
|
['name'=>'krw','code'=>'f159'],
|
||||||
|
['name'=>'btc','code'=>'f15a'],
|
||||||
|
['name'=>'file','code'=>'f15b'],
|
||||||
|
['name'=>'file-text','code'=>'f15c'],
|
||||||
|
['name'=>'sort-alpha-asc','code'=>'f15d'],
|
||||||
|
['name'=>'sort-alpha-desc','code'=>'f15e'],
|
||||||
|
['name'=>'sort-amount-asc','code'=>'f160'],
|
||||||
|
['name'=>'sort-amount-desc','code'=>'f161'],
|
||||||
|
['name'=>'sort-numeric-asc','code'=>'f162'],
|
||||||
|
['name'=>'sort-numeric-desc','code'=>'f163'],
|
||||||
|
['name'=>'thumbs-up','code'=>'f164'],
|
||||||
|
['name'=>'thumbs-down','code'=>'f165'],
|
||||||
|
['name'=>'youtube-square','code'=>'f166'],
|
||||||
|
['name'=>'youtube','code'=>'f167'],
|
||||||
|
['name'=>'xing','code'=>'f168'],
|
||||||
|
['name'=>'xing-square','code'=>'f169'],
|
||||||
|
['name'=>'youtube-play','code'=>'f16a'],
|
||||||
|
['name'=>'dropbox','code'=>'f16b'],
|
||||||
|
['name'=>'stack-overflow','code'=>'f16c'],
|
||||||
|
['name'=>'instagram','code'=>'f16d'],
|
||||||
|
['name'=>'flickr','code'=>'f16e'],
|
||||||
|
['name'=>'adn','code'=>'f170'],
|
||||||
|
['name'=>'bitbucket','code'=>'f171'],
|
||||||
|
['name'=>'bitbucket-square','code'=>'f172'],
|
||||||
|
['name'=>'tumblr','code'=>'f173'],
|
||||||
|
['name'=>'tumblr-square','code'=>'f174'],
|
||||||
|
['name'=>'long-arrow-down','code'=>'f175'],
|
||||||
|
['name'=>'long-arrow-up','code'=>'f176'],
|
||||||
|
['name'=>'long-arrow-left','code'=>'f177'],
|
||||||
|
['name'=>'long-arrow-right','code'=>'f178'],
|
||||||
|
['name'=>'apple','code'=>'f179'],
|
||||||
|
['name'=>'windows','code'=>'f17a'],
|
||||||
|
['name'=>'android','code'=>'f17b'],
|
||||||
|
['name'=>'linux','code'=>'f17c'],
|
||||||
|
['name'=>'dribbble','code'=>'f17d'],
|
||||||
|
['name'=>'skype','code'=>'f17e'],
|
||||||
|
['name'=>'foursquare','code'=>'f180'],
|
||||||
|
['name'=>'trello','code'=>'f181'],
|
||||||
|
['name'=>'female','code'=>'f182'],
|
||||||
|
['name'=>'male','code'=>'f183'],
|
||||||
|
['name'=>'gratipay','code'=>'f184'],
|
||||||
|
['name'=>'sun-o','code'=>'f185'],
|
||||||
|
['name'=>'moon-o','code'=>'f186'],
|
||||||
|
['name'=>'archive','code'=>'f187'],
|
||||||
|
['name'=>'bug','code'=>'f188'],
|
||||||
|
['name'=>'vk','code'=>'f189'],
|
||||||
|
['name'=>'weibo','code'=>'f18a'],
|
||||||
|
['name'=>'renren','code'=>'f18b'],
|
||||||
|
['name'=>'pagelines','code'=>'f18c'],
|
||||||
|
['name'=>'stack-exchange','code'=>'f18d'],
|
||||||
|
['name'=>'arrow-circle-o-right','code'=>'f18e'],
|
||||||
|
['name'=>'arrow-circle-o-left','code'=>'f190'],
|
||||||
|
['name'=>'caret-square-o-left','code'=>'f191'],
|
||||||
|
['name'=>'dot-circle-o','code'=>'f192'],
|
||||||
|
['name'=>'wheelchair','code'=>'f193'],
|
||||||
|
['name'=>'vimeo-square','code'=>'f194'],
|
||||||
|
['name'=>'try','code'=>'f195'],
|
||||||
|
['name'=>'plus-square-o','code'=>'f196'],
|
||||||
|
['name'=>'space-shuttle','code'=>'f197'],
|
||||||
|
['name'=>'slack','code'=>'f198'],
|
||||||
|
['name'=>'envelope-square','code'=>'f199'],
|
||||||
|
['name'=>'wordpress','code'=>'f19a'],
|
||||||
|
['name'=>'openid','code'=>'f19b'],
|
||||||
|
['name'=>'university','code'=>'f19c'],
|
||||||
|
['name'=>'graduation-cap','code'=>'f19d'],
|
||||||
|
['name'=>'yahoo','code'=>'f19e'],
|
||||||
|
['name'=>'google','code'=>'f1a0'],
|
||||||
|
['name'=>'reddit','code'=>'f1a1'],
|
||||||
|
['name'=>'reddit-square','code'=>'f1a2'],
|
||||||
|
['name'=>'stumbleupon-circle','code'=>'f1a3'],
|
||||||
|
['name'=>'stumbleupon','code'=>'f1a4'],
|
||||||
|
['name'=>'delicious','code'=>'f1a5'],
|
||||||
|
['name'=>'digg','code'=>'f1a6'],
|
||||||
|
['name'=>'pied-piper','code'=>'f1a7'],
|
||||||
|
['name'=>'pied-piper-alt','code'=>'f1a8'],
|
||||||
|
['name'=>'drupal','code'=>'f1a9'],
|
||||||
|
['name'=>'joomla','code'=>'f1aa'],
|
||||||
|
['name'=>'language','code'=>'f1ab'],
|
||||||
|
['name'=>'fax','code'=>'f1ac'],
|
||||||
|
['name'=>'building','code'=>'f1ad'],
|
||||||
|
['name'=>'child','code'=>'f1ae'],
|
||||||
|
['name'=>'paw','code'=>'f1b0'],
|
||||||
|
['name'=>'spoon','code'=>'f1b1'],
|
||||||
|
['name'=>'cube','code'=>'f1b2'],
|
||||||
|
['name'=>'cubes','code'=>'f1b3'],
|
||||||
|
['name'=>'behance','code'=>'f1b4'],
|
||||||
|
['name'=>'behance-square','code'=>'f1b5'],
|
||||||
|
['name'=>'steam','code'=>'f1b6'],
|
||||||
|
['name'=>'steam-square','code'=>'f1b7'],
|
||||||
|
['name'=>'recycle','code'=>'f1b8'],
|
||||||
|
['name'=>'car','code'=>'f1b9'],
|
||||||
|
['name'=>'taxi','code'=>'f1ba'],
|
||||||
|
['name'=>'tree','code'=>'f1bb'],
|
||||||
|
['name'=>'spotify','code'=>'f1bc'],
|
||||||
|
['name'=>'deviantart','code'=>'f1bd'],
|
||||||
|
['name'=>'soundcloud','code'=>'f1be'],
|
||||||
|
['name'=>'database','code'=>'f1c0'],
|
||||||
|
['name'=>'file-pdf-o','code'=>'f1c1'],
|
||||||
|
['name'=>'file-word-o','code'=>'f1c2'],
|
||||||
|
['name'=>'file-excel-o','code'=>'f1c3'],
|
||||||
|
['name'=>'file-powerpoint-o','code'=>'f1c4'],
|
||||||
|
['name'=>'file-image-o','code'=>'f1c5'],
|
||||||
|
['name'=>'file-archive-o','code'=>'f1c6'],
|
||||||
|
['name'=>'file-audio-o','code'=>'f1c7'],
|
||||||
|
['name'=>'file-video-o','code'=>'f1c8'],
|
||||||
|
['name'=>'file-code-o','code'=>'f1c9'],
|
||||||
|
['name'=>'vine','code'=>'f1ca'],
|
||||||
|
['name'=>'codepen','code'=>'f1cb'],
|
||||||
|
['name'=>'jsfiddle','code'=>'f1cc'],
|
||||||
|
['name'=>'life-ring','code'=>'f1cd'],
|
||||||
|
['name'=>'circle-o-notch','code'=>'f1ce'],
|
||||||
|
['name'=>'rebel','code'=>'f1d0'],
|
||||||
|
['name'=>'empire','code'=>'f1d1'],
|
||||||
|
['name'=>'git-square','code'=>'f1d2'],
|
||||||
|
['name'=>'git','code'=>'f1d3'],
|
||||||
|
['name'=>'hacker-news','code'=>'f1d4'],
|
||||||
|
['name'=>'tencent-weibo','code'=>'f1d5'],
|
||||||
|
['name'=>'qq','code'=>'f1d6'],
|
||||||
|
['name'=>'weixin','code'=>'f1d7'],
|
||||||
|
['name'=>'paper-plane','code'=>'f1d8'],
|
||||||
|
['name'=>'paper-plane-o','code'=>'f1d9'],
|
||||||
|
['name'=>'history','code'=>'f1da'],
|
||||||
|
['name'=>'circle-thin','code'=>'f1db'],
|
||||||
|
['name'=>'header','code'=>'f1dc'],
|
||||||
|
['name'=>'paragraph','code'=>'f1dd'],
|
||||||
|
['name'=>'sliders','code'=>'f1de'],
|
||||||
|
['name'=>'share-alt','code'=>'f1e0'],
|
||||||
|
['name'=>'share-alt-square','code'=>'f1e1'],
|
||||||
|
['name'=>'bomb','code'=>'f1e2'],
|
||||||
|
['name'=>'futbol-o','code'=>'f1e3'],
|
||||||
|
['name'=>'tty','code'=>'f1e4'],
|
||||||
|
['name'=>'binoculars','code'=>'f1e5'],
|
||||||
|
['name'=>'plug','code'=>'f1e6'],
|
||||||
|
['name'=>'slideshare','code'=>'f1e7'],
|
||||||
|
['name'=>'twitch','code'=>'f1e8'],
|
||||||
|
['name'=>'yelp','code'=>'f1e9'],
|
||||||
|
['name'=>'newspaper-o','code'=>'f1ea'],
|
||||||
|
['name'=>'wifi','code'=>'f1eb'],
|
||||||
|
['name'=>'calculator','code'=>'f1ec'],
|
||||||
|
['name'=>'paypal','code'=>'f1ed'],
|
||||||
|
['name'=>'google-wallet','code'=>'f1ee'],
|
||||||
|
['name'=>'cc-visa','code'=>'f1f0'],
|
||||||
|
['name'=>'cc-mastercard','code'=>'f1f1'],
|
||||||
|
['name'=>'cc-discover','code'=>'f1f2'],
|
||||||
|
['name'=>'cc-amex','code'=>'f1f3'],
|
||||||
|
['name'=>'cc-paypal','code'=>'f1f4'],
|
||||||
|
['name'=>'cc-stripe','code'=>'f1f5'],
|
||||||
|
['name'=>'bell-slash','code'=>'f1f6'],
|
||||||
|
['name'=>'bell-slash-o','code'=>'f1f7'],
|
||||||
|
['name'=>'trash','code'=>'f1f8'],
|
||||||
|
['name'=>'copyright','code'=>'f1f9'],
|
||||||
|
['name'=>'at','code'=>'f1fa'],
|
||||||
|
['name'=>'eyedropper','code'=>'f1fb'],
|
||||||
|
['name'=>'paint-brush','code'=>'f1fc'],
|
||||||
|
['name'=>'birthday-cake','code'=>'f1fd'],
|
||||||
|
['name'=>'area-chart','code'=>'f1fe'],
|
||||||
|
['name'=>'pie-chart','code'=>'f200'],
|
||||||
|
['name'=>'line-chart','code'=>'f201'],
|
||||||
|
['name'=>'lastfm','code'=>'f202'],
|
||||||
|
['name'=>'lastfm-square','code'=>'f203'],
|
||||||
|
['name'=>'toggle-off','code'=>'f204'],
|
||||||
|
['name'=>'toggle-on','code'=>'f205'],
|
||||||
|
['name'=>'bicycle','code'=>'f206'],
|
||||||
|
['name'=>'bus','code'=>'f207'],
|
||||||
|
['name'=>'ioxhost','code'=>'f208'],
|
||||||
|
['name'=>'angellist','code'=>'f209'],
|
||||||
|
['name'=>'cc','code'=>'f20a'],
|
||||||
|
['name'=>'ils','code'=>'f20b'],
|
||||||
|
['name'=>'meanpath','code'=>'f20c'],
|
||||||
|
['name'=>'buysellads','code'=>'f20d'],
|
||||||
|
['name'=>'connectdevelop','code'=>'f20e'],
|
||||||
|
['name'=>'dashcube','code'=>'f210'],
|
||||||
|
['name'=>'forumbee','code'=>'f211'],
|
||||||
|
['name'=>'leanpub','code'=>'f212'],
|
||||||
|
['name'=>'sellsy','code'=>'f213'],
|
||||||
|
['name'=>'shirtsinbulk','code'=>'f214'],
|
||||||
|
['name'=>'simplybuilt','code'=>'f215'],
|
||||||
|
['name'=>'skyatlas','code'=>'f216'],
|
||||||
|
['name'=>'cart-plus','code'=>'f217'],
|
||||||
|
['name'=>'cart-arrow-down','code'=>'f218'],
|
||||||
|
['name'=>'diamond','code'=>'f219'],
|
||||||
|
['name'=>'ship','code'=>'f21a'],
|
||||||
|
['name'=>'user-secret','code'=>'f21b'],
|
||||||
|
['name'=>'motorcycle','code'=>'f21c'],
|
||||||
|
['name'=>'street-view','code'=>'f21d'],
|
||||||
|
['name'=>'heartbeat','code'=>'f21e'],
|
||||||
|
['name'=>'venus','code'=>'f221'],
|
||||||
|
['name'=>'mars','code'=>'f222'],
|
||||||
|
['name'=>'mercury','code'=>'f223'],
|
||||||
|
['name'=>'transgender','code'=>'f224'],
|
||||||
|
['name'=>'transgender-alt','code'=>'f225'],
|
||||||
|
['name'=>'venus-double','code'=>'f226'],
|
||||||
|
['name'=>'mars-double','code'=>'f227'],
|
||||||
|
['name'=>'venus-mars','code'=>'f228'],
|
||||||
|
['name'=>'mars-stroke','code'=>'f229'],
|
||||||
|
['name'=>'mars-stroke-v','code'=>'f22a'],
|
||||||
|
['name'=>'mars-stroke-h','code'=>'f22b'],
|
||||||
|
['name'=>'neuter','code'=>'f22c'],
|
||||||
|
['name'=>'genderless','code'=>'f22d'],
|
||||||
|
['name'=>'facebook-official','code'=>'f230'],
|
||||||
|
['name'=>'pinterest-p','code'=>'f231'],
|
||||||
|
['name'=>'whatsapp','code'=>'f232'],
|
||||||
|
['name'=>'server','code'=>'f233'],
|
||||||
|
['name'=>'user-plus','code'=>'f234'],
|
||||||
|
['name'=>'user-times','code'=>'f235'],
|
||||||
|
['name'=>'bed','code'=>'f236'],
|
||||||
|
['name'=>'viacoin','code'=>'f237'],
|
||||||
|
['name'=>'train','code'=>'f238'],
|
||||||
|
['name'=>'subway','code'=>'f239'],
|
||||||
|
['name'=>'medium','code'=>'f23a'],
|
||||||
|
['name'=>'y-combinator','code'=>'f23b'],
|
||||||
|
['name'=>'optin-monster','code'=>'f23c'],
|
||||||
|
['name'=>'opencart','code'=>'f23d'],
|
||||||
|
['name'=>'expeditedssl','code'=>'f23e'],
|
||||||
|
['name'=>'battery-full','code'=>'f240'],
|
||||||
|
['name'=>'battery-three-quarters','code'=>'f241'],
|
||||||
|
['name'=>'battery-half','code'=>'f242'],
|
||||||
|
['name'=>'battery-quarter','code'=>'f243'],
|
||||||
|
['name'=>'battery-empty','code'=>'f244'],
|
||||||
|
['name'=>'mouse-pointer','code'=>'f245'],
|
||||||
|
['name'=>'i-cursor','code'=>'f246'],
|
||||||
|
['name'=>'object-group','code'=>'f247'],
|
||||||
|
['name'=>'object-ungroup','code'=>'f248'],
|
||||||
|
['name'=>'sticky-note','code'=>'f249'],
|
||||||
|
['name'=>'sticky-note-o','code'=>'f24a'],
|
||||||
|
['name'=>'cc-jcb','code'=>'f24b'],
|
||||||
|
['name'=>'cc-diners-club','code'=>'f24c'],
|
||||||
|
['name'=>'clone','code'=>'f24d'],
|
||||||
|
['name'=>'balance-scale','code'=>'f24e'],
|
||||||
|
['name'=>'hourglass-o','code'=>'f250'],
|
||||||
|
['name'=>'hourglass-start','code'=>'f251'],
|
||||||
|
['name'=>'hourglass-half','code'=>'f252'],
|
||||||
|
['name'=>'hourglass-end','code'=>'f253'],
|
||||||
|
['name'=>'hourglass','code'=>'f254'],
|
||||||
|
['name'=>'hand-rock-o','code'=>'f255'],
|
||||||
|
['name'=>'hand-paper-o','code'=>'f256'],
|
||||||
|
['name'=>'hand-scissors-o','code'=>'f257'],
|
||||||
|
['name'=>'hand-lizard-o','code'=>'f258'],
|
||||||
|
['name'=>'hand-spock-o','code'=>'f259'],
|
||||||
|
['name'=>'hand-pointer-o','code'=>'f25a'],
|
||||||
|
['name'=>'hand-peace-o','code'=>'f25b'],
|
||||||
|
['name'=>'trademark','code'=>'f25c'],
|
||||||
|
['name'=>'registered','code'=>'f25d'],
|
||||||
|
['name'=>'creative-commons','code'=>'f25e'],
|
||||||
|
['name'=>'gg','code'=>'f260'],
|
||||||
|
['name'=>'gg-circle','code'=>'f261'],
|
||||||
|
['name'=>'tripadvisor','code'=>'f262'],
|
||||||
|
['name'=>'odnoklassniki','code'=>'f263'],
|
||||||
|
['name'=>'odnoklassniki-square','code'=>'f264'],
|
||||||
|
['name'=>'get-pocket','code'=>'f265'],
|
||||||
|
['name'=>'wikipedia-w','code'=>'f266'],
|
||||||
|
['name'=>'safari','code'=>'f267'],
|
||||||
|
['name'=>'chrome','code'=>'f268'],
|
||||||
|
['name'=>'firefox','code'=>'f269'],
|
||||||
|
['name'=>'opera','code'=>'f26a'],
|
||||||
|
['name'=>'internet-explorer','code'=>'f26b'],
|
||||||
|
['name'=>'television','code'=>'f26c'],
|
||||||
|
['name'=>'contao','code'=>'f26d'],
|
||||||
|
['name'=>'500px','code'=>'f26e'],
|
||||||
|
['name'=>'amazon','code'=>'f270'],
|
||||||
|
['name'=>'calendar-plus-o','code'=>'f271'],
|
||||||
|
['name'=>'calendar-minus-o','code'=>'f272'],
|
||||||
|
['name'=>'calendar-times-o','code'=>'f273'],
|
||||||
|
['name'=>'calendar-check-o','code'=>'f274'],
|
||||||
|
['name'=>'industry','code'=>'f275'],
|
||||||
|
['name'=>'map-pin','code'=>'f276'],
|
||||||
|
['name'=>'map-signs','code'=>'f277'],
|
||||||
|
['name'=>'map-o','code'=>'f278'],
|
||||||
|
['name'=>'map','code'=>'f279'],
|
||||||
|
['name'=>'commenting','code'=>'f27a'],
|
||||||
|
['name'=>'commenting-o','code'=>'f27b'],
|
||||||
|
['name'=>'houzz','code'=>'f27c'],
|
||||||
|
['name'=>'vimeo','code'=>'f27d'],
|
||||||
|
['name'=>'black-tie','code'=>'f27e'],
|
||||||
|
['name'=>'fonticons','code'=>'f280'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($icons as $icon) {
|
||||||
|
$data[$icon['name']] = '&#x' . $icon['code'] . ' ' . ucwords(str_replace('-', ' ', $icon['name']));
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
173
app/Http/Controllers/ProposalTemplateController.php
Normal file
173
app/Http/Controllers/ProposalTemplateController.php
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\CreateProposalTemplateRequest;
|
||||||
|
use App\Http\Requests\ProposalTemplateRequest;
|
||||||
|
use App\Http\Requests\UpdateProposalTemplateRequest;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\ProposalTemplate;
|
||||||
|
use App\Ninja\Datatables\ProposalTemplateDatatable;
|
||||||
|
use App\Ninja\Repositories\ProposalTemplateRepository;
|
||||||
|
use App\Services\ProposalTemplateService;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Session;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
class ProposalTemplateController extends BaseController
|
||||||
|
{
|
||||||
|
protected $proposalTemplateRepo;
|
||||||
|
protected $proposalTemplateService;
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
|
||||||
|
public function __construct(ProposalTemplateRepository $proposalTemplateRepo, ProposalTemplateService $proposalTemplateService)
|
||||||
|
{
|
||||||
|
$this->proposalTemplateRepo = $proposalTemplateRepo;
|
||||||
|
$this->proposalTemplateService = $proposalTemplateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return View::make('list_wrapper', [
|
||||||
|
'entityType' => ENTITY_PROPOSAL_TEMPLATE,
|
||||||
|
'datatable' => new ProposalTemplateDatatable(),
|
||||||
|
'title' => trans('texts.proposal_templates'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatatable($expensePublicId = null)
|
||||||
|
{
|
||||||
|
$search = Input::get('sSearch');
|
||||||
|
$userId = Auth::user()->filterId();
|
||||||
|
|
||||||
|
return $this->proposalTemplateService->getDatatable($search, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(ProposalTemplateRequest $request)
|
||||||
|
{
|
||||||
|
$data = array_merge($this->getViewmodel(), [
|
||||||
|
'template' => null,
|
||||||
|
'method' => 'POST',
|
||||||
|
'url' => 'proposals/templates',
|
||||||
|
'title' => trans('texts.new_proposal_template'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return View::make('proposals/templates/edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getViewmodel()
|
||||||
|
{
|
||||||
|
$customTemplates = ProposalTemplate::scope()->orderBy('name')->get();
|
||||||
|
$defaultTemplates = ProposalTemplate::whereNull('account_id')->orderBy('public_id')->get();
|
||||||
|
|
||||||
|
$options = [];
|
||||||
|
$customLabel = trans('texts.custom');
|
||||||
|
$defaultLabel = trans('texts.default');
|
||||||
|
|
||||||
|
foreach ($customTemplates as $template) {
|
||||||
|
if (! isset($options[$customLabel])) {
|
||||||
|
$options[$customLabel] = [];
|
||||||
|
}
|
||||||
|
$options[trans('texts.custom')][$template->public_id] = $template->name;
|
||||||
|
}
|
||||||
|
foreach ($defaultTemplates as $template) {
|
||||||
|
if (! isset($options[$defaultLabel])) {
|
||||||
|
$options[$defaultLabel] = [];
|
||||||
|
}
|
||||||
|
$options[trans('texts.default')][$template->public_id] = $template->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'customTemplates' => $customTemplates,
|
||||||
|
'defaultTemplates' => $defaultTemplates,
|
||||||
|
'templateOptions' => $options,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($publicId)
|
||||||
|
{
|
||||||
|
Session::reflash();
|
||||||
|
|
||||||
|
return redirect("proposals/templates/$publicId/edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(ProposalTemplateRequest $request, $publicId = false, $clone = false)
|
||||||
|
{
|
||||||
|
$template = $request->entity();
|
||||||
|
|
||||||
|
if ($clone) {
|
||||||
|
$template->id = null;
|
||||||
|
$template->public_id = null;
|
||||||
|
$template->name = '';
|
||||||
|
$template->private_notes = '';
|
||||||
|
$method = 'POST';
|
||||||
|
$url = 'proposals/templates';
|
||||||
|
} else {
|
||||||
|
$method = 'PUT';
|
||||||
|
$url = 'proposals/templates/' . $template->public_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array_merge($this->getViewmodel(), [
|
||||||
|
'template' => $template,
|
||||||
|
'entity' => $clone ? false : $template,
|
||||||
|
'method' => $method,
|
||||||
|
'url' => $url,
|
||||||
|
'title' => trans('texts.edit_proposal_template'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return View::make('proposals/templates/edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cloneProposal(ProposalTemplateRequest $request, $publicId)
|
||||||
|
{
|
||||||
|
return self::edit($request, $publicId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(CreateProposalTemplateRequest $request)
|
||||||
|
{
|
||||||
|
$proposalTemplate = $this->proposalTemplateService->save($request->input());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.created_proposal_template'));
|
||||||
|
|
||||||
|
return redirect()->to($proposalTemplate->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProposalTemplateRequest $request)
|
||||||
|
{
|
||||||
|
$proposalTemplate = $this->proposalTemplateService->save($request->input(), $request->entity());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.updated_proposal_template'));
|
||||||
|
|
||||||
|
$action = Input::get('action');
|
||||||
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposalTemplate->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bulk()
|
||||||
|
{
|
||||||
|
$action = Input::get('action');
|
||||||
|
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||||
|
|
||||||
|
$count = $this->proposalTemplateService->bulk($ids, $action);
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$field = $count == 1 ? "{$action}d_proposal_template" : "{$action}d_proposal_templates";
|
||||||
|
$message = trans("texts.$field", ['count' => $count]);
|
||||||
|
Session::flash('message', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('/proposals/templates');
|
||||||
|
}
|
||||||
|
}
|
@ -97,7 +97,7 @@ class QuoteController extends BaseController
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'entityType' => ENTITY_QUOTE,
|
'entityType' => ENTITY_QUOTE,
|
||||||
'account' => $account,
|
'account' => Auth::user()->account->load('country'),
|
||||||
'products' => Product::scope()->orderBy('product_key')->get(),
|
'products' => Product::scope()->orderBy('product_key')->get(),
|
||||||
'taxRateOptions' => $account->present()->taxRateOptions,
|
'taxRateOptions' => $account->present()->taxRateOptions,
|
||||||
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
||||||
@ -148,6 +148,11 @@ class QuoteController extends BaseController
|
|||||||
{
|
{
|
||||||
$invitation = Invitation::with('invoice.invoice_items', 'invoice.invitations')->where('invitation_key', '=', $invitationKey)->firstOrFail();
|
$invitation = Invitation::with('invoice.invoice_items', 'invoice.invitations')->where('invitation_key', '=', $invitationKey)->firstOrFail();
|
||||||
$invoice = $invitation->invoice;
|
$invoice = $invitation->invoice;
|
||||||
|
$account = $invoice->account;
|
||||||
|
|
||||||
|
if ($account->requiresAuthorization($invoice) && ! session('authorized:' . $invitation->invitation_key)) {
|
||||||
|
return redirect()->to('view/' . $invitation->invitation_key);
|
||||||
|
}
|
||||||
|
|
||||||
if ($invoice->due_date) {
|
if ($invoice->due_date) {
|
||||||
$carbonDueDate = \Carbon::parse($invoice->due_date);
|
$carbonDueDate = \Carbon::parse($invoice->due_date);
|
||||||
|
@ -75,6 +75,7 @@ class ReportController extends BaseController
|
|||||||
'activity',
|
'activity',
|
||||||
'aging',
|
'aging',
|
||||||
'client',
|
'client',
|
||||||
|
'credit',
|
||||||
'document',
|
'document',
|
||||||
'expense',
|
'expense',
|
||||||
'invoice',
|
'invoice',
|
||||||
|
@ -134,6 +134,7 @@ class SubscriptionController extends BaseController
|
|||||||
$subscription = Subscription::scope($subscriptionPublicId)->firstOrFail();
|
$subscription = Subscription::scope($subscriptionPublicId)->firstOrFail();
|
||||||
} else {
|
} else {
|
||||||
$subscription = Subscription::createNew();
|
$subscription = Subscription::createNew();
|
||||||
|
$subscriptionPublicId = $subscription->public_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = Validator::make(Input::all(), $rules);
|
$validator = Validator::make(Input::all(), $rules);
|
||||||
@ -154,6 +155,14 @@ class SubscriptionController extends BaseController
|
|||||||
Session::flash('message', $message);
|
Session::flash('message', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Redirect::to('settings/' . ACCOUNT_API_TOKENS);
|
return redirect('/settings/api_tokens');
|
||||||
|
|
||||||
|
/*
|
||||||
|
if ($subscriptionPublicId) {
|
||||||
|
return Redirect::to('subscriptions/' . $subscriptionPublicId . '/edit');
|
||||||
|
} else {
|
||||||
|
return redirect('/settings/api_tokens');
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,16 @@ class TaskKanbanController extends BaseController
|
|||||||
$task->task_status_sort_order = $i++;
|
$task->task_status_sort_order = $i++;
|
||||||
$task->save();
|
$task->save();
|
||||||
}
|
}
|
||||||
// otherwise, check that the tasks orders are correct
|
// otherwise, check that the orders are correct
|
||||||
} else {
|
} else {
|
||||||
|
for ($i=0; $i<$statuses->count(); $i++) {
|
||||||
|
$status = $statuses[$i];
|
||||||
|
if ($status->sort_order != $i) {
|
||||||
|
$status->sort_order = $i;
|
||||||
|
$status->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$firstStatus = $statuses[0];
|
$firstStatus = $statuses[0];
|
||||||
$counts = [];
|
$counts = [];
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
|
@ -118,7 +118,7 @@ class UserController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! Auth::user()->confirmed) {
|
if (! Auth::user()->confirmed) {
|
||||||
Session::flash('error', trans('texts.confirmation_required'));
|
Session::flash('error', trans('texts.confirmation_required', ['link' => link_to('/resend_confirmation', trans('texts.click_here'))]));
|
||||||
|
|
||||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||||
}
|
}
|
||||||
|
@ -100,8 +100,8 @@ class ApiCheck
|
|||||||
return Response::json("Please wait {$wait} second(s)", 403, $headers);
|
return Response::json("Please wait {$wait} second(s)", 403, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::put("hour_throttle:{$key}", $new_hour_throttle, 10);
|
Cache::put("hour_throttle:{$key}", $new_hour_throttle, 60);
|
||||||
Cache::put("last_api_request:{$key}", time(), 10);
|
Cache::put("last_api_request:{$key}", time(), 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Http\Middleware;
|
|||||||
|
|
||||||
use App\Models\Contact;
|
use App\Models\Contact;
|
||||||
use App\Models\Invitation;
|
use App\Models\Invitation;
|
||||||
|
use App\Models\ProposalInvitation;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Session;
|
use Session;
|
||||||
@ -25,13 +26,14 @@ class Authenticate
|
|||||||
public function handle($request, Closure $next, $guard = 'user')
|
public function handle($request, Closure $next, $guard = 'user')
|
||||||
{
|
{
|
||||||
$authenticated = Auth::guard($guard)->check();
|
$authenticated = Auth::guard($guard)->check();
|
||||||
|
$invitationKey = $request->invitation_key ?: $request->proposal_invitation_key;
|
||||||
|
|
||||||
if ($guard == 'client') {
|
if ($guard == 'client') {
|
||||||
if (! empty($request->invitation_key)) {
|
if (! empty($request->invitation_key) || ! empty($request->proposal_invitation_key)) {
|
||||||
$contact_key = session('contact_key');
|
$contact_key = session('contact_key');
|
||||||
if ($contact_key) {
|
if ($contact_key) {
|
||||||
$contact = $this->getContact($contact_key);
|
$contact = $this->getContact($contact_key);
|
||||||
$invitation = $this->getInvitation($request->invitation_key);
|
$invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key));
|
||||||
|
|
||||||
if (! $invitation) {
|
if (! $invitation) {
|
||||||
return response()->view('error', [
|
return response()->view('error', [
|
||||||
@ -59,7 +61,7 @@ class Authenticate
|
|||||||
$contact = false;
|
$contact = false;
|
||||||
if ($contact_key) {
|
if ($contact_key) {
|
||||||
$contact = $this->getContact($contact_key);
|
$contact = $this->getContact($contact_key);
|
||||||
} elseif ($invitation = $this->getInvitation($request->invitation_key)) {
|
} elseif ($invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key))) {
|
||||||
$contact = $invitation->contact;
|
$contact = $invitation->contact;
|
||||||
Session::put('contact_key', $contact->contact_key);
|
Session::put('contact_key', $contact->contact_key);
|
||||||
}
|
}
|
||||||
@ -89,6 +91,7 @@ class Authenticate
|
|||||||
|
|
||||||
if ($authenticated) {
|
if ($authenticated) {
|
||||||
$request->merge(['contact' => $contact]);
|
$request->merge(['contact' => $contact]);
|
||||||
|
$account->loadLocalizationSettings($contact->client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +111,7 @@ class Authenticate
|
|||||||
*
|
*
|
||||||
* @return \Illuminate\Database\Eloquent\Model|null|static
|
* @return \Illuminate\Database\Eloquent\Model|null|static
|
||||||
*/
|
*/
|
||||||
protected function getInvitation($key)
|
protected function getInvitation($key, $isProposal = false)
|
||||||
{
|
{
|
||||||
if (! $key) {
|
if (! $key) {
|
||||||
return false;
|
return false;
|
||||||
@ -118,7 +121,12 @@ class Authenticate
|
|||||||
list($key) = explode('&', $key);
|
list($key) = explode('&', $key);
|
||||||
$key = substr($key, 0, RANDOM_KEY_LENGTH);
|
$key = substr($key, 0, RANDOM_KEY_LENGTH);
|
||||||
|
|
||||||
|
if ($isProposal) {
|
||||||
|
$invitation = ProposalInvitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
||||||
|
} else {
|
||||||
$invitation = Invitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
$invitation = Invitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
||||||
|
}
|
||||||
|
|
||||||
if ($invitation && ! $invitation->is_deleted) {
|
if ($invitation && ! $invitation->is_deleted) {
|
||||||
return $invitation;
|
return $invitation;
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,6 +7,7 @@ use Closure;
|
|||||||
use App\Models\LookupAccount;
|
use App\Models\LookupAccount;
|
||||||
use App\Models\LookupContact;
|
use App\Models\LookupContact;
|
||||||
use App\Models\LookupInvitation;
|
use App\Models\LookupInvitation;
|
||||||
|
use App\Models\LookupProposalInvitation;
|
||||||
use App\Models\LookupAccountToken;
|
use App\Models\LookupAccountToken;
|
||||||
use App\Models\LookupUser;
|
use App\Models\LookupUser;
|
||||||
use Auth;
|
use Auth;
|
||||||
@ -43,6 +44,8 @@ class DatabaseLookup
|
|||||||
} elseif ($guard == 'contact') {
|
} elseif ($guard == 'contact') {
|
||||||
if ($key = request()->invitation_key) {
|
if ($key = request()->invitation_key) {
|
||||||
LookupInvitation::setServerByField('invitation_key', $key);
|
LookupInvitation::setServerByField('invitation_key', $key);
|
||||||
|
} elseif ($key = request()->proposal_invitation_key) {
|
||||||
|
LookupProposalInvitation::setServerByField('invitation_key', $key);
|
||||||
} elseif ($key = request()->contact_key ?: session('contact_key')) {
|
} elseif ($key = request()->contact_key ?: session('contact_key')) {
|
||||||
LookupContact::setServerByField('contact_key', $key);
|
LookupContact::setServerByField('contact_key', $key);
|
||||||
} elseif ($key = request()->account_key) {
|
} elseif ($key = request()->account_key) {
|
||||||
|
@ -36,9 +36,14 @@ class StartupCheck
|
|||||||
// Set up trusted X-Forwarded-Proto proxies
|
// Set up trusted X-Forwarded-Proto proxies
|
||||||
// TRUSTED_PROXIES accepts a comma delimited list of subnets
|
// TRUSTED_PROXIES accepts a comma delimited list of subnets
|
||||||
// ie, TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
|
// ie, TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
|
||||||
|
// set TRUSTED_PROXIES=* if you want to trust every proxy.
|
||||||
if (isset($_ENV['TRUSTED_PROXIES'])) {
|
if (isset($_ENV['TRUSTED_PROXIES'])) {
|
||||||
|
if (env('TRUSTED_PROXIES') == '*') {
|
||||||
|
$request->setTrustedProxies(['127.0.0.1', $request->server->get('REMOTE_ADDR')]);
|
||||||
|
} else{
|
||||||
$request->setTrustedProxies(array_map('trim', explode(',', env('TRUSTED_PROXIES'))));
|
$request->setTrustedProxies(array_map('trim', explode(',', env('TRUSTED_PROXIES'))));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure all request are over HTTPS in production
|
// Ensure all request are over HTTPS in production
|
||||||
if (Utils::requireHTTPS() && ! $request->secure()) {
|
if (Utils::requireHTTPS() && ! $request->secure()) {
|
||||||
@ -218,7 +223,7 @@ class StartupCheck
|
|||||||
|
|
||||||
// Show message to IE 8 and before users
|
// Show message to IE 8 and before users
|
||||||
if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) {
|
if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) {
|
||||||
Session::flash('error', trans('texts.old_browser', ['link' => OUTDATE_BROWSER_URL]));
|
Session::flash('error', trans('texts.old_browser', ['link' => link_to(OUTDATE_BROWSER_URL, trans('texts.newer_browser'), ['target' => '_blank'])]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $next($request);
|
$response = $next($request);
|
||||||
|
36
app/Http/Requests/CreatePaymentTermAPIRequest.php
Normal file
36
app/Http/Requests/CreatePaymentTermAPIRequest.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
|
||||||
|
class CreatePaymentTermAPIRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->user()->can('create', ENTITY_PAYMENT_TERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'num_days' => 'required|numeric|unique:payment_terms',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
}
|
||||||
|
}
|
28
app/Http/Requests/CreateProposalCategoryRequest.php
Normal file
28
app/Http/Requests/CreateProposalCategoryRequest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class CreateProposalCategoryRequest extends ProposalCategoryRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->user()->can('create', ENTITY_PROPOSAL_CATEGORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => sprintf('required|unique:proposal_categories,name,,id,account_id,%s', $this->user()->account_id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
28
app/Http/Requests/CreateProposalRequest.php
Normal file
28
app/Http/Requests/CreateProposalRequest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class CreateProposalRequest extends ProposalRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->user()->can('create', ENTITY_PROPOSAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'invoice_id' => 'required',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
28
app/Http/Requests/CreateProposalSnippetRequest.php
Normal file
28
app/Http/Requests/CreateProposalSnippetRequest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class CreateProposalSnippetRequest extends ProposalSnippetRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->user()->can('create', ENTITY_PROPOSAL_SNIPPET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => sprintf('required|unique:proposal_snippets,name,,id,account_id,%s', $this->user()->account_id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
28
app/Http/Requests/CreateProposalTemplateRequest.php
Normal file
28
app/Http/Requests/CreateProposalTemplateRequest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class CreateProposalTemplateRequest extends ProposalTemplateRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->user()->can('create', ENTITY_PROPOSAL_TEMPLATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => sprintf('required|unique:proposal_templates,name,,id,account_id,%s', $this->user()->account_id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ class EntityRequest extends Request
|
|||||||
if (! $publicId) {
|
if (! $publicId) {
|
||||||
$publicId = Input::get('public_id') ?: Input::get('id');
|
$publicId = Input::get('public_id') ?: Input::get('id');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $publicId) {
|
if (! $publicId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
8
app/Http/Requests/PaymentTermRequest.php
Normal file
8
app/Http/Requests/PaymentTermRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class PaymentTermRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PAYMENT_TERM;
|
||||||
|
}
|
8
app/Http/Requests/ProposalCategoryRequest.php
Normal file
8
app/Http/Requests/ProposalCategoryRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalCategoryRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
}
|
8
app/Http/Requests/ProposalRequest.php
Normal file
8
app/Http/Requests/ProposalRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL;
|
||||||
|
}
|
34
app/Http/Requests/ProposalSnippetRequest.php
Normal file
34
app/Http/Requests/ProposalSnippetRequest.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Models\ProposalCategory;
|
||||||
|
|
||||||
|
class ProposalSnippetRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
|
||||||
|
public function sanitize()
|
||||||
|
{
|
||||||
|
$input = $this->all();
|
||||||
|
|
||||||
|
// check if we're creating a new proposal category
|
||||||
|
if ($this->proposal_category_id == '-1') {
|
||||||
|
$data = [
|
||||||
|
'name' => trim($this->proposal_category_name)
|
||||||
|
];
|
||||||
|
if (ProposalCategory::validate($data) === true) {
|
||||||
|
$category = app('App\Ninja\Repositories\ProposalCategoryRepository')->save($data);
|
||||||
|
$input['proposal_category_id'] = $category->id;
|
||||||
|
} else {
|
||||||
|
$input['proposal_category_id'] = null;
|
||||||
|
}
|
||||||
|
} elseif ($this->proposal_category_id) {
|
||||||
|
$input['proposal_category_id'] = ProposalCategory::getPrivateId($this->proposal_category_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->replace($input);
|
||||||
|
|
||||||
|
return $this->all();
|
||||||
|
}
|
||||||
|
}
|
8
app/Http/Requests/ProposalTemplateRequest.php
Normal file
8
app/Http/Requests/ProposalTemplateRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalTemplateRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
}
|
@ -34,7 +34,7 @@ class UpdateInvoiceAPIRequest extends InvoiceRequest
|
|||||||
$invoiceId = $this->entity()->id;
|
$invoiceId = $this->entity()->id;
|
||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'invoice_items' => 'valid_invoice_items',
|
'invoice_items' => 'required|valid_invoice_items',
|
||||||
'invoice_number' => 'unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id,
|
'invoice_number' => 'unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id,
|
||||||
'discount' => 'positive',
|
'discount' => 'positive',
|
||||||
//'invoice_date' => 'date',
|
//'invoice_date' => 'date',
|
||||||
|
@ -31,7 +31,7 @@ class UpdateInvoiceRequest extends InvoiceRequest
|
|||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'client' => 'required',
|
'client' => 'required',
|
||||||
'invoice_items' => 'valid_invoice_items',
|
'invoice_items' => 'required|valid_invoice_items',
|
||||||
'invoice_number' => 'required|unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id,
|
'invoice_number' => 'required|unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id,
|
||||||
'discount' => 'positive',
|
'discount' => 'positive',
|
||||||
'invoice_date' => 'required',
|
'invoice_date' => 'required',
|
||||||
|
17
app/Http/Requests/UpdatePaymentTermRequest.php
Normal file
17
app/Http/Requests/UpdatePaymentTermRequest.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class UpdatePaymentTermRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
32
app/Http/Requests/UpdateProposalCategoryRequest.php
Normal file
32
app/Http/Requests/UpdateProposalCategoryRequest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class UpdateProposalCategoryRequest extends ProposalCategoryRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->entity() && $this->user()->can('edit', $this->entity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
if (! $this->entity()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => sprintf('required|unique:proposal_categories,name,,id,account_id,%s', $this->user()->account_id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
32
app/Http/Requests/UpdateProposalRequest.php
Normal file
32
app/Http/Requests/UpdateProposalRequest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class UpdateProposalRequest extends ProposalRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->entity() && $this->user()->can('edit', $this->entity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
if (! $this->entity()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'invoice_id' => 'required',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
32
app/Http/Requests/UpdateProposalSnippetRequest.php
Normal file
32
app/Http/Requests/UpdateProposalSnippetRequest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class UpdateProposalSnippetRequest extends ProposalSnippetRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->entity() && $this->user()->can('edit', $this->entity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
if (! $this->entity()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => sprintf('required|unique:proposal_snippets,name,%s,id,account_id,%s', $this->entity()->id, $this->user()->account_id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
32
app/Http/Requests/UpdateProposalTemplateRequest.php
Normal file
32
app/Http/Requests/UpdateProposalTemplateRequest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class UpdateProposalTemplateRequest extends ProposalTemplateRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->entity() && $this->user()->can('edit', $this->entity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
if (! $this->entity()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => sprintf('required|unique:proposal_templates,name,%s,id,account_id,%s', $this->entity()->id, $this->user()->account_id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
48
app/Http/ViewComposers/ProposalComposer.php
Normal file
48
app/Http/ViewComposers/ProposalComposer.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\ViewComposers;
|
||||||
|
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use App\Models\ProposalSnippet;
|
||||||
|
use App\Models\Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClientPortalHeaderComposer.php.
|
||||||
|
*
|
||||||
|
* @copyright See LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
class ProposalComposer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bind data to the view.
|
||||||
|
*
|
||||||
|
* @param View $view
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function compose(View $view)
|
||||||
|
{
|
||||||
|
$snippets = ProposalSnippet::scope()
|
||||||
|
->with('proposal_category')
|
||||||
|
->orderBy('name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$view->with('snippets', $snippets);
|
||||||
|
|
||||||
|
|
||||||
|
$documents = Document::scope()
|
||||||
|
->whereNull('invoice_id')
|
||||||
|
->whereNull('expense_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
foreach ($documents as $document) {
|
||||||
|
$data[] = [
|
||||||
|
'src' => $document->getProposalUrl(),
|
||||||
|
'public_id' => $document->public_id,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->with('documents', $data);
|
||||||
|
}
|
||||||
|
}
|
157
app/Jobs/ConvertInvoiceToUbl.php
Normal file
157
app/Jobs/ConvertInvoiceToUbl.php
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Jobs\Job;
|
||||||
|
use CleverIt\UBL\Invoice\Generator;
|
||||||
|
use CleverIt\UBL\Invoice\Invoice;
|
||||||
|
use CleverIt\UBL\Invoice\Party;
|
||||||
|
use CleverIt\UBL\Invoice\Address;
|
||||||
|
use CleverIt\UBL\Invoice\Country;
|
||||||
|
use CleverIt\UBL\Invoice\Contact;
|
||||||
|
use CleverIt\UBL\Invoice\TaxTotal;
|
||||||
|
use CleverIt\UBL\Invoice\TaxSubTotal;
|
||||||
|
use CleverIt\UBL\Invoice\TaxCategory;
|
||||||
|
use CleverIt\UBL\Invoice\TaxScheme;
|
||||||
|
use CleverIt\UBL\Invoice\InvoiceLine;
|
||||||
|
use CleverIt\UBL\Invoice\Item;
|
||||||
|
use CleverIt\UBL\Invoice\LegalMonetaryTotal;
|
||||||
|
|
||||||
|
class ConvertInvoiceToUbl extends Job
|
||||||
|
{
|
||||||
|
const INVOICE_TYPE_STANDARD = 380;
|
||||||
|
const INVOICE_TYPE_CREDIT = 381;
|
||||||
|
|
||||||
|
public function __construct($invoice)
|
||||||
|
{
|
||||||
|
$this->invoice = $invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$invoice = $this->invoice;
|
||||||
|
$account = $invoice->account;
|
||||||
|
$client = $invoice->client;
|
||||||
|
$ublInvoice = new Invoice();
|
||||||
|
|
||||||
|
// invoice
|
||||||
|
$ublInvoice->setId($invoice->invoice_number);
|
||||||
|
$ublInvoice->setIssueDate(date_create($invoice->invoice_date));
|
||||||
|
$ublInvoice->setInvoiceTypeCode($invoice->amount < 0 ? self::INVOICE_TYPE_CREDIT : self::INVOICE_TYPE_STANDARD);
|
||||||
|
|
||||||
|
$supplierParty = $this->createParty($account, $invoice->user);
|
||||||
|
$ublInvoice->setAccountingSupplierParty($supplierParty);
|
||||||
|
|
||||||
|
$customerParty = $this->createParty($client, $client->contacts[0]);
|
||||||
|
$ublInvoice->setAccountingCustomerParty($customerParty);
|
||||||
|
|
||||||
|
// line items
|
||||||
|
$invoiceLine = [];
|
||||||
|
$taxable = $invoice->getTaxable();
|
||||||
|
|
||||||
|
foreach ($invoice->invoice_items as $index => $item) {
|
||||||
|
$itemTaxable = $invoice->getItemTaxable($item, $taxable);
|
||||||
|
$item->setRelation('invoice', $invoice);
|
||||||
|
$invoiceLines[] = $this->createInvoiceLine($index, $item, $itemTaxable);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ublInvoice->setInvoiceLines($invoiceLines);
|
||||||
|
|
||||||
|
if ($invoice->hasTaxes()) {
|
||||||
|
$taxtotal = new TaxTotal();
|
||||||
|
$taxAmount1 = $taxAmount2 = 0;
|
||||||
|
|
||||||
|
if ($invoice->tax_name1 || floatval($invoice->tax_rate1)) {
|
||||||
|
$taxAmount1 = $this->createTaxRate($taxtotal, $taxable, $invoice->tax_rate1, $invoice->tax_name1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invoice->tax_name2 || floatval($invoice->tax_rate2)) {
|
||||||
|
$taxAmount2 = $this->createTaxRate($taxtotal, $taxable, $invoice->tax_rate2, $invoice->tax_name2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxtotal->setTaxAmount($taxAmount1 + $taxAmount2);
|
||||||
|
$ublInvoice->setTaxTotal($taxtotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ublInvoice->setLegalMonetaryTotal((new LegalMonetaryTotal())
|
||||||
|
//->setLineExtensionAmount()
|
||||||
|
->setTaxExclusiveAmount($taxable)
|
||||||
|
->setPayableAmount($invoice->balance));
|
||||||
|
|
||||||
|
return Generator::invoice($ublInvoice, $invoice->client->getCurrencyCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createParty($company, $user)
|
||||||
|
{
|
||||||
|
$party = new Party();
|
||||||
|
$party->setName($company->name);
|
||||||
|
$address = (new Address())
|
||||||
|
->setCityName($company->city)
|
||||||
|
->setStreetName($company->address1)
|
||||||
|
->setBuildingNumber($company->address2)
|
||||||
|
->setPostalZone($company->postal_code);
|
||||||
|
|
||||||
|
if ($company->country_id) {
|
||||||
|
$country = new Country();
|
||||||
|
$country->setIdentificationCode($company->country->iso_3166_2);
|
||||||
|
$address->setCountry($country);
|
||||||
|
}
|
||||||
|
|
||||||
|
$party->setPostalAddress($address);
|
||||||
|
$party->setPhysicalLocation($address);
|
||||||
|
|
||||||
|
$contact = new Contact();
|
||||||
|
$contact->setElectronicMail($user->email);
|
||||||
|
$party->setContact($contact);
|
||||||
|
|
||||||
|
return $party;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createInvoiceLine($index, $item, $taxable)
|
||||||
|
{
|
||||||
|
$invoiceLine = (new InvoiceLine())
|
||||||
|
->setId($index + 1)
|
||||||
|
->setInvoicedQuantity($item->qty)
|
||||||
|
->setLineExtensionAmount($item->costWithDiscount())
|
||||||
|
->setItem((new Item())
|
||||||
|
->setName($item->product_key)
|
||||||
|
->setDescription($item->description));
|
||||||
|
//->setSellersItemIdentification("1ABCD"));
|
||||||
|
|
||||||
|
if ($item->hasTaxes()) {
|
||||||
|
$taxtotal = new TaxTotal();
|
||||||
|
$itemTaxAmount1 = $itemTaxAmount2 = 0;
|
||||||
|
|
||||||
|
if ($item->tax_name1 || floatval($item->tax_rate1)) {
|
||||||
|
$itemTaxAmount1 = $this->createTaxRate($taxtotal, $taxable, $item->tax_rate1, $item->tax_name1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->tax_name2 || floatval($item->tax_rate2)) {
|
||||||
|
$itemTaxAmount2 = $this->createTaxRate($taxtotal, $taxable, $item->tax_rate2, $item->tax_name2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxtotal->setTaxAmount($itemTaxAmount1 + $itemTaxAmount2);
|
||||||
|
$invoiceLine->setTaxTotal($taxtotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $invoiceLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTaxRate(&$taxtotal, $taxable, $taxRate, $taxName)
|
||||||
|
{
|
||||||
|
$invoice = $this->invoice;
|
||||||
|
$taxAmount = $invoice->taxAmount($taxable, $taxRate);
|
||||||
|
$taxScheme = ((new TaxScheme()))->setId($taxName);
|
||||||
|
|
||||||
|
$taxtotal->addTaxSubTotal((new TaxSubTotal())
|
||||||
|
->setTaxAmount($taxAmount)
|
||||||
|
->setTaxableAmount($taxable)
|
||||||
|
->setTaxCategory((new TaxCategory())
|
||||||
|
->setId($taxName)
|
||||||
|
->setName($taxName)
|
||||||
|
->setTaxScheme($taxScheme)
|
||||||
|
->setPercent($taxRate)));
|
||||||
|
|
||||||
|
return $taxAmount;
|
||||||
|
}
|
||||||
|
}
|
@ -46,6 +46,10 @@ class DownloadInvoices extends Job
|
|||||||
*/
|
*/
|
||||||
public function handle(UserMailer $userMailer)
|
public function handle(UserMailer $userMailer)
|
||||||
{
|
{
|
||||||
|
if (! extension_loaded('GMP')) {
|
||||||
|
die(trans('texts.gmp_required'));
|
||||||
|
}
|
||||||
|
|
||||||
$zip = Archive::instance_by_useragent(date('Y-m-d') . '_' . str_replace(' ', '_', trans('texts.invoice_pdfs')));
|
$zip = Archive::instance_by_useragent(date('Y-m-d') . '_' . str_replace(' ', '_', trans('texts.invoice_pdfs')));
|
||||||
|
|
||||||
foreach ($this->invoices as $invoice) {
|
foreach ($this->invoices as $invoice) {
|
||||||
@ -54,34 +58,5 @@ class DownloadInvoices extends Job
|
|||||||
|
|
||||||
$zip->finish();
|
$zip->finish();
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
/*
|
|
||||||
// if queues are disabled download a zip file
|
|
||||||
if (config('queue.default') === 'sync' || count($this->invoices) <= 10) {
|
|
||||||
$zip = Archive::instance_by_useragent(date('Y-m-d') . '-Invoice_PDFs');
|
|
||||||
foreach ($this->invoices as $invoice) {
|
|
||||||
$zip->add_file($invoice->getFileName(), $invoice->getPDFString());
|
|
||||||
}
|
|
||||||
$zip->finish();
|
|
||||||
exit;
|
|
||||||
|
|
||||||
// otherwise sends the PDFs in an email
|
|
||||||
} else {
|
|
||||||
$data = [];
|
|
||||||
foreach ($this->invoices as $invoice) {
|
|
||||||
$data[] = [
|
|
||||||
'name' => $invoice->getFileName(),
|
|
||||||
'data' => $invoice->getPDFString(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$subject = trans('texts.invoices_are_attached');
|
|
||||||
$data = [
|
|
||||||
'documents' => $data
|
|
||||||
];
|
|
||||||
|
|
||||||
$userMailer->sendMessage($this->user, $subject, false, $data);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,11 @@ class PurgeAccountData extends Job
|
|||||||
'vendors',
|
'vendors',
|
||||||
'contacts',
|
'contacts',
|
||||||
'clients',
|
'clients',
|
||||||
|
'proposals',
|
||||||
|
'proposal_templates',
|
||||||
|
'proposal_snippets',
|
||||||
|
'proposal_categories',
|
||||||
|
'proposal_invitations',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
@ -71,6 +76,7 @@ class PurgeAccountData extends Job
|
|||||||
$lookupAccount = LookupAccount::whereAccountKey($account->account_key)->firstOrFail();
|
$lookupAccount = LookupAccount::whereAccountKey($account->account_key)->firstOrFail();
|
||||||
DB::table('lookup_contacts')->where('lookup_account_id', '=', $lookupAccount->id)->delete();
|
DB::table('lookup_contacts')->where('lookup_account_id', '=', $lookupAccount->id)->delete();
|
||||||
DB::table('lookup_invitations')->where('lookup_account_id', '=', $lookupAccount->id)->delete();
|
DB::table('lookup_invitations')->where('lookup_account_id', '=', $lookupAccount->id)->delete();
|
||||||
|
DB::table('lookup_proposal_invitations')->where('lookup_account_id', '=', $lookupAccount->id)->delete();
|
||||||
|
|
||||||
config(['database.default' => $current]);
|
config(['database.default' => $current]);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,11 @@ class SendInvoiceEmail extends Job implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
protected $server;
|
protected $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Proposal
|
||||||
|
*/
|
||||||
|
protected $proposal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
@ -51,12 +56,13 @@ class SendInvoiceEmail extends Job implements ShouldQueue
|
|||||||
* @param bool $reminder
|
* @param bool $reminder
|
||||||
* @param mixed $pdfString
|
* @param mixed $pdfString
|
||||||
*/
|
*/
|
||||||
public function __construct(Invoice $invoice, $userId = false, $reminder = false, $template = false)
|
public function __construct(Invoice $invoice, $userId = false, $reminder = false, $template = false, $proposal = false)
|
||||||
{
|
{
|
||||||
$this->invoice = $invoice;
|
$this->invoice = $invoice;
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->reminder = $reminder;
|
$this->reminder = $reminder;
|
||||||
$this->template = $template;
|
$this->template = $template;
|
||||||
|
$this->proposal = $proposal;
|
||||||
$this->server = config('database.default');
|
$this->server = config('database.default');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +78,7 @@ class SendInvoiceEmail extends Job implements ShouldQueue
|
|||||||
Auth::onceUsingId($this->userId);
|
Auth::onceUsingId($this->userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$mailer->sendInvoice($this->invoice, $this->reminder, $this->template);
|
$mailer->sendInvoice($this->invoice, $this->reminder, $this->template, $this->proposal);
|
||||||
|
|
||||||
if (App::runningInConsole() && $this->userId) {
|
if (App::runningInConsole() && $this->userId) {
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
|
@ -61,4 +61,17 @@ class HTMLUtils
|
|||||||
return $previous;
|
return $previous;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEnvForAccount($field, $default = '')
|
||||||
|
{
|
||||||
|
$key = '';
|
||||||
|
|
||||||
|
if ($user = auth()->user()) {
|
||||||
|
$key .= $user->account->id . '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
$key .= $field;
|
||||||
|
|
||||||
|
return env($key, env($field, $default));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ class HistoryUtils
|
|||||||
public static function loadHistory($users)
|
public static function loadHistory($users)
|
||||||
{
|
{
|
||||||
$userIds = [];
|
$userIds = [];
|
||||||
|
session([RECENTLY_VIEWED => false]);
|
||||||
|
|
||||||
if (is_array($users)) {
|
if (is_array($users)) {
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
@ -37,7 +38,7 @@ class HistoryUtils
|
|||||||
ACTIVITY_TYPE_VIEW_QUOTE,
|
ACTIVITY_TYPE_VIEW_QUOTE,
|
||||||
];
|
];
|
||||||
|
|
||||||
$activities = Activity::with(['client.contacts', 'invoice', 'task', 'expense'])
|
$activities = Activity::with(['client.contacts', 'invoice', 'task.project', 'expense'])
|
||||||
->whereIn('user_id', $userIds)
|
->whereIn('user_id', $userIds)
|
||||||
->whereIn('activity_type_id', $activityTypes)
|
->whereIn('activity_type_id', $activityTypes)
|
||||||
->orderBy('id', 'desc')
|
->orderBy('id', 'desc')
|
||||||
@ -53,6 +54,12 @@ class HistoryUtils
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$entity->setRelation('client', $activity->client);
|
$entity->setRelation('client', $activity->client);
|
||||||
|
|
||||||
|
if ($entity->project) {
|
||||||
|
$project = $entity->project;
|
||||||
|
$project->setRelation('client', $activity->client);
|
||||||
|
static::trackViewed($project);
|
||||||
|
}
|
||||||
} elseif ($activity->activity_type_id == ACTIVITY_TYPE_CREATE_EXPENSE || $activity->activity_type_id == ACTIVITY_TYPE_UPDATE_EXPENSE) {
|
} elseif ($activity->activity_type_id == ACTIVITY_TYPE_CREATE_EXPENSE || $activity->activity_type_id == ACTIVITY_TYPE_UPDATE_EXPENSE) {
|
||||||
$entity = $activity->expense;
|
$entity = $activity->expense;
|
||||||
if (! $entity) {
|
if (! $entity) {
|
||||||
@ -80,6 +87,8 @@ class HistoryUtils
|
|||||||
ENTITY_QUOTE,
|
ENTITY_QUOTE,
|
||||||
ENTITY_TASK,
|
ENTITY_TASK,
|
||||||
ENTITY_EXPENSE,
|
ENTITY_EXPENSE,
|
||||||
|
ENTITY_PROJECT,
|
||||||
|
ENTITY_PROPOSAL,
|
||||||
//ENTITY_RECURRING_EXPENSE,
|
//ENTITY_RECURRING_EXPENSE,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -87,6 +96,10 @@ class HistoryUtils
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($entity->is_deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$object = static::convertToObject($entity);
|
$object = static::convertToObject($entity);
|
||||||
$history = Session::get(RECENTLY_VIEWED) ?: [];
|
$history = Session::get(RECENTLY_VIEWED) ?: [];
|
||||||
$accountHistory = isset($history[$entity->account_id]) ? $history[$entity->account_id] : [];
|
$accountHistory = isset($history[$entity->account_id]) ? $history[$entity->account_id] : [];
|
||||||
@ -135,6 +148,9 @@ class HistoryUtils
|
|||||||
} elseif (method_exists($entity, 'client') && $entity->client) {
|
} elseif (method_exists($entity, 'client') && $entity->client) {
|
||||||
$object->client_id = $entity->client->public_id;
|
$object->client_id = $entity->client->public_id;
|
||||||
$object->client_name = $entity->client->getDisplayName();
|
$object->client_name = $entity->client->getDisplayName();
|
||||||
|
} elseif (method_exists($entity, 'invoice') && $entity->invoice) {
|
||||||
|
$object->client_id = $entity->invoice->client->public_id;
|
||||||
|
$object->client_name = $entity->invoice->client->getDisplayName();
|
||||||
} else {
|
} else {
|
||||||
$object->client_id = 0;
|
$object->client_id = 0;
|
||||||
$object->client_name = 0;
|
$object->client_name = 0;
|
||||||
@ -175,7 +191,8 @@ class HistoryUtils
|
|||||||
$button = '';
|
$button = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$str .= sprintf('<li>%s<a href="%s"><div>%s %s</div></a></li>', $button, $link, $icon, $name);
|
$padding = $str ? 16 : 0;
|
||||||
|
$str .= sprintf('<li style="margin-top: %spx">%s<a href="%s"><div>%s %s</div></a></li>', $padding, $button, $link, $icon, $name);
|
||||||
$lastClientId = $item->client_id;
|
$lastClientId = $item->client_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +364,9 @@ class Utils
|
|||||||
if ($field == 'checkbox') {
|
if ($field == 'checkbox') {
|
||||||
$data[] = $field;
|
$data[] = $field;
|
||||||
} elseif ($field) {
|
} elseif ($field) {
|
||||||
if ($module) {
|
if (substr($field, 0, 1) == '-') {
|
||||||
|
$data[] = substr($field, 1);
|
||||||
|
} elseif ($module) {
|
||||||
$data[] = mtrans($module, $field);
|
$data[] = mtrans($module, $field);
|
||||||
} else {
|
} else {
|
||||||
$data[] = trans("texts.$field");
|
$data[] = trans("texts.$field");
|
||||||
@ -564,6 +566,10 @@ class Utils
|
|||||||
|
|
||||||
if ($type === ENTITY_EXPENSE_CATEGORY) {
|
if ($type === ENTITY_EXPENSE_CATEGORY) {
|
||||||
return 'expense_categories';
|
return 'expense_categories';
|
||||||
|
} elseif ($type === ENTITY_PROPOSAL_CATEGORY) {
|
||||||
|
return 'proposal_categories';
|
||||||
|
} elseif ($type === ENTITY_TASK_STATUS) {
|
||||||
|
return 'task_statuses';
|
||||||
} else {
|
} else {
|
||||||
return $type . 's';
|
return $type . 's';
|
||||||
}
|
}
|
||||||
@ -1087,6 +1093,25 @@ class Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getCustomLabel($value)
|
||||||
|
{
|
||||||
|
if (strpos($value, '|') !== false) {
|
||||||
|
return explode('|', $value)[0];
|
||||||
|
} else {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCustomValues($value)
|
||||||
|
{
|
||||||
|
if (strpos($value, '|') !== false) {
|
||||||
|
$values = explode(',', explode('|', $value)[1]);
|
||||||
|
return array_combine($values, $values);
|
||||||
|
} else {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function formatWebsite($link)
|
public static function formatWebsite($link)
|
||||||
{
|
{
|
||||||
if (! $link) {
|
if (! $link) {
|
||||||
@ -1260,7 +1285,7 @@ class Utils
|
|||||||
$tax1 = round($amount * $taxRate1 / 100, 2);
|
$tax1 = round($amount * $taxRate1 / 100, 2);
|
||||||
$tax2 = round($amount * $taxRate2 / 100, 2);
|
$tax2 = round($amount * $taxRate2 / 100, 2);
|
||||||
|
|
||||||
return round($amount + $tax1 + $tax2, 2);
|
return round($tax1 + $tax2, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function roundSignificant($value, $precision = 2) {
|
public static function roundSignificant($value, $precision = 2) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Listeners;
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Events\SubdomainWasRemoved;
|
||||||
use App\Events\SubdomainWasUpdated;
|
use App\Events\SubdomainWasUpdated;
|
||||||
use App\Ninja\DNS\Cloudflare;
|
use App\Ninja\DNS\Cloudflare;
|
||||||
|
|
||||||
@ -19,4 +20,11 @@ class DNSListener
|
|||||||
if(env("CLOUDFLARE_DNS_ENABLED"))
|
if(env("CLOUDFLARE_DNS_ENABLED"))
|
||||||
Cloudflare::addDNSRecord($event->account);
|
Cloudflare::addDNSRecord($event->account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeDNSRecord(SubdomainWasRemoved $event)
|
||||||
|
{
|
||||||
|
if(env("CLOUDFLARE_DNS_ENABLED"))
|
||||||
|
Cloudflare::removeDNSRecord($event->account);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ class HandleUserLoggedIn
|
|||||||
if (in_array(config('app.key'), ['SomeRandomString', 'SomeRandomStringSomeRandomString', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'])) {
|
if (in_array(config('app.key'), ['SomeRandomString', 'SomeRandomStringSomeRandomString', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'])) {
|
||||||
Session::flash('error', trans('texts.error_app_key_set_to_default'));
|
Session::flash('error', trans('texts.error_app_key_set_to_default'));
|
||||||
} elseif (in_array($appCipher, ['MCRYPT_RIJNDAEL_256', 'MCRYPT_RIJNDAEL_128'])) {
|
} elseif (in_array($appCipher, ['MCRYPT_RIJNDAEL_256', 'MCRYPT_RIJNDAEL_128'])) {
|
||||||
Session::flash('error', trans('texts.mcrypt_warning'));
|
Session::flash('error', trans('texts.mcrypt_warning', ['command' => '<code>php artisan ninja:update-key --legacy=true</code>']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,8 @@ class HandleUserSettingsChanged
|
|||||||
|
|
||||||
if ($event->user && $event->user->isEmailBeingChanged()) {
|
if ($event->user && $event->user->isEmailBeingChanged()) {
|
||||||
$this->userMailer->sendConfirmation($event->user);
|
$this->userMailer->sendConfirmation($event->user);
|
||||||
|
$this->userMailer->sendEmailChanged($event->user);
|
||||||
|
|
||||||
Session::flash('warning', trans('texts.verify_email'));
|
Session::flash('warning', trans('texts.verify_email'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,19 +254,30 @@ class SubscriptionListener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate JSON data
|
||||||
$manager = new Manager();
|
$manager = new Manager();
|
||||||
$manager->setSerializer(new ArraySerializer());
|
$manager->setSerializer(new ArraySerializer());
|
||||||
$manager->parseIncludes($include);
|
$manager->parseIncludes($include);
|
||||||
|
|
||||||
$resource = new Item($entity, $transformer, $entity->getEntityType());
|
$resource = new Item($entity, $transformer, $entity->getEntityType());
|
||||||
$data = $manager->createData($resource)->toArray();
|
$jsonData = $manager->createData($resource)->toArray();
|
||||||
|
|
||||||
// For legacy Zapier support
|
// For legacy Zapier support
|
||||||
if (isset($data['client_id'])) {
|
if (isset($jsonData['client_id'])) {
|
||||||
$data['client_name'] = $entity->client->getDisplayName();
|
$jsonData['client_name'] = $entity->client->getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach ($subscriptions as $subscription) {
|
foreach ($subscriptions as $subscription) {
|
||||||
|
switch ($subscription->format) {
|
||||||
|
case SUBSCRIPTION_FORMAT_JSON:
|
||||||
|
$data = $jsonData;
|
||||||
|
break;
|
||||||
|
case SUBSCRIPTION_FORMAT_UBL:
|
||||||
|
$data = $ublData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
self::notifySubscription($subscription, $data);
|
self::notifySubscription($subscription, $data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,7 @@ class Account extends Eloquent
|
|||||||
'invoice_fields',
|
'invoice_fields',
|
||||||
'invoice_embed_documents',
|
'invoice_embed_documents',
|
||||||
'document_email_attachment',
|
'document_email_attachment',
|
||||||
|
'ubl_email_attachment',
|
||||||
'enable_client_portal_dashboard',
|
'enable_client_portal_dashboard',
|
||||||
'page_size',
|
'page_size',
|
||||||
'live_preview',
|
'live_preview',
|
||||||
@ -237,6 +238,8 @@ class Account extends Eloquent
|
|||||||
'hours',
|
'hours',
|
||||||
'id_number',
|
'id_number',
|
||||||
'invoice',
|
'invoice',
|
||||||
|
'invoice_date',
|
||||||
|
'invoice_number',
|
||||||
'item',
|
'item',
|
||||||
'line_total',
|
'line_total',
|
||||||
'outstanding',
|
'outstanding',
|
||||||
@ -245,6 +248,8 @@ class Account extends Eloquent
|
|||||||
'po_number',
|
'po_number',
|
||||||
'quantity',
|
'quantity',
|
||||||
'quote',
|
'quote',
|
||||||
|
'quote_date',
|
||||||
|
'quote_number',
|
||||||
'rate',
|
'rate',
|
||||||
'service',
|
'service',
|
||||||
'subtotal',
|
'subtotal',
|
||||||
@ -500,7 +505,7 @@ class Account extends Eloquent
|
|||||||
if ($gatewayId) {
|
if ($gatewayId) {
|
||||||
return $this->getGatewayConfig($gatewayId) != false;
|
return $this->getGatewayConfig($gatewayId) != false;
|
||||||
} else {
|
} else {
|
||||||
return count($this->account_gateways) > 0;
|
return $this->account_gateways->count() > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1484,6 +1489,14 @@ class Account extends Eloquent
|
|||||||
return $this->hasFeature(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment;
|
return $this->hasFeature(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function attachUBL()
|
||||||
|
{
|
||||||
|
return $this->hasFeature(FEATURE_PDF_ATTACHMENT) && $this->ubl_email_attachment;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
@ -1643,6 +1656,7 @@ class Account extends Eloquent
|
|||||||
ENTITY_EXPENSE,
|
ENTITY_EXPENSE,
|
||||||
ENTITY_VENDOR,
|
ENTITY_VENDOR,
|
||||||
ENTITY_PROJECT,
|
ENTITY_PROJECT,
|
||||||
|
ENTITY_PROPOSAL,
|
||||||
])) {
|
])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1651,6 +1665,8 @@ class Account extends Eloquent
|
|||||||
$entityType = ENTITY_EXPENSE;
|
$entityType = ENTITY_EXPENSE;
|
||||||
} elseif ($entityType == ENTITY_PROJECT) {
|
} elseif ($entityType == ENTITY_PROJECT) {
|
||||||
$entityType = ENTITY_TASK;
|
$entityType = ENTITY_TASK;
|
||||||
|
} elseif ($entityType == ENTITY_PROPOSAL) {
|
||||||
|
$entityType = ENTITY_QUOTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: single & checks bitmask match
|
// note: single & checks bitmask match
|
||||||
|
@ -38,6 +38,7 @@ class AccountEmailSettings extends Eloquent
|
|||||||
public static $templates = [
|
public static $templates = [
|
||||||
TEMPLATE_INVOICE,
|
TEMPLATE_INVOICE,
|
||||||
TEMPLATE_QUOTE,
|
TEMPLATE_QUOTE,
|
||||||
|
TEMPLATE_PROPOSAL,
|
||||||
//TEMPLATE_PARTIAL,
|
//TEMPLATE_PARTIAL,
|
||||||
TEMPLATE_PAYMENT,
|
TEMPLATE_PAYMENT,
|
||||||
TEMPLATE_REMINDER1,
|
TEMPLATE_REMINDER1,
|
||||||
|
@ -268,4 +268,13 @@ class AccountGateway extends EntityModel
|
|||||||
|
|
||||||
return \URL::to(env('WEBHOOK_PREFIX', '').'payment_hook/'.$account->account_key.'/'.$this->gateway_id.env('WEBHOOK_SUFFIX', ''));
|
return \URL::to(env('WEBHOOK_PREFIX', '').'payment_hook/'.$account->account_key.'/'.$this->gateway_id.env('WEBHOOK_SUFFIX', ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isTestMode()
|
||||||
|
{
|
||||||
|
if ($this->isGateway(GATEWAY_STRIPE)) {
|
||||||
|
return strpos($this->getPublishableStripeKey(), 'test') !== false;
|
||||||
|
} else {
|
||||||
|
return $this->getConfigField('testMode');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,7 +351,7 @@ class Client extends EntityModel
|
|||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! count($this->contacts)) {
|
if (! $this->contacts->count()) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,6 +386,29 @@ class Client extends EntityModel
|
|||||||
return $this->hasAddress() && env('GOOGLE_MAPS_ENABLED') !== false;
|
return $this->hasAddress() && env('GOOGLE_MAPS_ENABLED') !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function addressesMatch()
|
||||||
|
{
|
||||||
|
$fields = [
|
||||||
|
'address1',
|
||||||
|
'address2',
|
||||||
|
'city',
|
||||||
|
'state',
|
||||||
|
'postal_code',
|
||||||
|
'country_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
if ($this->$field != $this->{'shipping_' . $field}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
@ -131,6 +131,21 @@ class Contact extends EntityModel implements AuthenticatableContract, CanResetPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function getSearchName()
|
||||||
|
{
|
||||||
|
$name = $this->getFullName();
|
||||||
|
$email = $this->email;
|
||||||
|
|
||||||
|
if ($name && $email) {
|
||||||
|
return sprintf('%s <%s>', $name, $email);
|
||||||
|
} else {
|
||||||
|
return $name ?: $email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $contact_key
|
* @param $contact_key
|
||||||
*
|
*
|
||||||
|
@ -41,6 +41,6 @@ class Country extends Eloquent
|
|||||||
*/
|
*/
|
||||||
public function getName()
|
public function getName()
|
||||||
{
|
{
|
||||||
return $this->name;
|
return trans('texts.country_' . $this->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,6 +272,15 @@ class Document extends EntityModel
|
|||||||
return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name);
|
return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProposalUrl()
|
||||||
|
{
|
||||||
|
if (! $this->is_proposal || ! $this->document_key) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return url('proposal/image/'. $this->account->account_key . '/' . $this->document_key . '/' . $this->name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
@ -321,6 +321,7 @@ class EntityModel extends Eloquent
|
|||||||
'recurring_expenses' => 'files-o',
|
'recurring_expenses' => 'files-o',
|
||||||
'credits' => 'credit-card',
|
'credits' => 'credit-card',
|
||||||
'quotes' => 'file-text-o',
|
'quotes' => 'file-text-o',
|
||||||
|
'proposals' => 'th-large',
|
||||||
'tasks' => 'clock-o',
|
'tasks' => 'clock-o',
|
||||||
'expenses' => 'file-image-o',
|
'expenses' => 'file-image-o',
|
||||||
'vendors' => 'building',
|
'vendors' => 'building',
|
||||||
@ -354,6 +355,15 @@ class EntityModel extends Eloquent
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getFormUrl($entityType)
|
||||||
|
{
|
||||||
|
if (in_array($entityType, [ENTITY_PROPOSAL_CATEGORY, ENTITY_PROPOSAL_SNIPPET, ENTITY_PROPOSAL_TEMPLATE])) {
|
||||||
|
return str_replace('_', 's/', Utils::pluralizeEntityType($entityType));
|
||||||
|
} else {
|
||||||
|
return Utils::pluralizeEntityType($entityType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function getStates($entityType = false)
|
public static function getStates($entityType = false)
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
@ -61,6 +61,7 @@ class Expense extends EntityModel
|
|||||||
'vendor',
|
'vendor',
|
||||||
'amount',
|
'amount',
|
||||||
'public_notes',
|
'public_notes',
|
||||||
|
'private_notes',
|
||||||
'expense_category',
|
'expense_category',
|
||||||
'expense_date',
|
'expense_date',
|
||||||
];
|
];
|
||||||
@ -73,7 +74,8 @@ class Expense extends EntityModel
|
|||||||
'category' => 'expense_category',
|
'category' => 'expense_category',
|
||||||
'client' => 'client',
|
'client' => 'client',
|
||||||
'vendor' => 'vendor',
|
'vendor' => 'vendor',
|
||||||
'notes|details' => 'public_notes',
|
'notes|details^private' => 'public_notes',
|
||||||
|
'notes|details^public' => 'private_notes',
|
||||||
'date' => 'expense_date',
|
'date' => 'expense_date',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -253,6 +255,11 @@ class Expense extends EntityModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function amountWithTax()
|
public function amountWithTax()
|
||||||
|
{
|
||||||
|
return $this->amount + $this->taxAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function taxAmount()
|
||||||
{
|
{
|
||||||
return Utils::calculateTaxes($this->amount, $this->tax_rate1, $this->tax_rate2);
|
return Utils::calculateTaxes($this->amount, $this->tax_rate1, $this->tax_rate2);
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Carbon;
|
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Utils;
|
|
||||||
use App\Models\LookupInvitation;
|
use App\Models\LookupInvitation;
|
||||||
|
use App\Models\Traits\Inviteable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Invitation.
|
* Class Invitation.
|
||||||
@ -13,6 +12,8 @@ use App\Models\LookupInvitation;
|
|||||||
class Invitation extends EntityModel
|
class Invitation extends EntityModel
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
use Inviteable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -58,102 +59,6 @@ class Invitation extends EntityModel
|
|||||||
return $this->belongsTo('App\Models\Account');
|
return $this->belongsTo('App\Models\Account');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're getting the link for PhantomJS to generate the PDF
|
|
||||||
// we need to make sure it's served from our site
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $type
|
|
||||||
* @param bool $forceOnsite
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLink($type = 'view', $forceOnsite = false, $forcePlain = false)
|
|
||||||
{
|
|
||||||
if (! $this->account) {
|
|
||||||
$this->load('account');
|
|
||||||
}
|
|
||||||
|
|
||||||
$account = $this->account;
|
|
||||||
$iframe_url = $account->iframe_url;
|
|
||||||
$url = trim(SITE_URL, '/');
|
|
||||||
|
|
||||||
if (env('REQUIRE_HTTPS')) {
|
|
||||||
$url = str_replace('http://', 'https://', $url);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($account->hasFeature(FEATURE_CUSTOM_URL)) {
|
|
||||||
if (Utils::isNinjaProd() && ! Utils::isReseller()) {
|
|
||||||
$url = $account->present()->clientPortalLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($iframe_url && ! $forceOnsite) {
|
|
||||||
return "{$iframe_url}?{$this->invitation_key}";
|
|
||||||
} elseif ($this->account->subdomain && ! $forcePlain) {
|
|
||||||
$url = Utils::replaceSubdomain($url, $account->subdomain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "{$url}/{$type}/{$this->invitation_key}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool|string
|
|
||||||
*/
|
|
||||||
public function getStatus()
|
|
||||||
{
|
|
||||||
$hasValue = false;
|
|
||||||
$parts = [];
|
|
||||||
$statuses = $this->message_id ? ['sent', 'opened', 'viewed'] : ['sent', 'viewed'];
|
|
||||||
|
|
||||||
foreach ($statuses as $status) {
|
|
||||||
$field = "{$status}_date";
|
|
||||||
$date = '';
|
|
||||||
if ($this->$field && $this->field != '0000-00-00 00:00:00') {
|
|
||||||
$date = Utils::dateToString($this->$field);
|
|
||||||
$hasValue = true;
|
|
||||||
$parts[] = trans('texts.invitation_status_' . $status) . ': ' . $date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $hasValue ? implode($parts, '<br/>') : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
|
||||||
return $this->invitation_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param null $messageId
|
|
||||||
*/
|
|
||||||
public function markSent($messageId = null)
|
|
||||||
{
|
|
||||||
$this->message_id = $messageId;
|
|
||||||
$this->email_error = null;
|
|
||||||
$this->sent_date = Carbon::now()->toDateTimeString();
|
|
||||||
$this->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isSent()
|
|
||||||
{
|
|
||||||
return $this->sent_date && $this->sent_date != '0000-00-00 00:00:00';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function markViewed()
|
|
||||||
{
|
|
||||||
$invoice = $this->invoice;
|
|
||||||
$client = $invoice->client;
|
|
||||||
|
|
||||||
$this->viewed_date = Carbon::now()->toDateTimeString();
|
|
||||||
$this->save();
|
|
||||||
|
|
||||||
$invoice->markViewed();
|
|
||||||
$client->markLoggedIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function signatureDiv()
|
public function signatureDiv()
|
||||||
{
|
{
|
||||||
if (! $this->signature_base64) {
|
if (! $this->signature_base64) {
|
||||||
|
@ -451,6 +451,23 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
->where('is_recurring', '=', false);
|
->where('is_recurring', '=', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $query
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function scopeUnapprovedQuotes($query, $includeInvoiceId = false)
|
||||||
|
{
|
||||||
|
return $query->quotes()
|
||||||
|
->where(function ($query) use ($includeInvoiceId) {
|
||||||
|
$query->whereId($includeInvoiceId)
|
||||||
|
->orWhere(function ($query) {
|
||||||
|
$query->where('invoice_status_id', '<', INVOICE_STATUS_APPROVED)
|
||||||
|
->whereNull('quote_invoice_id');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $query
|
* @param $query
|
||||||
* @param $typeId
|
* @param $typeId
|
||||||
@ -710,11 +727,11 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFileName()
|
public function getFileName($extension = 'pdf')
|
||||||
{
|
{
|
||||||
$entityType = $this->getEntityType();
|
$entityType = $this->getEntityType();
|
||||||
|
|
||||||
return trans("texts.$entityType") . '_' . $this->invoice_number . '.pdf';
|
return trans("texts.$entityType") . '_' . $this->invoice_number . '.' . $extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -841,6 +858,14 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
return $this->invoice_status_id >= INVOICE_STATUS_VIEWED;
|
return $this->invoice_status_id >= INVOICE_STATUS_VIEWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isApproved()
|
||||||
|
{
|
||||||
|
return $this->invoice_status_id >= INVOICE_STATUS_APPROVED || $this->quote_invoice_id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -1403,21 +1428,12 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$paidAmount = $this->getAmountPaid($calculatePaid);
|
$paidAmount = $this->getAmountPaid($calculatePaid);
|
||||||
|
|
||||||
if ($this->tax_name1) {
|
if ($this->tax_name1) {
|
||||||
if ($account->inclusive_taxes) {
|
$invoiceTaxAmount = $this->taxAmount($taxable, $this->tax_rate1);
|
||||||
$invoiceTaxAmount = round($taxable - ($taxable / (1 + ($this->tax_rate1 / 100))), 2);
|
|
||||||
} else {
|
|
||||||
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
|
|
||||||
}
|
|
||||||
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
||||||
$this->calculateTax($taxes, $this->tax_name1, $this->tax_rate1, $invoiceTaxAmount, $invoicePaidAmount);
|
$this->calculateTax($taxes, $this->tax_name1, $this->tax_rate1, $invoiceTaxAmount, $invoicePaidAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->tax_name2) {
|
if ($this->tax_name2) {
|
||||||
if ($account->inclusive_taxes) {
|
$invoiceTaxAmount = $this->taxAmount($taxable, $this->tax_rate2);
|
||||||
$invoiceTaxAmount = round($taxable - ($taxable / (1 + ($this->tax_rate2 / 100))), 2);
|
|
||||||
} else {
|
|
||||||
$invoiceTaxAmount = round($taxable * ($this->tax_rate2 / 100), 2);
|
|
||||||
}
|
|
||||||
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
||||||
$this->calculateTax($taxes, $this->tax_name2, $this->tax_rate2, $invoiceTaxAmount, $invoicePaidAmount);
|
$this->calculateTax($taxes, $this->tax_name2, $this->tax_rate2, $invoiceTaxAmount, $invoicePaidAmount);
|
||||||
}
|
}
|
||||||
@ -1426,21 +1442,12 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$itemTaxable = $this->getItemTaxable($invoiceItem, $taxable);
|
$itemTaxable = $this->getItemTaxable($invoiceItem, $taxable);
|
||||||
|
|
||||||
if ($invoiceItem->tax_name1) {
|
if ($invoiceItem->tax_name1) {
|
||||||
if ($account->inclusive_taxes) {
|
$itemTaxAmount = $this->taxAmount($itemTaxable, $invoiceItem->tax_rate1);
|
||||||
$itemTaxAmount = round($taxable - ($taxable / (1 + ($invoiceItem->tax_rate1 / 100))), 2);
|
|
||||||
} else {
|
|
||||||
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate1 / 100), 2);
|
|
||||||
}
|
|
||||||
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
||||||
$this->calculateTax($taxes, $invoiceItem->tax_name1, $invoiceItem->tax_rate1, $itemTaxAmount, $itemPaidAmount);
|
$this->calculateTax($taxes, $invoiceItem->tax_name1, $invoiceItem->tax_rate1, $itemTaxAmount, $itemPaidAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($invoiceItem->tax_name2) {
|
if ($invoiceItem->tax_name2) {
|
||||||
if ($account->inclusive_taxes) {
|
$itemTaxAmount = $this->taxAmount($itemTaxable, $invoiceItem->tax_rate2);
|
||||||
$itemTaxAmount = round($taxable - ($taxable / (1 + ($invoiceItem->tax_rate2 / 100))), 2);
|
|
||||||
} else {
|
|
||||||
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate2 / 100), 2);
|
|
||||||
}
|
|
||||||
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
||||||
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
|
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
|
||||||
}
|
}
|
||||||
@ -1449,6 +1456,28 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
return $taxes;
|
return $taxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTaxTotal()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
foreach ($this->getTaxes() as $tax) {
|
||||||
|
$total += $tax['amount'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function taxAmount($taxable, $rate)
|
||||||
|
{
|
||||||
|
$account = $this->account;
|
||||||
|
|
||||||
|
if ($account->inclusive_taxes) {
|
||||||
|
return round($taxable - ($taxable / (1 + ($rate / 100))), 2);
|
||||||
|
} else {
|
||||||
|
return round($taxable * ($rate / 100), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $taxes
|
* @param $taxes
|
||||||
* @param $name
|
* @param $name
|
||||||
@ -1484,18 +1513,18 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
*/
|
*/
|
||||||
public function countDocuments($expenses = false)
|
public function countDocuments($expenses = false)
|
||||||
{
|
{
|
||||||
$count = count($this->documents);
|
$count = $this->documents->count();
|
||||||
|
|
||||||
foreach ($this->expenses as $expense) {
|
foreach ($this->expenses as $expense) {
|
||||||
if ($expense->invoice_documents) {
|
if ($expense->invoice_documents) {
|
||||||
$count += count($expense->documents);
|
$count += $expense->documents->count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expenses) {
|
if ($expenses) {
|
||||||
foreach ($expenses as $expense) {
|
foreach ($expenses as $expense) {
|
||||||
if ($expense->invoice_documents) {
|
if ($expense->invoice_documents) {
|
||||||
$count += count($expense->documents);
|
$count += $expense->documents->count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1525,7 +1554,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
public function hasExpenseDocuments()
|
public function hasExpenseDocuments()
|
||||||
{
|
{
|
||||||
foreach ($this->expenses as $expense) {
|
foreach ($this->expenses as $expense) {
|
||||||
if ($expense->invoice_documents && count($expense->documents)) {
|
if ($expense->invoice_documents && $expense->documents->count()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1606,6 +1635,28 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasTaxes()
|
||||||
|
{
|
||||||
|
if ($this->tax_name1 || $this->tax_rate1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->tax_name2 || $this->tax_rate2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLocked()
|
||||||
|
{
|
||||||
|
if (! config('ninja.lock_sent_invoices')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->isSent() && ! $this->is_recurring;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoice::creating(function ($invoice) {
|
Invoice::creating(function ($invoice) {
|
||||||
|
@ -107,4 +107,33 @@ class InvoiceItem extends EntityModel
|
|||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasTaxes()
|
||||||
|
{
|
||||||
|
if ($this->tax_name1 || $this->tax_rate1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->tax_name2 || $this->tax_rate2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function costWithDiscount()
|
||||||
|
{
|
||||||
|
$cost = $this->cost;
|
||||||
|
|
||||||
|
if ($this->discount != 0) {
|
||||||
|
if ($this->invoice->is_amount_discount) {
|
||||||
|
$cost -= $discount / $this->qty;
|
||||||
|
} else {
|
||||||
|
$cost -= $cost * $discount / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cost;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
47
app/Models/LookupProposalInvitation.php
Normal file
47
app/Models/LookupProposalInvitation.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Eloquent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class LookupProposalInvitation extends LookupModel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'lookup_account_id',
|
||||||
|
'invitation_key',
|
||||||
|
'message_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function updateInvitation($accountKey, $invitation)
|
||||||
|
{
|
||||||
|
if (! env('MULTI_DB_ENABLED')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $invitation->message_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = config('database.default');
|
||||||
|
config(['database.default' => DB_NINJA_LOOKUP]);
|
||||||
|
|
||||||
|
$lookupAccount = LookupAccount::whereAccountKey($accountKey)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$lookupInvitation = LookupProposalInvitation::whereLookupAccountId($lookupAccount->id)
|
||||||
|
->whereInvitationKey($invitation->invitation_key)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$lookupInvitation->message_id = $invitation->message_id;
|
||||||
|
$lookupInvitation->save();
|
||||||
|
|
||||||
|
config(['database.default' => $current]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -47,6 +47,8 @@ class Product extends EntityModel
|
|||||||
'product_key',
|
'product_key',
|
||||||
'notes',
|
'notes',
|
||||||
'cost',
|
'cost',
|
||||||
|
'custom_value1',
|
||||||
|
'custom_value2',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +61,8 @@ class Product extends EntityModel
|
|||||||
'product|item' => 'product_key',
|
'product|item' => 'product_key',
|
||||||
'notes|description|details' => 'notes',
|
'notes|description|details' => 'notes',
|
||||||
'cost|amount|price' => 'cost',
|
'cost|amount|price' => 'cost',
|
||||||
|
'custom_value1' => 'custom_value1',
|
||||||
|
'custom_value2' => 'custom_value2',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
107
app/Models/Proposal.php
Normal file
107
app/Models/Proposal.php
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class Proposal extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $presenter = 'App\Ninja\Presenters\ProposalPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'private_notes',
|
||||||
|
'html',
|
||||||
|
'css',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposals/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function invoice()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Invoice')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function invitations()
|
||||||
|
{
|
||||||
|
return $this->hasMany('App\Models\ProposalInvitation')->orderBy('proposal_invitations.contact_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function proposal_invitations()
|
||||||
|
{
|
||||||
|
return $this->hasMany('App\Models\ProposalInvitation')->orderBy('proposal_invitations.contact_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function proposal_template()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\ProposalTemplate')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->invoice->invoice_number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
71
app/Models/ProposalCategory.php
Normal file
71
app/Models/ProposalCategory.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalCategory extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposals/categories/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
85
app/Models/ProposalInvitation.php
Normal file
85
app/Models/ProposalInvitation.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use App\Models\LookupProposalInvitation;
|
||||||
|
use App\Models\Traits\Inviteable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Invitation.
|
||||||
|
*/
|
||||||
|
class ProposalInvitation extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use Inviteable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_INVITATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function proposal()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Proposal')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function contact()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Contact')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\User')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProposalInvitation::creating(function ($invitation)
|
||||||
|
{
|
||||||
|
LookupProposalInvitation::createNew($invitation->account->account_key, [
|
||||||
|
'invitation_key' => $invitation->invitation_key,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
ProposalInvitation::updating(function ($invitation)
|
||||||
|
{
|
||||||
|
$dirty = $invitation->getDirty();
|
||||||
|
if (array_key_exists('message_id', $dirty)) {
|
||||||
|
LookupProposalInvitation::updateInvitation($invitation->account->account_key, $invitation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ProposalInvitation::deleted(function ($invitation)
|
||||||
|
{
|
||||||
|
if ($invitation->forceDeleting) {
|
||||||
|
LookupProposalInvitation::deleteWhere([
|
||||||
|
'invitation_key' => $invitation->invitation_key,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
84
app/Models/ProposalSnippet.php
Normal file
84
app/Models/ProposalSnippet.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalSnippet extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'icon',
|
||||||
|
'private_notes',
|
||||||
|
'proposal_category_id',
|
||||||
|
'html',
|
||||||
|
'css',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $presenter = 'App\Ninja\Presenters\ProposalSnippetPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposals/snippets/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function proposal_category()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\ProposalCategory')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
74
app/Models/ProposalTemplate.php
Normal file
74
app/Models/ProposalTemplate.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalTemplate extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'private_notes',
|
||||||
|
'html',
|
||||||
|
'css',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $presenter = 'App\Ninja\Presenters\ProposalTemplatePresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposals/templates/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
@ -129,7 +129,7 @@ class RecurringExpense extends EntityModel
|
|||||||
|
|
||||||
public function amountWithTax()
|
public function amountWithTax()
|
||||||
{
|
{
|
||||||
return Utils::calculateTaxes($this->amount, $this->tax_rate1, $this->tax_rate2);
|
return $this->amount + Utils::calculateTaxes($this->amount, $this->tax_rate1, $this->tax_rate2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class Subscription extends EntityModel
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'event_id',
|
'event_id',
|
||||||
'target_url',
|
'target_url',
|
||||||
|
'format',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,17 +129,27 @@ class Task extends EntityModel
|
|||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public static function calcDuration($task)
|
public static function calcDuration($task, $startTimeCutoff = 0, $endTimeCutoff = 0)
|
||||||
{
|
{
|
||||||
$duration = 0;
|
$duration = 0;
|
||||||
$parts = json_decode($task->time_log) ?: [];
|
$parts = json_decode($task->time_log) ?: [];
|
||||||
|
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
|
$startTime = $part[0];
|
||||||
if (count($part) == 1 || ! $part[1]) {
|
if (count($part) == 1 || ! $part[1]) {
|
||||||
$duration += time() - $part[0];
|
$endTime = time();
|
||||||
} else {
|
} else {
|
||||||
$duration += $part[1] - $part[0];
|
$endTime = $part[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($startTimeCutoff) {
|
||||||
|
$startTime = max($startTime, $startTimeCutoff);
|
||||||
|
}
|
||||||
|
if ($endTimeCutoff) {
|
||||||
|
$endTime = min($endTime, $endTimeCutoff);
|
||||||
|
}
|
||||||
|
|
||||||
|
$duration += $endTime - $startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $duration;
|
return $duration;
|
||||||
@ -148,9 +158,9 @@ class Task extends EntityModel
|
|||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getDuration()
|
public function getDuration($startTimeCutoff = 0, $endTimeCutoff = 0)
|
||||||
{
|
{
|
||||||
return self::calcDuration($this);
|
return self::calcDuration($this, $startTimeCutoff, $endTimeCutoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,8 +240,11 @@ class Task extends EntityModel
|
|||||||
|
|
||||||
public function scopeDateRange($query, $startDate, $endDate)
|
public function scopeDateRange($query, $startDate, $endDate)
|
||||||
{
|
{
|
||||||
$query->whereRaw('cast(substring(time_log, 3, 10) as unsigned) >= ' . $startDate->format('U'));
|
$query->whereRaw('cast(substring(time_log, 3, 10) as unsigned) <= ' . $endDate->modify('+1 day')->format('U'))
|
||||||
$query->whereRaw('cast(substring(time_log, 3, 10) as unsigned) <= ' . $endDate->modify('+1 day')->format('U'));
|
->whereRaw('case
|
||||||
|
when is_running then unix_timestamp()
|
||||||
|
else cast(substring(time_log, length(time_log) - 11, 10) as unsigned)
|
||||||
|
end >= ' . $startDate->format('U'));
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
113
app/Models/Traits/Inviteable.php
Normal file
113
app/Models/Traits/Inviteable.php
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Traits;
|
||||||
|
|
||||||
|
use Carbon;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SendsEmails.
|
||||||
|
*/
|
||||||
|
trait Inviteable
|
||||||
|
{
|
||||||
|
// If we're getting the link for PhantomJS to generate the PDF
|
||||||
|
// we need to make sure it's served from our site
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
* @param bool $forceOnsite
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLink($type = 'view', $forceOnsite = false, $forcePlain = false)
|
||||||
|
{
|
||||||
|
if (! $this->account) {
|
||||||
|
$this->load('account');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->proposal_id) {
|
||||||
|
$type = 'proposal';
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = $this->account;
|
||||||
|
$iframe_url = $account->iframe_url;
|
||||||
|
$url = trim(SITE_URL, '/');
|
||||||
|
|
||||||
|
if (env('REQUIRE_HTTPS')) {
|
||||||
|
$url = str_replace('http://', 'https://', $url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($account->hasFeature(FEATURE_CUSTOM_URL)) {
|
||||||
|
if (Utils::isNinjaProd() && ! Utils::isReseller()) {
|
||||||
|
$url = $account->present()->clientPortalLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($iframe_url && ! $forceOnsite) {
|
||||||
|
return "{$iframe_url}?{$this->invitation_key}";
|
||||||
|
} elseif ($this->account->subdomain && ! $forcePlain) {
|
||||||
|
$url = Utils::replaceSubdomain($url, $account->subdomain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{$url}/{$type}/{$this->invitation_key}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool|string
|
||||||
|
*/
|
||||||
|
public function getStatus()
|
||||||
|
{
|
||||||
|
$hasValue = false;
|
||||||
|
$parts = [];
|
||||||
|
$statuses = $this->message_id ? ['sent', 'opened', 'viewed'] : ['sent', 'viewed'];
|
||||||
|
|
||||||
|
foreach ($statuses as $status) {
|
||||||
|
$field = "{$status}_date";
|
||||||
|
$date = '';
|
||||||
|
if ($this->$field && $this->field != '0000-00-00 00:00:00') {
|
||||||
|
$date = Utils::dateToString($this->$field);
|
||||||
|
$hasValue = true;
|
||||||
|
$parts[] = trans('texts.invitation_status_' . $status) . ': ' . $date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hasValue ? implode($parts, '<br/>') : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->invitation_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param null $messageId
|
||||||
|
*/
|
||||||
|
public function markSent($messageId = null)
|
||||||
|
{
|
||||||
|
$this->message_id = $messageId;
|
||||||
|
$this->email_error = null;
|
||||||
|
$this->sent_date = Carbon::now()->toDateTimeString();
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSent()
|
||||||
|
{
|
||||||
|
return $this->sent_date && $this->sent_date != '0000-00-00 00:00:00';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markViewed()
|
||||||
|
{
|
||||||
|
$this->viewed_date = Carbon::now()->toDateTimeString();
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
if ($this->invoice) {
|
||||||
|
$invoice = $this->invoice;
|
||||||
|
$client = $invoice->client;
|
||||||
|
|
||||||
|
$invoice->markViewed();
|
||||||
|
$client->markLoggedIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user