From e540e98c80a309e839cb241ce45d6d9cabba5b2d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 21 Jun 2016 19:50:26 +1000 Subject: [PATCH 01/44] Add company logo_url to UserAccount Transformer --- app/Ninja/Transformers/UserAccountTransformer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Ninja/Transformers/UserAccountTransformer.php b/app/Ninja/Transformers/UserAccountTransformer.php index 0d64579e2c19..b0d408479f19 100644 --- a/app/Ninja/Transformers/UserAccountTransformer.php +++ b/app/Ninja/Transformers/UserAccountTransformer.php @@ -35,6 +35,7 @@ class UserAccountTransformer extends EntityTransformer 'token' => $user->account->getToken($user->id, $this->tokenName), 'default_url' => SITE_URL, 'logo' => $user->account->logo, + 'logo_url' => $user->account->getLogoURL(), ]; } } \ No newline at end of file From e68e213237de055bb60f1204417321a2c43809c8 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 11 Jul 2016 23:38:06 +0300 Subject: [PATCH 02/44] Bug fixes --- app/Http/Controllers/AccountController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 158ea60b0297..52b451d8b1e8 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -235,7 +235,7 @@ class AccountController extends BaseController } } - if (!empty($new_plan)) { + if (!empty($new_plan) && $new_plan['plan'] != PLAN_FREE) { $time_used = $planDetails['paid']->diff(date_create()); $days_used = $time_used->days; From 401851e212310de8f072bd440a58f14328b364be Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 12 Jul 2016 23:46:41 +0300 Subject: [PATCH 03/44] Working on buy now buttons --- CHANGELOG.md | 7 +- app/Http/Controllers/AccountController.php | 11 ++ .../Controllers/OnlinePaymentController.php | 59 ++++++++ app/Http/Middleware/VerifyCsrfToken.php | 2 +- app/Http/routes.php | 1 + .../PaymentDrivers/BasePaymentDriver.php | 9 +- app/Services/PaymentService.php | 16 --- resources/lang/en/texts.php | 9 +- .../views/accounts/client_portal.blade.php | 136 +++++++++++++++++- .../views/payments/credit_card.blade.php | 18 ++- 10 files changed, 234 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50c1739ad6b3..813bbb4c110c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [2.6] - 2016-07-08 +## [Unreleased] + +### Added +- Added 'Buy Now' buttons + +## [2.6] - 2016-07-12 ### Added - Configuration for first day of the week #950 diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 52b451d8b1e8..9e6f058c8185 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -25,6 +25,7 @@ use App\Models\Document; use App\Models\Gateway; use App\Models\InvoiceDesign; use App\Models\TaxRate; +use App\Models\Product; use App\Models\PaymentTerm; use App\Ninja\Repositories\AccountRepository; use App\Ninja\Repositories\ReferralRepository; @@ -714,6 +715,14 @@ class AccountController extends BaseController ); } + $types = [GATEWAY_TYPE_CREDIT_CARD, GATEWAY_TYPE_BANK_TRANSFER, GATEWAY_TYPE_PAYPAL, GATEWAY_TYPE_BITCOIN, GATEWAY_TYPE_DWOLLA]; + $options = []; + foreach ($types as $type) { + if ($account->getGatewayByType($type)) { + $options[$type] = trans("texts.{$type}"); + } + } + $data = [ 'client_view_css' => $css, 'enable_portal_password' => $account->enable_portal_password, @@ -721,6 +730,8 @@ class AccountController extends BaseController 'title' => trans('texts.client_portal'), 'section' => ACCOUNT_CLIENT_PORTAL, 'account' => $account, + 'products' => Product::scope()->orderBy('product_key')->get(), + 'gateway_types' => $options, ]; return View::make('accounts.client_portal', $data); diff --git a/app/Http/Controllers/OnlinePaymentController.php b/app/Http/Controllers/OnlinePaymentController.php index 60483bf7b2cc..da227f6c74bf 100644 --- a/app/Http/Controllers/OnlinePaymentController.php +++ b/app/Http/Controllers/OnlinePaymentController.php @@ -4,14 +4,20 @@ use Session; use Input; use Utils; use View; +use Auth; +use URL; use Exception; +use Validator; use App\Models\Invitation; use App\Models\Account; use App\Models\Payment; +use App\Models\Product; use App\Models\PaymentMethod; use App\Services\PaymentService; use App\Ninja\Mailers\UserMailer; use App\Http\Requests\CreateOnlinePaymentRequest; +use App\Ninja\Repositories\ClientRepository; +use App\Services\InvoiceService; /** * Class OnlinePaymentController @@ -203,4 +209,57 @@ class OnlinePaymentController extends BaseController } } + public function handleBuyNow(ClientRepository $clientRepo, InvoiceService $invoiceService, $gatewayType = false) + { + $account = Account::whereAccountKey(Input::get('account_key'))->first(); + $redirectUrl = Input::get('redirect_url', URL::previous()); + + if ( ! $account) { + return redirect()->to("{$redirectUrl}/?error=invalid account"); + } + + Auth::onceUsingId($account->users[0]->id); + $product = Product::scope(Input::get('product_id'))->first(); + + if ( ! $product) { + return redirect()->to("{$redirectUrl}/?error=invalid product"); + } + + $rules = [ + 'first_name' => 'string|max:100', + 'last_name' => 'string|max:100', + 'email' => 'email|string|max:100', + ]; + + $validator = Validator::make(Input::all(), $rules); + if ($validator->fails()) { + return redirect()->to("{$redirectUrl}/?error=" . $validator->errors()->first()); + } + + $data = [ + 'contact' => Input::all() + ]; + $client = $clientRepo->save($data); + + $data = [ + 'client_id' => $client->public_id, + 'invoice_items' => [[ + 'product_key' => $product->product_key, + 'notes' => $product->notes, + 'cost' => $product->cost, + 'qty' => 1, + 'tax_rate1' => $product->default_tax_rate ? $product->default_tax_rate->rate : 0, + 'tax_name1' => $product->default_tax_rate ? $product->default_tax_rate->name : '', + ]] + ]; + $invoice = $invoiceService->save($data); + $invitation = $invoice->invitations[0]; + $link = $invitation->getLink(); + + if ($gatewayType) { + return redirect()->to($invitation->getLink('payment') . "/{$gatewayType}"); + } else { + return redirect()->to($invitation->getLink()); + } + } } diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 90ca57627a72..43456eaf6ffc 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -30,6 +30,7 @@ class VerifyCsrfToken extends BaseVerifier 'hook/email_bounced', 'reseller_stats', 'payment_hook/*', + 'buy_now/*', ]; /** @@ -42,7 +43,6 @@ class VerifyCsrfToken extends BaseVerifier public function handle($request, Closure $next) { foreach ($this->openRoutes as $route) { - if ($request->is($route)) { return $next($request); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 7bd65e479df0..195b1b037442 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -81,6 +81,7 @@ Route::post('signup/submit', 'AccountController@submitSignup'); Route::get('/auth/{provider}', 'Auth\AuthController@authLogin'); Route::get('/auth_unlink', 'Auth\AuthController@authUnlink'); +Route::match(['GET', 'POST'], '/buy_now/{gateway_type?}', 'OnlinePaymentController@handleBuyNow'); Route::post('/hook/email_bounced', 'AppController@emailBounced'); Route::post('/hook/email_opened', 'AppController@emailOpened'); diff --git a/app/Ninja/PaymentDrivers/BasePaymentDriver.php b/app/Ninja/PaymentDrivers/BasePaymentDriver.php index 17ed76d3fc7c..0e906ed55853 100644 --- a/app/Ninja/PaymentDrivers/BasePaymentDriver.php +++ b/app/Ninja/PaymentDrivers/BasePaymentDriver.php @@ -227,7 +227,7 @@ class BasePaymentDriver $gateway = $this->gateway(); if ($input) { - $this->updateAddress(); + $this->updateClient(); } // load or create token @@ -280,8 +280,13 @@ class BasePaymentDriver } } - private function updateAddress() + private function updateClient() { + if ( ! $this->contact()->email && $this->input['email']) { + $this->contact()->email = $this->input['email']; + $this->contact()->save(); + } + if ( ! $this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) { return; } diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 107fec533dd0..4c58f62f4624 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -12,21 +12,6 @@ use App\Ninja\Datatables\PaymentDatatable; class PaymentService extends BaseService { - /** - * @var DatatableService - */ - protected $datatableService; - - /** - * @var PaymentRepository - */ - protected $paymentRepo; - - /** - * @var AccountRepository - */ - protected $accountRepo; - /** * PaymentService constructor. * @@ -157,5 +142,4 @@ class PaymentService extends BaseService return parent::bulk($ids, $action); } } - } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 4038ee96a24e..e8b9bae2756a 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -2027,7 +2027,14 @@ $LANG = array( 'restored_expense_category' => 'Successfully restored expense category', 'apply_taxes' => 'Apply taxes', 'min_to_max_users' => ':min to :max users', - 'max_users_reached' => 'The maximum number of users has been reached.' + 'max_users_reached' => 'The maximum number of users has been reached.', + 'buy_now_buttons' => 'Buy Now Buttons', + 'landing_page' => 'Landing Page', + 'payment_type' => 'Payment Type', + 'form' => 'Form', + 'link' => 'Link', + 'fields' => 'Fields', + 'buy_now_buttons_warning' => 'Note: client and invoice records are created even if the transaction isn\'t completed.', ); diff --git a/resources/views/accounts/client_portal.blade.php b/resources/views/accounts/client_portal.blade.php index 14883901831c..daf1efa2594e 100644 --- a/resources/views/accounts/client_portal.blade.php +++ b/resources/views/accounts/client_portal.blade.php @@ -3,7 +3,16 @@ @section('head') @parent - + @include('money_script') + + + + + @stop @section('content') @@ -69,6 +78,65 @@ +
+
+

{!! trans('texts.buy_now_buttons') !!}

+
+
+
+ + {!! Former::select('product') + ->onchange('updateBuyNowButtons()') + ->addOption('', '') + ->inlineHelp('buy_now_buttons_warning') + ->addGroupClass('product-select') !!} + + {!! Former::inline_checkboxes('client_fields') + ->onchange('updateBuyNowButtons()') + ->checkboxes([ + trans('texts.first_name') => ['value' => 'first_name', 'name' => 'first_name'], + trans('texts.last_name') => ['value' => 'last_name', 'name' => 'last_name'], + trans('texts.email') => ['value' => 'email', 'name' => 'email'], + ]) !!} + + {!! Former::inline_radios('landing_page') + ->onchange('showPaymentTypes();updateBuyNowButtons();') + ->radios([ + trans('texts.invoice') => ['value' => 'invoice', 'name' => 'landing_page_type'], + trans('texts.payment') => ['value' => 'payment', 'name' => 'landing_page_type'], + ])->check('invoice') !!} + + + +

 

+ + +
+
+ +
+ +
+ +
+
+
+ @if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_CSS))
@@ -95,12 +163,74 @@ {!! Former::close() !!} + + @stop diff --git a/resources/views/payments/credit_card.blade.php b/resources/views/payments/credit_card.blade.php index a9c3f89e9007..b60f99259125 100644 --- a/resources/views/payments/credit_card.blade.php +++ b/resources/views/payments/credit_card.blade.php @@ -77,16 +77,14 @@
- @if (isset($paymentTitle) || ! empty($contact->email)) -
-
- {!! Former::text('email') - ->placeholder(trans('texts.email')) - ->autocomplete('email') - ->label('') !!} -
+
+
+ {!! Former::text('email') + ->placeholder(trans('texts.email')) + ->autocomplete('email') + ->label('') !!}
- @endif +

 
 

