1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-17 16:42:48 +01:00

Allow client to choose auto bill settings

This commit is contained in:
Joshua Dwire 2016-05-06 13:26:37 -04:00
parent 70b9c9bba7
commit 9a64db3234
9 changed files with 200 additions and 13 deletions

View File

@ -305,7 +305,7 @@ class PublicClientController extends BaseController
->make();
}
public function invoiceIndex()
public function recurringInvoiceIndex()
{
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
@ -322,6 +322,34 @@ class PublicClientController extends BaseController
$data = [
'color' => $color,
'account' => $account,
'client' => $invitation->invoice->client,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.recurring_invoices'),
'entityType' => ENTITY_RECURRING_INVOICE,
'columns' => Utils::trans(['frequency', 'start_date', 'end_date', 'invoice_total', 'auto_bill']),
];
return response()->view('public_list', $data);
}
public function invoiceIndex()
{
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'account' => $account,
'client' => $invitation->invoice->client,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE,
@ -340,6 +368,15 @@ class PublicClientController extends BaseController
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch'));
}
public function recurringInvoiceDatatable()
{
if (!$invitation = $this->getInvitation()) {
return '';
}
return $this->invoiceRepo->getClientRecurringDatatable($invitation->contact_id);
}
public function paymentIndex()
{
@ -860,4 +897,28 @@ class PublicClientController extends BaseController
Session::flash('error', $message);
Utils::logError("Payment Method Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
}
public function setAutoBill(){
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$validator = Validator::make(Input::all(), array('public_id' => 'required'));
$client = $invitation->invoice->client;
if ($validator->fails()) {
return Redirect::to('client/invoices/recurring');
}
$publicId = Input::get('public_id');
$enable = Input::get('enable');
$invoice = $client->invoices->where('public_id', intval($publicId))->first();
if ($invoice && $invoice->is_recurring && $invoice->enable_auto_bill > AUTO_BILL_OFF) {
$invoice->auto_bill = $enable ? true : false;
$invoice->save();
}
return Redirect::to('client/invoices/recurring');
}
}

View File

