mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Added ability for clients to approve a quote, converting it to an invoice
This commit is contained in:
parent
2a297e3d3f
commit
89269f4bd7
@ -133,23 +133,13 @@ class InvoiceController extends \BaseController
|
|||||||
public function view($invitationKey)
|
public function view($invitationKey)
|
||||||
{
|
{
|
||||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->firstOrFail();
|
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->firstOrFail();
|
||||||
|
|
||||||
$invoice = $invitation->invoice;
|
$invoice = $invitation->invoice;
|
||||||
|
|
||||||
if (!$invoice || $invoice->is_deleted) {
|
if (!$invoice || $invoice->is_deleted) {
|
||||||
return View::make('invoices.deleted');
|
return View::make('invoices.deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($invoice->is_quote && $invoice->quote_invoice_id) {
|
|
||||||
$invoice = Invoice::scope($invoice->quote_invoice_id, $invoice->account_id)->firstOrFail();
|
|
||||||
|
|
||||||
if (!$invoice || $invoice->is_deleted) {
|
|
||||||
return View::make('invoices.deleted');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country');
|
$invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country');
|
||||||
|
|
||||||
$client = $invoice->client;
|
$client = $invoice->client;
|
||||||
|
|
||||||
if (!$client || $client->is_deleted) {
|
if (!$client || $client->is_deleted) {
|
||||||
@ -169,7 +159,7 @@ class InvoiceController extends \BaseController
|
|||||||
|
|
||||||
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||||
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||||
$invoice->is_pro = $client->account->isPro();
|
$invoice->is_pro = $client->account->isPro();
|
||||||
|
|
||||||
$contact = $invitation->contact;
|
$contact = $invitation->contact;
|
||||||
$contact->setVisible([
|
$contact->setVisible([
|
||||||
@ -179,6 +169,7 @@ class InvoiceController extends \BaseController
|
|||||||
'phone', ]);
|
'phone', ]);
|
||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
|
'isConverted' => $invoice->quote_invoice_id ? true : false,
|
||||||
'showClientHeader' => true,
|
'showClientHeader' => true,
|
||||||
'showBreadcrumbs' => false,
|
'showBreadcrumbs' => false,
|
||||||
'hideLogo' => $client->account->isWhiteLabel(),
|
'hideLogo' => $client->account->isWhiteLabel(),
|
||||||
|
@ -614,10 +614,6 @@ class PaymentController extends \BaseController
|
|||||||
$account->save();
|
$account->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($invoice->is_quote) {
|
|
||||||
$invoice = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$payment = Payment::createNew($invitation);
|
$payment = Payment::createNew($invitation);
|
||||||
$payment->invitation_id = $invitation->id;
|
$payment->invitation_id = $invitation->id;
|
||||||
$payment->account_gateway_id = $accountGateway->id;
|
$payment->account_gateway_id = $accountGateway->id;
|
||||||
|
@ -141,7 +141,7 @@ class QuoteController extends \BaseController
|
|||||||
$clone = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id);
|
$clone = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id);
|
||||||
|
|
||||||
Session::flash('message', trans('texts.converted_to_invoice'));
|
Session::flash('message', trans('texts.converted_to_invoice'));
|
||||||
return Redirect::to('invoices/'.$clone->public_id);
|
return Redirect::to('invoices/'.$clone->public_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$statusId = Input::get('statusId');
|
$statusId = Input::get('statusId');
|
||||||
@ -156,4 +156,24 @@ class QuoteController extends \BaseController
|
|||||||
|
|
||||||
return Redirect::to('quotes');
|
return Redirect::to('quotes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function approve($invitationKey)
|
||||||
|
{
|
||||||
|
$invitation = Invitation::with('invoice.invoice_items', 'invoice.invitations')->where('invitation_key', '=', $invitationKey)->firstOrFail();
|
||||||
|
$invoice = $invitation->invoice;
|
||||||
|
|
||||||
|
if ($invoice->is_quote && !$invoice->quote_invoice_id) {
|
||||||
|
Activity::approveQuote($invitation);
|
||||||
|
$invoice = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id);
|
||||||
|
Session::flash('message', trans('texts.converted_to_invoice'));
|
||||||
|
|
||||||
|
foreach ($invoice->invitations as $invitationClone) {
|
||||||
|
if ($invitation->contact_id == $invitationClone->contact_id) {
|
||||||
|
$invitationKey = $invitationClone->invitation_key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect::to("view/{$invitationKey}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,6 +510,6 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -500,6 +500,6 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
|
||||||
|
|
||||||
|
'approve' => 'Approve',
|
||||||
);
|
);
|
||||||
|
@ -508,5 +508,6 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -480,6 +480,6 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
);
|
);
|
@ -501,6 +501,6 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
);
|
);
|
@ -503,6 +503,6 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -511,6 +511,7 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -509,7 +509,7 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
@ -504,7 +504,7 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
@ -491,7 +491,7 @@ return array(
|
|||||||
'payment_email' => 'Payment Email',
|
'payment_email' => 'Payment Email',
|
||||||
'quote_email' => 'Quote Email',
|
'quote_email' => 'Quote Email',
|
||||||
'reset_all' => 'Reset All',
|
'reset_all' => 'Reset All',
|
||||||
|
'approve' => 'Approve',
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -231,6 +231,19 @@ class Activity extends Eloquent
|
|||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function approveQuote($invitation) {
|
||||||
|
|
||||||
|
$activity = Activity::getBlank($invitation);
|
||||||
|
$activity->client_id = $invitation->invoice->client_id;
|
||||||
|
$activity->invitation_id = $invitation->id;
|
||||||
|
$activity->contact_id = $invitation->contact_id;
|
||||||
|
$activity->invoice_id = $invitation->invoice_id;
|
||||||
|
$activity->activity_type_id = ACTIVITY_TYPE_APPROVE_QUOTE;
|
||||||
|
$activity->message = Utils::encodeActivity($invitation->contact, 'approved', $invitation->invoice);
|
||||||
|
$activity->balance = $invitation->invoice->client->balance;
|
||||||
|
$activity->save();
|
||||||
|
}
|
||||||
|
|
||||||
public static function createPayment($payment)
|
public static function createPayment($payment)
|
||||||
{
|
{
|
||||||
$client = $payment->client;
|
$client = $payment->client;
|
||||||
|
@ -80,7 +80,6 @@ class InvoiceRepository
|
|||||||
$query = \DB::table('invitations')
|
$query = \DB::table('invitations')
|
||||||
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
|
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
|
||||||
->join('clients', 'clients.id', '=', 'invoices.client_id')
|
->join('clients', 'clients.id', '=', 'invoices.client_id')
|
||||||
//->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
|
||||||
->where('invitations.contact_id', '=', $contactId)
|
->where('invitations.contact_id', '=', $contactId)
|
||||||
->where('invitations.deleted_at', '=', null)
|
->where('invitations.deleted_at', '=', null)
|
||||||
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE)
|
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE)
|
||||||
@ -99,7 +98,6 @@ class InvoiceRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $table->addColumn('due_date', function ($model) { return Utils::fromSqlDate($model->due_date); })
|
return $table->addColumn('due_date', function ($model) { return Utils::fromSqlDate($model->due_date); })
|
||||||
//->addColumn('invoice_status_name', function($model) { return $model->invoice_status_name; })
|
|
||||||
->make();
|
->make();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ Route::post('get_started', 'AccountController@getStarted');
|
|||||||
|
|
||||||
// Client visible pages
|
// Client visible pages
|
||||||
Route::get('view/{invitation_key}', 'InvoiceController@view');
|
Route::get('view/{invitation_key}', 'InvoiceController@view');
|
||||||
|
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||||
Route::get('payment/{invitation_key}', 'PaymentController@show_payment');
|
Route::get('payment/{invitation_key}', 'PaymentController@show_payment');
|
||||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||||
Route::get('complete', 'PaymentController@offsite_payment');
|
Route::get('complete', 'PaymentController@offsite_payment');
|
||||||
@ -228,6 +229,7 @@ define("ACTIVITY_TYPE_RESTORE_INVOICE", 25);
|
|||||||
define("ACTIVITY_TYPE_RESTORE_CLIENT", 26);
|
define("ACTIVITY_TYPE_RESTORE_CLIENT", 26);
|
||||||
define("ACTIVITY_TYPE_RESTORE_PAYMENT", 27);
|
define("ACTIVITY_TYPE_RESTORE_PAYMENT", 27);
|
||||||
define("ACTIVITY_TYPE_RESTORE_CREDIT", 28);
|
define("ACTIVITY_TYPE_RESTORE_CREDIT", 28);
|
||||||
|
define("ACTIVITY_TYPE_APPROVE_QUOTE", 29);
|
||||||
|
|
||||||
define('DEFAULT_INVOICE_NUMBER', '0001');
|
define('DEFAULT_INVOICE_NUMBER', '0001');
|
||||||
define('RECENTLY_VIEWED_LIMIT', 8);
|
define('RECENTLY_VIEWED_LIMIT', 8);
|
||||||
|
@ -81,6 +81,7 @@
|
|||||||
var needsRefresh = false;
|
var needsRefresh = false;
|
||||||
|
|
||||||
function refreshPDF() {
|
function refreshPDF() {
|
||||||
|
PDFJS.disableWorker = true;
|
||||||
if ({{ Auth::check() && Auth::user()->force_pdfjs ? 'false' : 'true' }} && (isFirefox || (isChrome && !isChromium))) {
|
if ({{ Auth::check() && Auth::user()->force_pdfjs ? 'false' : 'true' }} && (isFirefox || (isChrome && !isChromium))) {
|
||||||
var string = getPDFString();
|
var string = getPDFString();
|
||||||
if (!string) return;
|
if (!string) return;
|
||||||
|
@ -22,18 +22,20 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<p> </p>
|
<p> </p>
|
||||||
|
<div class="pull-right" style="text-align:right">
|
||||||
@if ($invoice->client->account->isGatewayConfigured() && !$invoice->isPaid() && !$invoice->is_recurring)
|
@if ($invoice->is_quote)
|
||||||
<div class="pull-right" style="text-align:right">
|
{{ Button::normal(trans('texts.download_pdf'), array('onclick' => 'onDownloadClick()', 'class' => 'btn-lg')) }}
|
||||||
{{ Button::normal(trans('texts.download_pdf'), array('onclick' => 'onDownloadClick()', 'class' => 'btn-lg')) }}
|
@if (!$isConverted)
|
||||||
{{ Button::success_link(URL::to('payment/' . $invitation->invitation_key), trans('texts.pay_now'), array('class' => 'btn-lg')) }}
|
{{ Button::success_link(URL::to('approve/' . $invitation->invitation_key), trans('texts.approve'), array('class' => 'btn-lg')) }}
|
||||||
</div>
|
@endif
|
||||||
|
@elseif ($invoice->client->account->isGatewayConfigured() && !$invoice->isPaid() && !$invoice->is_recurring)
|
||||||
|
{{ Button::normal(trans('texts.download_pdf'), array('onclick' => 'onDownloadClick()', 'class' => 'btn-lg')) }}
|
||||||
|
{{ Button::success_link(URL::to('payment/' . $invitation->invitation_key), trans('texts.pay_now'), array('class' => 'btn-lg')) }}
|
||||||
@else
|
@else
|
||||||
<div class="pull-right">
|
{{ Button::success('Download PDF', array('onclick' => 'onDownloadClick()', 'class' => 'btn-lg')) }}
|
||||||
{{ Button::success('Download PDF', array('onclick' => 'onDownloadClick()', 'class' => 'btn-lg')) }}
|
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="clearfix"></div><p> </p>
|
<div class="clearfix"></div><p> </p>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user