@@ -257,5 +255,5 @@

 

{!! Former::close() !!} - + @stop From fa9b0bf78b5f3a17ba712e07a0c3570228d158a6 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 13 Jul 2016 12:03:39 +0300 Subject: [PATCH 04/44] Working on buy now buttons --- app/Http/Controllers/AccountController.php | 1 + .../Controllers/OnlinePaymentController.php | 3 +- app/Http/routes.php | 1 + app/Models/Account.php | 1 + .../PaymentDrivers/BasePaymentDriver.php | 20 +++- app/Ninja/Repositories/AccountRepository.php | 1 + .../2016_07_13_083821_add_buy_now_buttons.php | 55 +++++++++ resources/lang/en/texts.php | 3 + .../views/accounts/client_portal.blade.php | 106 ++++++++++-------- resources/views/credits/edit.blade.php | 22 ++-- resources/views/expenses/edit.blade.php | 6 +- resources/views/invoices/edit.blade.php | 3 + resources/views/tasks/edit.blade.php | 6 +- 13 files changed, 167 insertions(+), 61 deletions(-) create mode 100644 database/migrations/2016_07_13_083821_add_buy_now_buttons.php diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 9e6f058c8185..17fac3e3d8ed 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -827,6 +827,7 @@ class AccountController extends BaseController $account->enable_client_portal_dashboard = !!Input::get('enable_client_portal_dashboard'); $account->enable_portal_password = !!Input::get('enable_portal_password'); $account->send_portal_password = !!Input::get('send_portal_password'); + $account->enable_buy_now_buttons = !!Input::get('enable_buy_now_buttons'); // Only allowed for pro Invoice Ninja users or white labeled self-hosted users if (Auth::user()->account->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) { diff --git a/app/Http/Controllers/OnlinePaymentController.php b/app/Http/Controllers/OnlinePaymentController.php index da227f6c74bf..fded55725b66 100644 --- a/app/Http/Controllers/OnlinePaymentController.php +++ b/app/Http/Controllers/OnlinePaymentController.php @@ -214,7 +214,7 @@ class OnlinePaymentController extends BaseController $account = Account::whereAccountKey(Input::get('account_key'))->first(); $redirectUrl = Input::get('redirect_url', URL::previous()); - if ( ! $account) { + if ( ! $account || ! $account->enable_buy_now_buttons || ! $account->hasFeature(FEATURE_BUY_NOW_BUTTONS)) { return redirect()->to("{$redirectUrl}/?error=invalid account"); } @@ -237,6 +237,7 @@ class OnlinePaymentController extends BaseController } $data = [ + 'currency_id' => $account->currency_id, 'contact' => Input::all() ]; $client = $clientRepo->save($data); diff --git a/app/Http/routes.php b/app/Http/routes.php index 195b1b037442..8347210f1812 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -753,6 +753,7 @@ if (!defined('CONTACT_EMAIL')) { define('FEATURE_TASKS', 'tasks'); define('FEATURE_EXPENSES', 'expenses'); define('FEATURE_REPORTS', 'reports'); + define('FEATURE_BUY_NOW_BUTTONS', 'buy_now_buttons'); define('FEATURE_API', 'api'); define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password'); define('FEATURE_CUSTOM_URL', 'custom_url'); diff --git a/app/Models/Account.php b/app/Models/Account.php index 56c6829f243b..91dec9537d38 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -1170,6 +1170,7 @@ class Account extends Eloquent case FEATURE_MORE_INVOICE_DESIGNS: case FEATURE_QUOTES: case FEATURE_REPORTS: + case FEATURE_BUY_NOW_BUTTONS: case FEATURE_API: case FEATURE_CLIENT_PORTAL_PASSWORD: case FEATURE_CUSTOM_URL: diff --git a/app/Ninja/PaymentDrivers/BasePaymentDriver.php b/app/Ninja/PaymentDrivers/BasePaymentDriver.php index 0e906ed55853..33e9cd9b259a 100644 --- a/app/Ninja/PaymentDrivers/BasePaymentDriver.php +++ b/app/Ninja/PaymentDrivers/BasePaymentDriver.php @@ -282,19 +282,29 @@ class BasePaymentDriver private function updateClient() { - if ( ! $this->contact()->email && $this->input['email']) { - $this->contact()->email = $this->input['email']; - $this->contact()->save(); - } - if ( ! $this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) { return; } + // update the contact info + if ( ! $this->contact()->getFullName()) { + $this->contact()->first_name = $this->input['first_name']; + $this->contact()->last_name = $this->input['last_name']; + } + + if ( ! $this->contact()->email) { + $this->contact()->email = $this->input['email']; + } + + if ($this->contact()->isDirty()) { + $this->contact()->save(); + } + if ( ! $this->accountGateway->show_address || ! $this->accountGateway->update_address) { return; } + // update the address info $client = $this->client(); $client->address1 = trim($this->input['address1']); $client->address2 = trim($this->input['address2']); diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 1c6f7f30c35f..0d461d08b4b1 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -202,6 +202,7 @@ class AccountRepository ['new_user', '/users/create'], ['custom_fields', '/settings/invoice_settings'], ['invoice_number', '/settings/invoice_settings'], + ['buy_now_buttons', '/settings/client_portal#buyNow'] ]); $settings = array_merge(Account::$basicSettings, Account::$advancedSettings); diff --git a/database/migrations/2016_07_13_083821_add_buy_now_buttons.php b/database/migrations/2016_07_13_083821_add_buy_now_buttons.php new file mode 100644 index 000000000000..a27c0b09c504 --- /dev/null +++ b/database/migrations/2016_07_13_083821_add_buy_now_buttons.php @@ -0,0 +1,55 @@ +boolean('enable_buy_now_buttons')->default(false); + $table->dropColumn('invoice_design'); + }); + + Schema::table('datetime_formats', function($table) + { + $table->dropColumn('label'); + }); + + Schema::table('date_formats', function($table) + { + $table->dropColumn('label'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accounts', function($table) + { + $table->dropColumn('enable_buy_now_buttons'); + $table->text('invoice_design')->nullable(); + }); + + Schema::table('datetime_formats', function($table) + { + $table->string('label'); + }); + + Schema::table('date_formats', function($table) + { + $table->string('label'); + }); + } +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index e8b9bae2756a..d250e07d5fc4 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -2034,7 +2034,10 @@ $LANG = array( 'form' => 'Form', 'link' => 'Link', 'fields' => 'Fields', + 'dwolla' => 'Dwolla', 'buy_now_buttons_warning' => 'Note: client and invoice records are created even if the transaction isn\'t completed.', + 'buy_now_buttons_disabled' => 'This feature requires that a product is created and a payment gateway is configured.', + 'enable_buy_now_buttons_help' => 'Enable support for buy now buttons', ); diff --git a/resources/views/accounts/client_portal.blade.php b/resources/views/accounts/client_portal.blade.php index daf1efa2594e..7e9f43620ace 100644 --- a/resources/views/accounts/client_portal.blade.php +++ b/resources/views/accounts/client_portal.blade.php @@ -26,6 +26,7 @@ {!! Former::populateField('client_view_css', $client_view_css) !!} {!! Former::populateField('enable_portal_password', intval($enable_portal_password)) !!} {!! Former::populateField('send_portal_password', intval($send_portal_password)) !!} +{!! Former::populateField('enable_buy_now_buttons', intval($account->enable_buy_now_buttons)) !!} @if (!Utils::isNinja() && !Auth::user()->account->hasFeature(FEATURE_WHITE_LABEL))
@@ -83,55 +84,73 @@