@ -51,6 +51,8 @@ Route::group(['middleware' => 'auth:client'], function() {
Route::post('client/paymentmethods/{source_id}/remove', 'PublicClientController@removePaymentMethod');
Route::get('client/quotes', 'PublicClientController@quoteIndex');
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
Route::get('client/invoices/recurring', 'PublicClientController@recurringInvoiceIndex');
Route::post('client/invoices/auto_bill', 'PublicClientController@setAutoBill');
Route::get('client/documents', 'PublicClientController@documentIndex');
Route::get('client/payments', 'PublicClientController@paymentIndex');
Route::get('client/dashboard', 'PublicClientController@dashboard');
@ -60,6 +62,7 @@ Route::group(['middleware' => 'auth:client'], function() {
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable'));
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'PublicClientController@invoiceDatatable'));
Route::get('api/client.recurring_invoices', array('as'=>'api.client.recurring_invoices', 'uses'=>'PublicClientController@recurringInvoiceDatatable'));
Route::get('api/client.documents', array('as'=>'api.client.documents', 'uses'=>'PublicClientController@documentDatatable'));
Route::get('api/client.payments', array('as'=>'api.client.payments', 'uses'=>'PublicClientController@paymentDatatable'));
Route::get('api/client.activity', array('as'=>'api.client.activity', 'uses'=>'PublicClientController@activityDatatable'));
@ -687,6 +690,10 @@ if (!defined('CONTACT_EMAIL')) {
define('RESELLER_REVENUE_SHARE', 'A');
define('RESELLER_LIMITED_USERS', 'B');
define('AUTO_BILL_OFF', 0);
define('AUTO_BILL_OPT_IN', 1);
define('AUTO_BILL_OPT_OUT', 2);
// These must be lowercase
define('PLAN_FREE', 'free');

View File

@ -319,6 +319,10 @@ class Client extends EntityModel
$this->last_login = Carbon::now()->toDateTimeString();
$this->save();
}
public function hasAutoBillInvoices(){
return $this->invoices()->where('auto_bill', 1)->count() > 0;
}
}
Client::creating(function ($client) {

View File

@ -145,21 +145,68 @@ class InvoiceRepository extends BaseRepository
return $query;
}
public function getClientDatatable($contactId, $entityType, $search)
public function getClientRecurringDatatable($contactId)
{
$query = DB::table('invitations')
->join('accounts', 'accounts.id', '=', 'invitations.account_id')
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
->join('clients', 'clients.id', '=', 'invoices.client_id')
->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id')
->where('invitations.contact_id', '=', $contactId)
->where('invitations.deleted_at', '=', null)
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE)
->where('invoices.is_quote', '=', false)
->where('invoices.is_deleted', '=', false)
->where('clients.deleted_at', '=', null)
->where('invoices.is_recurring', '=', false)
// This needs to be a setting to also hide the activity on the dashboard page
//->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT)
->where('invoices.is_recurring', '=', true)
->where('invoices.enable_auto_bill', '>', AUTO_BILL_OFF)
//->where('invoices.start_date', '>=', date('Y-m-d H:i:s'))
->select(
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'),
'invitations.invitation_key',
'invoices.invoice_number',
'invoices.due_date',
'clients.public_id as client_public_id',
'clients.name as client_name',
'invoices.public_id',
'invoices.amount',
'invoices.start_date',
'invoices.end_date',
'invoices.auto_bill',
'frequencies.name as frequency'
);
$table = \Datatable::query($query)
->addColumn('frequency', function ($model) { return $model->frequency; })
->addColumn('start_date', function ($model) { return Utils::fromSqlDate($model->start_date); })
->addColumn('end_date', function ($model) { return Utils::fromSqlDate($model->end_date); })
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); })
->addColumn('auto_bill', function ($model) {
if ($model->auto_bill) {
return trans('texts.enabled') . ' <a href="javascript:setAutoBill('.$model->public_id.',false)">('.trans('texts.disable').')</a>';
} else {
return trans('texts.disabled') . ' <a href="javascript:setAutoBill('.$model->public_id.',true)">('.trans('texts.enable').')</a>';
}
});
return $table->make();
}
public function getClientDatatable($contactId, $entityType, $search)
{
$query = DB::table('invitations')
->join('accounts', 'accounts.id', '=', 'invitations.account_id')
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
->join('clients', 'clients.id', '=', 'invoices.client_id')
->where('invitations.contact_id', '=', $contactId)
->where('invitations.deleted_at', '=', null)
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE)
->where('invoices.is_deleted', '=', false)
->where('clients.deleted_at', '=', null)
->where('invoices.is_recurring', '=', false)
// This needs to be a setting to also hide the activity on the dashboard page
//->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT)
->select(
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'),
'invitations.invitation_key',
@ -269,6 +316,16 @@ class InvoiceRepository extends BaseRepository
$invoice->start_date = Utils::toSqlDate($data['start_date']);
$invoice->end_date = Utils::toSqlDate($data['end_date']);
$invoice->auto_bill = isset($data['auto_bill']) && $data['auto_bill'] ? true : false;
$invoice->enable_auto_bill = isset($data['enable_auto_bill']) ? intval($data['enable_auto_bill']) : 0;
if ($invoice->enable_auto_bill != AUTO_BILL_OPT_IN && $invoice->enable_auto_bill != AUTO_BILL_OPT_OUT ) {
// Auto-bill is not enabled
$invoice->enable_auto_bill = AUTO_BILL_OFF;
$invoice->auto_bill = false;
} elseif ($isNew) {
$invoice->auto_bill = $invoice->enable_auto_bill == AUTO_BILL_OPT_OUT;
}
if (isset($data['recurring_due_date'])) {
$invoice->due_date = $data['recurring_due_date'];

View File

@ -33,6 +33,15 @@ class PaymentsChanges extends Migration
$table->date('expiration')->nullable();
$table->text('gateway_error')->nullable();
});
Schema::table('invoices', function($table)
{
$table->tinyInteger('enable_auto_bill')->default(AUTO_BILL_OFF);
});
\DB::table('invoices')
->where('auto_bill', '=', 1)
->update(array('enable_auto_bill' => AUTO_BILL_OPT_OUT));
}
/**
@ -53,6 +62,10 @@ class PaymentsChanges extends Migration
$table->dropColumn('expiration');
$table->dropColumn('gateway_error');
});
Schema::table('invoices', function ($table) {
$table->dropColumn('enable_auto_bill');
});
Schema::dropIfExists('payment_statuses');
}

View File

@ -1266,7 +1266,15 @@ $LANG = array(
'add_payment_method' => 'Add Payment Method',
'account_holder_type' => 'Account Holder Type',
'ach_authorization' => 'I authorize :company to electronically debit my account and, if necessary, electronically credit my account to correct erroneous debits.',
'ach_authorization_required' => 'You must consent to ACH transactions.'
'ach_authorization_required' => 'You must consent to ACH transactions.',
'off' => 'Off',
'opt_in' => 'Opt-in',
'opt_out' => 'Opt-out',
'enabled_by_client' => 'Enabled by client',
'disabled_by_client' => 'Disabled by client',
'manage_auto_bill' => 'Manage Auto-bill',
'enabled' => 'Enabled',
'disabled' => 'Disabled',
);
return $LANG;

View File

@ -159,10 +159,25 @@
</span>
@if($account->getTokenGatewayId())
<span data-bind="visible: is_recurring()" style="display: none">
{!! Former::checkbox('auto_bill')
->label(trans('texts.auto_bill'))
->text(trans('texts.enable'))
->data_bind("checked: auto_bill, valueUpdate: 'afterkeydown'") !!}
{!! Former::radios('enable_auto_bill')->radios([
trans('texts.off') => array('name' => 'enable_auto_bill', 'value' => 0, 'data-bind' => "checked: enable_auto_bill, valueUpdate: 'afterkeydown', checkedValue:0"),
trans('texts.opt_in') => array('name' => 'enable_auto_bill', 'value' => 1, 'data-bind' => "checked: enable_auto_bill, valueUpdate: 'afterkeydown', checkedValue:1"),
trans('texts.opt_out') => array('name' => 'enable_auto_bill', 'value' => 2, 'data-bind' => "checked: enable_auto_bill, valueUpdate: 'afterkeydown', checkedValue:2"),
])->inline()
->label(trans('texts.auto_bill')) !!}
<input type="hidden" name="auto_bill" data-bind="attr: { value: auto_bill() }" />
<div class="row">
<div class="col-sm-8 col-sm-offset-4">
<div style="margin:-10px 0 10px;">
<div data-bind="visible: enable_auto_bill() == 1 &amp;&amp; auto_bill() &amp;&amp; public_id() != 0" style="display: none">
{{trans('texts.enabled_by_client')}} <a href="#" data-bind="click:function(){auto_bill(false)}">({{trans('texts.disable')}})</a>
</div>
<div data-bind="visible: enable_auto_bill() == 2 &amp;&amp; !auto_bill() &amp;&amp; public_id() != 0" style="display: none">
{{trans('texts.disabled_by_client')}} <a href="#" data-bind="click:function(){auto_bill(true)}">({{trans('texts.enable')}})</a>
</div>
</div>
</div>
</div>
</span>
@endif
{!! Former::text('po_number')->label(trans('texts.po_number_short'))->data_bind("value: po_number, valueUpdate: 'afterkeydown'") !!}

View File

@ -188,7 +188,8 @@ function InvoiceModel(data) {
self.tax_rate2 = ko.observable();
self.is_recurring = ko.observable(0);
self.is_quote = ko.observable({{ $entityType == ENTITY_QUOTE ? '1' : '0' }});
self.auto_bill = ko.observable();
self.auto_bill = ko.observable(false);
self.enable_auto_bill = ko.observable(0);
self.invoice_status_id = ko.observable(0);
self.invoice_items = ko.observableArray();
self.documents = ko.observableArray();

View File

@ -35,7 +35,12 @@
</div>
-->
<h3>{{ $title }}</h3>
@if($entityType == ENTITY_INVOICE && $account->getTokenGatewayId() && $client->hasAutoBillInvoices())
<div class="pull-right" style="margin-top:5px">
{!! Button::info(trans("texts.manage_auto_bill"))->asLinkTo(URL::to('/client/invoices/recurring'))->appendIcon(Icon::create('repeat')) !!}
</div>
@endif
<h3>{{ $title }}</h3>
{!! Datatable::table()
->addColumn($columns)
@ -45,6 +50,22 @@
</div>
@if($entityType == ENTITY_RECURRING_INVOICE)
{!! Former::open(URL::to('/client/invoices/auto_bill'))->id('auto_bill_form') !!}
<input type="hidden" name="public_id" id="auto_bill_public_id">
<input type="hidden" name="enable" id="auto_bill_enable">
{!! Former::close() !!}
<script type="text/javascript">
function setAutoBill(publicId, enable){
$('#auto_bill_public_id').val(publicId);
$('#auto_bill_enable').val(enable?'1':'0');
$('#auto_bill_form').submit();
}
</script>
@endif
<p>&nbsp;</p>
@stop