{!! trans('texts.buy_now_buttons') !!}

-
+
- {!! Former::select('product') - ->onchange('updateBuyNowButtons()') - ->addOption('', '') - ->inlineHelp('buy_now_buttons_warning') - ->addGroupClass('product-select') !!} + @if (count($gateway_types) && count($products)) - {!! Former::inline_checkboxes('client_fields') - ->onchange('updateBuyNowButtons()') - ->checkboxes([ - trans('texts.first_name') => ['value' => 'first_name', 'name' => 'first_name'], - trans('texts.last_name') => ['value' => 'last_name', 'name' => 'last_name'], - trans('texts.email') => ['value' => 'email', 'name' => 'email'], - ]) !!} + {!! Former::checkbox('enable_buy_now_buttons') + ->text(trans('texts.enable')) + ->label(' ') + ->help(trans('texts.enable_buy_now_buttons_help')) !!} - {!! Former::inline_radios('landing_page') - ->onchange('showPaymentTypes();updateBuyNowButtons();') - ->radios([ - trans('texts.invoice') => ['value' => 'invoice', 'name' => 'landing_page_type'], - trans('texts.payment') => ['value' => 'payment', 'name' => 'landing_page_type'], - ])->check('invoice') !!} + @if ($account->enable_buy_now_buttons) + {!! Former::select('product') + ->onchange('updateBuyNowButtons()') + ->addOption('', '') + ->inlineHelp('buy_now_buttons_warning') + ->addGroupClass('product-select') !!} - + {!! Former::checkboxes('client_fields') + ->onchange('updateBuyNowButtons()') + ->checkboxes([ + trans('texts.first_name') => ['value' => 'first_name', 'name' => 'first_name'], + trans('texts.last_name') => ['value' => 'last_name', 'name' => 'last_name'], + trans('texts.email') => ['value' => 'email', 'name' => 'email'], + ]) !!} -

 

+ {!! Former::inline_radios('landing_page') + ->onchange('showPaymentTypes();updateBuyNowButtons();') + ->radios([ + trans('texts.invoice') => ['value' => 'invoice', 'name' => 'landing_page_type'], + trans('texts.payment') => ['value' => 'payment', 'name' => 'landing_page_type'], + ])->check('invoice') !!} - -
-
- -
- -
+ + +

 

+ + +
+
+ +
+ +
+ + @endif + + @else + +
+ {{ trans('texts.buy_now_buttons_disabled') }} +
+ + @endif
@@ -148,7 +167,6 @@ ->label(trans('texts.custom_css')) ->rows(10) ->raw() - ->autofocus() ->maxlength(60000) ->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!}
diff --git a/resources/views/credits/edit.blade.php b/resources/views/credits/edit.blade.php index 5c8377038f21..5b4c7ae24dec 100644 --- a/resources/views/credits/edit.blade.php +++ b/resources/views/credits/edit.blade.php @@ -2,12 +2,12 @@ @section('content') - + {!! Former::open($url)->addClass('col-md-10 col-md-offset-1 warn-on-exit')->method($method)->rules(array( 'client' => 'required', - 'amount' => 'required', + 'amount' => 'required', )) !!} - +
@@ -38,23 +38,27 @@ -@stop \ No newline at end of file +@stop diff --git a/resources/views/expenses/edit.blade.php b/resources/views/expenses/edit.blade.php index dce6caafb4a6..a555ed4c09bb 100644 --- a/resources/views/expenses/edit.blade.php +++ b/resources/views/expenses/edit.blade.php @@ -257,7 +257,11 @@ var $clientSelect = $('select#client_id'); for (var i=0; i Date: Wed, 13 Jul 2016 16:33:49 +0300 Subject: [PATCH 05/44] Moved buy now div id --- resources/views/accounts/client_portal.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/accounts/client_portal.blade.php b/resources/views/accounts/client_portal.blade.php index 7e9f43620ace..afd1b56cc1bf 100644 --- a/resources/views/accounts/client_portal.blade.php +++ b/resources/views/accounts/client_portal.blade.php @@ -79,12 +79,12 @@
-
+

{!! trans('texts.buy_now_buttons') !!}

-
+
@if (count($gateway_types) && count($products)) From b9f9c0807d84a5758b3a2697c67e515ddf3a1092 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 13 Jul 2016 16:34:26 +0300 Subject: [PATCH 06/44] WePay fixes --- app/Ninja/PaymentDrivers/WePayPaymentDriver.php | 17 ----------------- .../views/payments/braintree/paypal.blade.php | 2 ++ .../payments/wepay/bank_transfer.blade.php | 2 ++ 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/app/Ninja/PaymentDrivers/WePayPaymentDriver.php b/app/Ninja/PaymentDrivers/WePayPaymentDriver.php index c8865e74d103..a88cc1ac51af 100644 --- a/app/Ninja/PaymentDrivers/WePayPaymentDriver.php +++ b/app/Ninja/PaymentDrivers/WePayPaymentDriver.php @@ -7,8 +7,6 @@ use Exception; class WePayPaymentDriver extends BasePaymentDriver { - protected $sourceReferenceParam = 'accessToken'; - public function gatewayTypes() { $types = [ @@ -23,21 +21,6 @@ class WePayPaymentDriver extends BasePaymentDriver return $types; } - /* - public function startPurchase($input = false, $sourceId = false) - { - $data = parent::startPurchase($input, $sourceId); - - if ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) { - if ( ! $sourceId) { - throw new Exception(); - } - } - - return $data; - } - */ - public function tokenize() { return true; diff --git a/resources/views/payments/braintree/paypal.blade.php b/resources/views/payments/braintree/paypal.blade.php index 711124f6ecf9..3009d27f14be 100644 --- a/resources/views/payments/braintree/paypal.blade.php +++ b/resources/views/payments/braintree/paypal.blade.php @@ -39,4 +39,6 @@ @endif + {!! Former::close() !!} + @stop diff --git a/resources/views/payments/wepay/bank_transfer.blade.php b/resources/views/payments/wepay/bank_transfer.blade.php index cfca52b94a88..22ea75be4136 100644 --- a/resources/views/payments/wepay/bank_transfer.blade.php +++ b/resources/views/payments/wepay/bank_transfer.blade.php @@ -39,4 +39,6 @@ @endif + {!! Former::close() !!} + @stop From 7c303e514f0320e6dc701555c226684cdff22fd4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 13 Jul 2016 20:16:45 +0300 Subject: [PATCH 07/44] Temp fix for automated test --- tests/acceptance/OnlinePaymentCest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/OnlinePaymentCest.php b/tests/acceptance/OnlinePaymentCest.php index 0a69a6942f01..e56b9921d669 100644 --- a/tests/acceptance/OnlinePaymentCest.php +++ b/tests/acceptance/OnlinePaymentCest.php @@ -91,7 +91,8 @@ class OnlinePaymentCest // create recurring invoice and auto-bill $I->amOnPage('/recurring_invoices/create'); - $I->selectDropdown($I, $clientEmail, '.client_select .dropdown-toggle'); + //$I->selectDropdown($I, $clientEmail, '.client_select .dropdown-toggle'); + $I->selectDropdown($I, 'Test Test', '.client_select .dropdown-toggle'); $I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey); $I->click('table.invoice-table tbody tr:nth-child(1) .tt-selectable'); $I->selectOption('#auto_bill', 3); From bf166a1a6f2764b0bd5e242c5914228b6a9a78fa Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 13 Jul 2016 21:10:22 +0300 Subject: [PATCH 08/44] Fix for [Object] problem with certain designs --- public/built.js | 2 -- public/js/pdf.pdfmake.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/public/built.js b/public/built.js index 6e96fb8e2408..f72e47d0968e 100644 --- a/public/built.js +++ b/public/built.js @@ -31773,8 +31773,6 @@ NINJA.processItem = function(item, section) { NINJA.parseMarkdownText = function(val, groupText) { - val = val + "\n"; - var rules = [ ['\\\*\\\*(\\\w.+?)\\\*\\\*', {'bold': true}], // **value** ['\\\*(\\\w.+?)\\\*', {'italics': true}], // *value* diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index a9624c5f3b50..4a7a54849932 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -737,8 +737,6 @@ NINJA.processItem = function(item, section) { NINJA.parseMarkdownText = function(val, groupText) { - val = val + "\n"; - var rules = [ ['\\\*\\\*(\\\w.+?)\\\*\\\*', {'bold': true}], // **value** ['\\\*(\\\w.+?)\\\*', {'italics': true}], // *value* From 2cdba1e4ed54a3561ee7d889e091734a65aade19 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 14 Jul 2016 11:42:10 +0300 Subject: [PATCH 09/44] Fixes #974 --- resources/views/invoices/edit.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index b3ed7e984d76..3ede53d95cbc 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -833,7 +833,7 @@ model.invoice().custom_taxes2({{ $account->custom_invoice_taxes2 ? 'true' : 'false' }}); // set the default account tax rate @if ($account->invoice_taxes && ! empty($defaultTax)) - var defaultTax = {!! $defaultTax !!}; + var defaultTax = {!! $defaultTax->toJson() !!}; model.invoice().tax_rate1(defaultTax.rate); model.invoice().tax_name1(defaultTax.name); @endif From dce90da0c61212f658ba2a7895b6ca28ac270245 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 14 Jul 2016 12:01:05 +0300 Subject: [PATCH 10/44] Fix for code refactor --- app/Policies/DocumentPolicy.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Policies/DocumentPolicy.php b/app/Policies/DocumentPolicy.php index e9d67dd34154..0af6e80a2af6 100644 --- a/app/Policies/DocumentPolicy.php +++ b/app/Policies/DocumentPolicy.php @@ -2,7 +2,6 @@ namespace App\Policies; -use App\Models\Document; use App\Models\User; /** @@ -24,7 +23,7 @@ class DocumentPolicy extends EntityPolicy * @param Document $document * @return bool */ - public static function view(User $user, Document $document) + public static function view(User $user, $document) { if ($user->hasPermission('view_all')) { return true; @@ -41,4 +40,4 @@ class DocumentPolicy extends EntityPolicy return $user->owns($item); } -} \ No newline at end of file +} From c10fd83c74910d0a202bb9a76487440ae816dbf9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 14 Jul 2016 12:10:09 +0300 Subject: [PATCH 11/44] Updated change log --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 813bbb4c110c..edb65f532c04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added 'Buy Now' buttons +### Fixed +- Setting default tax rate breaks invoice creation #974 + + ## [2.6] - 2016-07-12 ### Added From 8590f9ad17efadbe4e8df8327c4bef7c3253650e Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 14 Jul 2016 12:46:00 +0300 Subject: [PATCH 12/44] Working on new pricing --- app/Console/Commands/SendRenewalInvoices.php | 2 +- app/Http/Controllers/AccountController.php | 81 +++++++++++++++++++ app/Models/User.php | 7 +- .../PaymentDrivers/BasePaymentDriver.php | 40 ++++----- app/Ninja/Repositories/AccountRepository.php | 19 +---- resources/views/accounts/management.blade.php | 37 +-------- 6 files changed, 107 insertions(+), 79 deletions(-) diff --git a/app/Console/Commands/SendRenewalInvoices.php b/app/Console/Commands/SendRenewalInvoices.php index 1c46f50680c5..6ed81a1183de 100644 --- a/app/Console/Commands/SendRenewalInvoices.php +++ b/app/Console/Commands/SendRenewalInvoices.php @@ -53,7 +53,7 @@ class SendRenewalInvoices extends Command $companies = Company::whereRaw('datediff(plan_expires, curdate()) = 10') ->orderBy('id') ->get(); - $this->info(count($companies).' companies found'); + $this->info(count($companies).' companies found renewing in 10 days'); foreach ($companies as $company) { if (!count($company->accounts)) { diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 17fac3e3d8ed..844f904005ca 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -158,6 +158,85 @@ class AccountController extends BaseController /** * @return \Illuminate\Http\RedirectResponse */ + public function changePlan() { + $user = Auth::user(); + $account = $user->account; + $company = $account->company; + + $plan = Input::get('plan'); + $term = Input::get('plan_term'); + $numUsers = Input::get('num_users'); + + $planDetails = $account->getPlanDetails(false, false); + + $newPlan = [ + 'plan' => $plan, + 'term' => $term, + 'num_users' => $numUsers, + ]; + $newPlan['price'] = Utils::getPlanPrice($newPlan); + $credit = 0; + + if (!empty($planDetails['started']) && $plan == PLAN_FREE) { + // Downgrade + $refund_deadline = clone $planDetails['started']; + $refund_deadline->modify('+30 days'); + + if ($plan == PLAN_FREE && $refund_deadline >= date_create()) { + // Refund + $company->plan = null; + $company->plan_term = null; + $company->plan_started = null; + $company->plan_expires = null; + $company->plan_paid = null; + + if ($payment = $account->company->payment) { + $ninjaAccount = $this->accountRepo->getNinjaAccount(); + $paymentDriver = $ninjaAccount->paymentDriver(); + $paymentDriver->refundPayment($payment); + Session::flash('message', trans('texts.plan_refunded')); + \Log::info("Refunded Plan Payment: {$account->name} - {$user->email}"); + } else { + Session::flash('message', trans('texts.updated_plan')); + } + + $account->company->save(); + } + } + + if (!empty($planDetails['paid']) && $plan != PLAN_FREE) { + $time_used = $planDetails['paid']->diff(date_create()); + $days_used = $time_used->days; + + if ($time_used->invert) { + // They paid in advance + $days_used *= -1; + } + + $days_total = $planDetails['paid']->diff($planDetails['expires'])->days; + $percent_used = $days_used / $days_total; + $credit = $planDetails['plan_price'] * (1 - $percent_used); + } + + if ($newPlan['price'] > $credit) { + $invitation = $this->accountRepo->enablePlan($newPlan, $credit); + return Redirect::to('view/' . $invitation->invitation_key); + } else { + + $company->plan = $plan; + $company->plan_term = $term; + $company->plan_price = $newPlan['price']; + $company->num_users = $numUsers; + $company->plan_expires = DateTime::createFromFormat('Y-m-d', $company->plan_paid)->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); + + // TODO change plan + var_dump($newPlan); + var_dump($credit); + dd('0'); + } + } + + /* public function changePlan() { $user = Auth::user(); $account = $user->account; @@ -280,6 +359,8 @@ class AccountController extends BaseController return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301); } + */ + /** * @param $entityType diff --git a/app/Models/User.php b/app/Models/User.php index e95459f384b6..933e7f888d82 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -432,8 +432,11 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac } - public function caddAddUsers() { - if ( ! $this->hasFeature(FEATURE_USERS)) { + public function caddAddUsers() + { + if ( ! Utils::isNinja()) { + return true; + } elseif ( ! $this->hasFeature(FEATURE_USERS)) { return false; } diff --git a/app/Ninja/PaymentDrivers/BasePaymentDriver.php b/app/Ninja/PaymentDrivers/BasePaymentDriver.php index 33e9cd9b259a..baef52a7a34e 100644 --- a/app/Ninja/PaymentDrivers/BasePaymentDriver.php +++ b/app/Ninja/PaymentDrivers/BasePaymentDriver.php @@ -591,57 +591,49 @@ class BasePaymentDriver if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) { $plan = strtolower($matches[1]); $term = strtolower($matches[2]); + $price = $invoice_item->cost; if ($plan == PLAN_ENTERPRISE) { preg_match('/###[\d] [\w]* (\d*)/', $invoice_item->notes, $matches); $numUsers = $matches[1]; } else { $numUsers = 1; } - } elseif ($invoice_item->product_key == 'Pending Monthly') { - $pending_monthly = true; } } if (!empty($plan)) { $account = Account::with('users')->find($invoice->client->public_id); + $company = $account->company; if( - $account->company->plan != $plan + $company->plan != $plan || DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create('-7 days') ) { // Either this is a different plan, or the subscription expired more than a week ago // Reset any grandfathering - $account->company->plan_started = date_create()->format('Y-m-d'); + $company->plan_started = date_create()->format('Y-m-d'); } if ( - $account->company->plan == $plan - && $account->company->plan_term == $term - && DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create() + $company->plan == $plan + && $company->plan_term == $term + && DateTime::createFromFormat('Y-m-d', $company->plan_expires) >= date_create() ) { // This is a renewal; mark it paid as of when this term expires - $account->company->plan_paid = $account->company->plan_expires; + $company->plan_paid = $company->plan_expires; } else { - $account->company->plan_paid = date_create()->format('Y-m-d'); + $company->plan_paid = date_create()->format('Y-m-d'); } - $account->company->payment_id = $payment->id; - $account->company->plan = $plan; - $account->company->plan_term = $term; - $account->company->plan_price = $payment->amount; - $account->company->num_users = $numUsers; - $account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid) + $company->payment_id = $payment->id; + $company->plan = $plan; + $company->plan_term = $term; + $company->plan_price = $price; + $company->num_users = $numUsers; + $company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid) ->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); - if (!empty($pending_monthly)) { - $account->company->pending_plan = $plan; - $account->company->pending_term = PLAN_TERM_MONTHLY; - } else { - $account->company->pending_plan = null; - $account->company->pending_term = null; - } - - $account->company->save(); + $company->save(); } } diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 0d461d08b4b1..8de9ee19a337 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -229,16 +229,16 @@ class AccountRepository return $data; } - public function enablePlan($plan, $credit = 0, $pending_monthly = false) + public function enablePlan($plan, $credit = 0) { $account = Auth::user()->account; $client = $this->getNinjaClient($account); - $invitation = $this->createNinjaInvoice($client, $account, $plan, $credit, $pending_monthly); + $invitation = $this->createNinjaInvoice($client, $account, $plan, $credit); return $invitation; } - public function createNinjaInvoice($client, $clientAccount, $plan, $credit = 0, $pending_monthly = false) + public function createNinjaInvoice($client, $clientAccount, $plan, $credit = 0) { $term = $plan['term']; $plan_cost = $plan['price']; @@ -285,19 +285,6 @@ class AccountRepository $item->product_key = 'Plan - '.ucfirst($plan).' ('.ucfirst($term).')'; $invoice->invoice_items()->save($item); - if ($pending_monthly) { - $term_end = $term == PLAN_MONTHLY ? date_create('+1 month') : date_create('+1 year'); - $pending_monthly_item = InvoiceItem::createNew($invoice); - $item->qty = 1; - $pending_monthly_item->cost = 0; - $pending_monthly_item->notes = trans('texts.plan_pending_monthly', ['date', Utils::dateToString($term_end)]); - - // Don't change this without updating the text in PaymentService->createPayment() - $pending_monthly_item->product_key = 'Pending Monthly'; - $invoice->invoice_items()->save($pending_monthly_item); - } - - $invitation = new Invitation(); $invitation->account_id = $account->id; $invitation->user_id = $account->users()->first()->id; diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index 1e9a25b5fe01..1f6669d15c61 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -38,11 +38,7 @@ @if ($planDetails && $planDetails['active'])

@@ -54,28 +50,6 @@

- @if ($account->company->pending_plan) -
- -
-

- @if ($account->company->pending_plan == PLAN_FREE) - {{ trans('texts.plan_changes_to', [ - 'plan'=>trans('texts.plan_free'), - 'date'=>Utils::dateToString($planDetails['expires']) - ]) }} - @else - {{ trans('texts.plan_term_changes_to', [ - 'plan'=>trans('texts.plan_'.$account->company->pending_plan), - 'term'=>trans('texts.plan_term_'.$account->company->pending_term.'ly'), - 'date'=>Utils::dateToString($planDetails['expires']) - ]) }} - @endif
- {{ trans('texts.cancel_plan_change') }} -

-
-
- @endif @if (Utils::isNinjaProd()) {!! Former::actions( Button::info(trans('texts.plan_change'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('edit'))) !!} @endif @@ -221,15 +195,6 @@ } } - @if ($account->company->pending_plan) - function cancelPendingChange(){ - $('#plan').val('{{ $planDetails['plan'] }}') - $('#plan_term').val('{{ $planDetails['term'] }}') - confirmChangePlan(); - return false; - } - @endif - jQuery(document).ready(function($){ function updatePlanModal() { var plan = $('#plan').val(); From 235acb05a4ee20091d83abc499298d66c3dc0d23 Mon Sep 17 00:00:00 2001 From: Sebastian Klaus Date: Thu, 14 Jul 2016 23:22:11 +0800 Subject: [PATCH 13/44] Fixed Request being statically called Signed-off-by: Sebastian Klaus --- CHANGELOG.md | 1 + app/Http/Middleware/StartupCheck.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb65f532c04..4f79e49a6791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,3 +26,4 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - "Manual entry" untranslatable #562 - Using a database table prefix breaks the dashboard #203 +- Request statically called in StartupCheck.php #977 diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index c8b89b002683..bc3559a182a2 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -33,12 +33,12 @@ class StartupCheck // 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' if (isset($_ENV['TRUSTED_PROXIES'])) { - 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 - if (Utils::requireHTTPS() && !Request::secure()) { - return Redirect::secure(Request::path()); + if (Utils::requireHTTPS() && !$request->secure()) { + return Redirect::secure($request->path()); } // If the database doens't yet exist we'll skip the rest From d2d70eb12af6a18ea1a34fb08e6ed62b12353686 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 14 Jul 2016 22:58:26 +0300 Subject: [PATCH 14/44] Fix for branding logo with custom designs --- public/built.js | 28 ++++++++++++++++++---------- public/js/pdf.pdfmake.js | 28 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/public/built.js b/public/built.js index f72e47d0968e..7e38c6b151cc 100644 --- a/public/built.js +++ b/public/built.js @@ -31118,23 +31118,31 @@ function GetPdfMake(invoice, javascript, callback) { return val; } - // Add ninja logo to the footer var dd = JSON.parse(javascript, jsonCallBack); var designId = invoice.invoice_design_id; if (!invoice.features.remove_created_by) { var footer = (typeof dd.footer === 'function') ? dd.footer() : dd.footer; if (footer) { - if (designId == NINJA.TEMPLATES.ELEGANT) { - footer[0].columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, -20, 20, 0]}) - } else if (designId == NINJA.TEMPLATES.PLAYFUL) { - footer.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, 0, 10, 10]}) - } else if (designId == NINJA.TEMPLATES.BOLD) { - footer[1].columns.push({image: logoImages.imageLogo2, alignment: 'right', width: 130, margin: [0, -20, 20, 0]}) - } else if (designId == NINJA.TEMPLATES.MODERN) { - footer[1].columns[0].stack.push({image: logoImages.imageLogo3, alignment: 'left', width: 130, margin: [40, 6, 0, 0]}); - } else { + if (footer.hasOwnProperty('columns')) { footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, 0, 0, 0]}) + } else { + var foundColumns; + for (var i=0; i Date: Thu, 14 Jul 2016 22:58:48 +0300 Subject: [PATCH 15/44] Working on new pricing --- app/Http/Controllers/AccountController.php | 35 ++++++++++---------- app/Ninja/Repositories/AccountRepository.php | 19 +++++++++++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 844f904005ca..6a0308b81dc9 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -183,13 +183,6 @@ class AccountController extends BaseController $refund_deadline->modify('+30 days'); if ($plan == PLAN_FREE && $refund_deadline >= date_create()) { - // Refund - $company->plan = null; - $company->plan_term = null; - $company->plan_started = null; - $company->plan_expires = null; - $company->plan_paid = null; - if ($payment = $account->company->payment) { $ninjaAccount = $this->accountRepo->getNinjaAccount(); $paymentDriver = $ninjaAccount->paymentDriver(); @@ -199,8 +192,6 @@ class AccountController extends BaseController } else { Session::flash('message', trans('texts.updated_plan')); } - - $account->company->save(); } } @@ -223,16 +214,24 @@ class AccountController extends BaseController return Redirect::to('view/' . $invitation->invitation_key); } else { - $company->plan = $plan; - $company->plan_term = $term; - $company->plan_price = $newPlan['price']; - $company->num_users = $numUsers; - $company->plan_expires = DateTime::createFromFormat('Y-m-d', $company->plan_paid)->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); + // create a credit + $credit = $credit - $newPlan['price']; + if ($credit > 0) { + $client = $this->accountRepo->getNinjaClient($account, $credit); + $this->accountRepo->createNinjaCredit(); + } - // TODO change plan - var_dump($newPlan); - var_dump($credit); - dd('0'); + if ($plan != PLAN_FREE) { + $company->plan_term = $term; + $company->plan_price = $newPlan['price']; + $company->num_users = $numUsers; + $company->plan_expires = date_create()->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); + } + + $company->plan = $plan; + $company->save(); + + return Redirect::to('settings/account_management'); } } diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 8de9ee19a337..41a6af5384de 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -13,6 +13,7 @@ use App\Models\Invitation; use App\Models\Invoice; use App\Models\InvoiceItem; use App\Models\Client; +use App\Models\Credit; use App\Models\Language; use App\Models\Contact; use App\Models\Account; @@ -238,6 +239,24 @@ class AccountRepository return $invitation; } + public function createNinjaCredit($client, $amount) + { + $account = $this->getNinjaAccount(); + + $lastCredit = Credit::withTrashed()->whereAccountId($account->id)->orderBy('public_id', 'DESC')->first(); + $publicId = $lastCredit ? ($lastCredit->public_id + 1) : 1; + + $credit = new Credit(); + $credit->public_id = $publicId; + $credit->account_id = $account->id; + $credit->user_id = $account->users()->first()->id; + $credit->client_id = $client->id; + $credit->amount = $amount; + $credit->save(); + + return $credit; + } + public function createNinjaInvoice($client, $clientAccount, $plan, $credit = 0) { $term = $plan['term']; From c6bf1d5fcc7cba5d65c8e7a23829908e7a1bb8e7 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Fri, 15 Jul 2016 10:41:16 +0300 Subject: [PATCH 16/44] Fix for wepay fee --- app/Ninja/PaymentDrivers/WePayPaymentDriver.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/Ninja/PaymentDrivers/WePayPaymentDriver.php b/app/Ninja/PaymentDrivers/WePayPaymentDriver.php index a88cc1ac51af..74f2f05d3bf0 100644 --- a/app/Ninja/PaymentDrivers/WePayPaymentDriver.php +++ b/app/Ninja/PaymentDrivers/WePayPaymentDriver.php @@ -53,7 +53,7 @@ class WePayPaymentDriver extends BasePaymentDriver $data['transaction_id'] = $transactionId; } - $data['applicationFee'] = $this->calculateApplicationFee($data['amount']); + $data['applicationFee'] = (WEPAY_APP_FEE_MULTIPLIER * $data['amount']) + WEPAY_APP_FEE_FIXED; $data['feePayer'] = WEPAY_FEE_PAYER; $data['callbackUri'] = $this->accountGateway->getWebhookUrl(); @@ -191,13 +191,6 @@ class WePayPaymentDriver extends BasePaymentDriver return $response->getCode() == 4004; } - private function calculateApplicationFee($amount) - { - $fee = (WEPAY_APP_FEE_MULTIPLIER * $amount) + WEPAY_APP_FEE_FIXED; - - return floor(min($fee, $amount * 0.2));// Maximum fee is 20% of the amount. - } - public function handleWebHook($input) { $accountGateway = $this->accountGateway; From cf3465b5e22737577e33b4becf2c39b223310721 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Fri, 15 Jul 2016 11:23:19 +0300 Subject: [PATCH 17/44] Fix for token billing --- app/Models/PaymentMethod.php | 6 +++--- app/Ninja/PaymentDrivers/BasePaymentDriver.php | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Models/PaymentMethod.php b/app/Models/PaymentMethod.php index 1a0f865be141..5b9a64757c0c 100644 --- a/app/Models/PaymentMethod.php +++ b/app/Models/PaymentMethod.php @@ -113,9 +113,9 @@ class PaymentMethod extends EntityModel */ public function scopeClientId($query, $clientId) { - return $query->with(['contact' => function($query) use ($clientId) { - return $query->whereClientId($clientId); - }]); + $query->whereHas('contact', function($query) use ($clientId) { + $query->whereClientId($clientId); + }); } /** diff --git a/app/Ninja/PaymentDrivers/BasePaymentDriver.php b/app/Ninja/PaymentDrivers/BasePaymentDriver.php index baef52a7a34e..9cc7c405431f 100644 --- a/app/Ninja/PaymentDrivers/BasePaymentDriver.php +++ b/app/Ninja/PaymentDrivers/BasePaymentDriver.php @@ -531,6 +531,7 @@ class BasePaymentDriver public function createPaymentMethod($customer) { $paymentMethod = PaymentMethod::createNew($this->invitation); + $paymentMethod->contact_id = $this->contact()->id; $paymentMethod->ip = Request::ip(); $paymentMethod->account_gateway_token_id = $customer->id; $paymentMethod->setRelation('account_gateway_token', $customer); From 86c08f3af58d7e3deedd632cf796019f99879a06 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Fri, 15 Jul 2016 11:24:08 +0300 Subject: [PATCH 18/44] Working on new pricing --- app/Http/Controllers/AccountController.php | 132 ------------------ resources/lang/en/texts.php | 2 +- resources/views/accounts/management.blade.php | 5 + 3 files changed, 6 insertions(+), 133 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 6a0308b81dc9..7c4e736d5357 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -214,13 +214,6 @@ class AccountController extends BaseController return Redirect::to('view/' . $invitation->invitation_key); } else { - // create a credit - $credit = $credit - $newPlan['price']; - if ($credit > 0) { - $client = $this->accountRepo->getNinjaClient($account, $credit); - $this->accountRepo->createNinjaCredit(); - } - if ($plan != PLAN_FREE) { $company->plan_term = $term; $company->plan_price = $newPlan['price']; @@ -235,131 +228,6 @@ class AccountController extends BaseController } } - /* - public function changePlan() { - $user = Auth::user(); - $account = $user->account; - - $plan = Input::get('plan'); - $term = Input::get('plan_term'); - - $planDetails = $account->getPlanDetails(false, false); - - $credit = 0; - if ($planDetails) { - if ($planDetails['plan'] == PLAN_PRO && $plan == PLAN_ENTERPRISE) { - // Upgrade from pro to enterprise - if($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) { - // Upgrade to yearly for now; switch to monthly in a year - $pending_monthly = true; - $term = PLAN_TERM_YEARLY; - } - - $new_plan = [ - 'plan' => PLAN_ENTERPRISE, - 'term' => $term, - ]; - } elseif ($planDetails['plan'] == $plan) { - // Term switch - if ($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) { - $pending_change = [ - 'plan' => $plan, - 'term' => $term - ]; - } elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY - || $planDetails['num_users'] != Input::get('num_users')) { - $new_plan = [ - 'plan' => $plan, - 'term' => $term, - ]; - } else { - // Cancel the pending change - $account->company->pending_plan = null; - $account->company->pending_term = null; - $account->company->save(); - Session::flash('message', trans('texts.updated_plan')); - } - } elseif (!empty($planDetails['started'])) { - // Downgrade - $refund_deadline = clone $planDetails['started']; - $refund_deadline->modify('+30 days'); - - if ($plan == PLAN_FREE && $refund_deadline >= date_create()) { - // Refund - $account->company->plan = null; - $account->company->plan_term = null; - $account->company->plan_started = null; - $account->company->plan_expires = null; - $account->company->plan_paid = null; - $account->company->pending_plan = null; - $account->company->pending_term = null; - - if ($payment = $account->company->payment) { - $ninjaAccount = $this->accountRepo->getNinjaAccount(); - $paymentDriver = $ninjaAccount->paymentDriver(); - $paymentDriver->refundPayment($payment); - Session::flash('message', trans('texts.plan_refunded')); - \Log::info("Refunded Plan Payment: {$account->name} - {$user->email}"); - } else { - Session::flash('message', trans('texts.updated_plan')); - } - - $account->company->save(); - - } else { - $pending_change = [ - 'plan' => $plan, - 'term' => $plan == PLAN_FREE ? null : $term, - ]; - } - } - - if (!empty($new_plan) && $new_plan['plan'] != PLAN_FREE) { - $time_used = $planDetails['paid']->diff(date_create()); - $days_used = $time_used->days; - - if ($time_used->invert) { - // They paid in advance - $days_used *= -1; - } - - $days_total = $planDetails['paid']->diff($planDetails['expires'])->days; - - $percent_used = $days_used / $days_total; - $credit = $planDetails['plan_price'] * (1 - $percent_used); - } - } else { - $new_plan = [ - 'plan' => $plan, - 'term' => $term, - ]; - } - - if (!empty($pending_change) && empty($new_plan)) { - $pending_change['num_users'] = Input::get('num_users'); - $account->company->pending_plan = $pending_change['plan']; - $account->company->pending_term = $pending_change['term']; - $account->company->pending_num_users = $pending_change['num_users']; - $account->company->pending_plan_price = Utils::getPlanPrice($pending_change); - $account->company->save(); - - Session::flash('message', trans('texts.updated_plan')); - } - - if (!empty($new_plan) && $new_plan['plan'] != PLAN_FREE) { - $new_plan['num_users'] = 1; - if ($new_plan['plan'] == PLAN_ENTERPRISE) { - $new_plan['num_users'] = Input::get('num_users'); - } - $new_plan['price'] = Utils::getPlanPrice($new_plan); - $invitation = $this->accountRepo->enablePlan($new_plan, $credit, !empty($pending_monthly)); - return Redirect::to('view/'.$invitation->invitation_key); - } - - return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301); - } - */ - /** * @param $entityType diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index d250e07d5fc4..42ae69883455 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -2038,7 +2038,7 @@ $LANG = array( 'buy_now_buttons_warning' => 'Note: client and invoice records are created even if the transaction isn\'t completed.', 'buy_now_buttons_disabled' => 'This feature requires that a product is created and a payment gateway is configured.', 'enable_buy_now_buttons_help' => 'Enable support for buy now buttons', - + 'changes_take_effect_immediately' => 'Note: changes take effect immediately', ); return $LANG; diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index 1f6669d15c61..0b7b46dbea16 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -122,6 +122,11 @@