mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 01:17:33 -04:00 
			
		
		
		
	Merge branch 'v5-develop' into bank_rules
This commit is contained in:
		
						commit
						428f42c723
					
				| @ -42,7 +42,7 @@ class S3Cleanup extends Command | |||||||
|      */ |      */ | ||||||
|     public function handle() |     public function handle() | ||||||
|     { |     { | ||||||
|         if (! Ninja::isHosted()) { |         if (!Ninja::isHosted()) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,6 +41,25 @@ class SubscriptionPurchaseController extends Controller | |||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function upgrade(Subscription $subscription, Request $request) | ||||||
|  |     { | ||||||
|  |         /* Make sure the contact is logged into the correct company for this subscription */ | ||||||
|  |         if (auth()->guard('contact')->user() && auth()->guard('contact')->user()->company_id != $subscription->company_id) { | ||||||
|  |             auth()->guard('contact')->logout(); | ||||||
|  |             $request->session()->invalidate(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($request->has('locale')) { | ||||||
|  |             $this->setLocale($request->query('locale')); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return view('billing-portal.purchasev2', [ | ||||||
|  |             'subscription' => $subscription, | ||||||
|  |             'hash' => Str::uuid()->toString(), | ||||||
|  |             'request_data' => $request->all(), | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Set locale for incoming request. |      * Set locale for incoming request. | ||||||
|      * |      * | ||||||
| @ -56,4 +75,7 @@ class SubscriptionPurchaseController extends Controller | |||||||
|             App::setLocale($record->locale); |             App::setLocale($record->locale); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ use App\Http\Requests\Invoice\StoreInvoiceRequest; | |||||||
| use App\Http\Requests\Invoice\UpdateInvoiceRequest; | use App\Http\Requests\Invoice\UpdateInvoiceRequest; | ||||||
| use App\Http\Requests\Invoice\UpdateReminderRequest; | use App\Http\Requests\Invoice\UpdateReminderRequest; | ||||||
| use App\Http\Requests\Invoice\UploadInvoiceRequest; | use App\Http\Requests\Invoice\UploadInvoiceRequest; | ||||||
|  | use App\Jobs\Cron\AutoBill; | ||||||
| use App\Jobs\Entity\EmailEntity; | use App\Jobs\Entity\EmailEntity; | ||||||
| use App\Jobs\Invoice\BulkInvoiceJob; | use App\Jobs\Invoice\BulkInvoiceJob; | ||||||
| use App\Jobs\Invoice\StoreInvoice; | use App\Jobs\Invoice\StoreInvoice; | ||||||
| @ -696,11 +697,14 @@ class InvoiceController extends BaseController | |||||||
|     { |     { | ||||||
|         /*If we are using bulk actions, we don't want to return anything */ |         /*If we are using bulk actions, we don't want to return anything */ | ||||||
|         switch ($action) { |         switch ($action) { | ||||||
|  |             case 'auto_bill': | ||||||
|  |                 $invoice = AutoBill::dispatch($invoice->id, $invoice->company->db); | ||||||
|  |                 return $this->itemResponse($invoice); | ||||||
|  | 
 | ||||||
|             case 'clone_to_invoice': |             case 'clone_to_invoice': | ||||||
|                 $invoice = CloneInvoiceFactory::create($invoice, auth()->user()->id); |                 $invoice = CloneInvoiceFactory::create($invoice, auth()->user()->id); | ||||||
| 
 |  | ||||||
|                 return $this->itemResponse($invoice); |                 return $this->itemResponse($invoice); | ||||||
|                 break; |                  | ||||||
|             case 'clone_to_quote': |             case 'clone_to_quote': | ||||||
|                 $quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id); |                 $quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										528
									
								
								app/Http/Livewire/BillingPortalPurchasev2.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										528
									
								
								app/Http/Livewire/BillingPortalPurchasev2.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,528 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Invoice Ninja (https://invoiceninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Http\Livewire; | ||||||
|  | 
 | ||||||
|  | use App\Factory\ClientFactory; | ||||||
|  | use App\Jobs\Mail\NinjaMailerJob; | ||||||
|  | use App\Jobs\Mail\NinjaMailerObject; | ||||||
|  | use App\Libraries\MultiDB; | ||||||
|  | use App\Mail\ContactPasswordlessLogin; | ||||||
|  | use App\Models\Client; | ||||||
|  | use App\Models\ClientContact; | ||||||
|  | use App\Models\Invoice; | ||||||
|  | use App\Models\Subscription; | ||||||
|  | use App\Repositories\ClientContactRepository; | ||||||
|  | use App\Repositories\ClientRepository; | ||||||
|  | use Illuminate\Support\Facades\App; | ||||||
|  | use Illuminate\Support\Facades\Auth; | ||||||
|  | use Illuminate\Support\Facades\Cache; | ||||||
|  | use Illuminate\Support\Facades\DB; | ||||||
|  | use Illuminate\Support\Str; | ||||||
|  | use App\DataMapper\ClientSettings; | ||||||
|  | use Livewire\Component; | ||||||
|  | 
 | ||||||
|  | class BillingPortalPurchasev2 extends Component | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Random hash generated by backend to handle the tracking of state. | ||||||
|  |      * | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     public $hash; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Top level text on the left side of billing page. | ||||||
|  |      * | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     public $heading_text; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * E-mail address model for user input. | ||||||
|  |      * | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     public $email; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Password model for user input. | ||||||
|  |      * | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     public $password; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Instance of subscription. | ||||||
|  |      * | ||||||
|  |      * @var Subscription | ||||||
|  |      */ | ||||||
|  |     public $subscription; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Instance of client contact. | ||||||
|  |      * | ||||||
|  |      * @var null|ClientContact | ||||||
|  |      */ | ||||||
|  |     public $contact; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Rules for validating the form. | ||||||
|  |      * | ||||||
|  |      * @var \string[][] | ||||||
|  |      */ | ||||||
|  |     protected $rules = [ | ||||||
|  |         'email' => ['required', 'email'], | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Id for CompanyGateway record. | ||||||
|  |      * | ||||||
|  |      * @var string|integer | ||||||
|  |      */ | ||||||
|  |     public $company_gateway_id; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Id for GatewayType. | ||||||
|  |      * | ||||||
|  |      * @var string|integer | ||||||
|  |      */ | ||||||
|  |     public $payment_method_id; | ||||||
|  | 
 | ||||||
|  |     private $user_coupon; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * List of steps that frontend form follows. | ||||||
|  |      * | ||||||
|  |      * @var array | ||||||
|  |      */ | ||||||
|  |     public $steps = [ | ||||||
|  |         'passed_email' => false, | ||||||
|  |         'existing_user' => false, | ||||||
|  |         'fetched_payment_methods' => false, | ||||||
|  |         'fetched_client' => false, | ||||||
|  |         'show_start_trial' => false, | ||||||
|  |         'passwordless_login_sent' => false, | ||||||
|  |         'started_payment' => false, | ||||||
|  |         'discount_applied' => false, | ||||||
|  |         'show_loading_bar' => false, | ||||||
|  |         'not_eligible' => null, | ||||||
|  |         'not_eligible_message' => null, | ||||||
|  |         'payment_required' => true, | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * List of payment methods fetched from client. | ||||||
|  |      * | ||||||
|  |      * @var array | ||||||
|  |      */ | ||||||
|  |     public $methods = []; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Instance of \App\Models\Invoice | ||||||
|  |      * | ||||||
|  |      * @var Invoice | ||||||
|  |      */ | ||||||
|  |     public $invoice; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Coupon model for user input | ||||||
|  |      * | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     public $coupon; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Quantity for seats | ||||||
|  |      * | ||||||
|  |      * @var int | ||||||
|  |      */ | ||||||
|  |     public $quantity; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * First-hit request data (queries, locales...). | ||||||
|  |      * | ||||||
|  |      * @var array | ||||||
|  |      */ | ||||||
|  |     public $request_data; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     public $price; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Disabled state of passwordless login button. | ||||||
|  |      * | ||||||
|  |      * @var bool | ||||||
|  |      */ | ||||||
|  |     public $passwordless_login_btn = false; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Instance of company. | ||||||
|  |      * | ||||||
|  |      * @var Company | ||||||
|  |      */ | ||||||
|  |     public $company; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Campaign reference. | ||||||
|  |      * | ||||||
|  |      * @var string|null | ||||||
|  |      */ | ||||||
|  |     public $campaign; | ||||||
|  | 
 | ||||||
|  |     public function mount() | ||||||
|  |     { | ||||||
|  |         MultiDB::setDb($this->company->db); | ||||||
|  | 
 | ||||||
|  |         $this->quantity = 1; | ||||||
|  | 
 | ||||||
|  |         $this->price = $this->subscription->price; | ||||||
|  | 
 | ||||||
|  |         if (request()->query('coupon')) { | ||||||
|  |             $this->coupon = request()->query('coupon'); | ||||||
|  |             $this->handleCoupon(); | ||||||
|  |         } | ||||||
|  |         elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){ | ||||||
|  |             $this->price = $this->subscription->promo_price; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Handle user authentication | ||||||
|  |      * | ||||||
|  |      * @return $this|bool|void | ||||||
|  |      */ | ||||||
|  |     public function authenticate() | ||||||
|  |     { | ||||||
|  |         $this->validate(); | ||||||
|  | 
 | ||||||
|  |         $contact = ClientContact::where('email', $this->email) | ||||||
|  |             ->where('company_id', $this->subscription->company_id) | ||||||
|  |             ->first(); | ||||||
|  | 
 | ||||||
|  |         if ($contact && $this->steps['existing_user'] === false) { | ||||||
|  |             return $this->steps['existing_user'] = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($contact && $this->steps['existing_user']) { | ||||||
|  |             $attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password, 'company_id' => $this->subscription->company_id]); | ||||||
|  | 
 | ||||||
|  |             return $attempt | ||||||
|  |                 ? $this->getPaymentMethods($contact) | ||||||
|  |                 : session()->flash('message', 'These credentials do not match our records.'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->steps['existing_user'] = false; | ||||||
|  | 
 | ||||||
|  |         $contact = $this->createBlankClient(); | ||||||
|  | 
 | ||||||
|  |         if ($contact && $contact instanceof ClientContact) { | ||||||
|  |             $this->getPaymentMethods($contact); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create a blank client. Used for new customers purchasing. | ||||||
|  |      * | ||||||
|  |      * @return mixed | ||||||
|  |      * @throws \Laracasts\Presenter\Exceptions\PresenterException | ||||||
|  |      */ | ||||||
|  |     protected function createBlankClient() | ||||||
|  |     { | ||||||
|  |         $company = $this->subscription->company; | ||||||
|  |         $user = $this->subscription->user; | ||||||
|  |         $user->setCompany($company); | ||||||
|  |          | ||||||
|  |         $client_repo = new ClientRepository(new ClientContactRepository()); | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'name' => '', | ||||||
|  |             'contacts' => [ | ||||||
|  |                 ['email' => $this->email], | ||||||
|  |             ], | ||||||
|  |             'client_hash' => Str::random(40), | ||||||
|  |             'settings' => ClientSettings::defaults(), | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         foreach ($this->request_data as $field => $value) { | ||||||
|  |             if (in_array($field, Client::$subscriptions_fillable)) { | ||||||
|  |                 $data[$field] = $value; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (in_array($field, ClientContact::$subscription_fillable)) { | ||||||
|  |                 $data['contacts'][0][$field] = $value; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | // nlog($this->subscription->group_settings->settings);
 | ||||||
|  | // nlog($this->subscription->group_settings->settings->currency_id);
 | ||||||
|  | 
 | ||||||
|  |         if(array_key_exists('currency_id', $this->request_data)) { | ||||||
|  | 
 | ||||||
|  |             $currency = Cache::get('currencies')->filter(function ($item){ | ||||||
|  |                 return $item->id == $this->request_data['currency_id']; | ||||||
|  |             })->first(); | ||||||
|  | 
 | ||||||
|  |             if($currency) | ||||||
|  |                 $data['settings']->currency_id = $currency->id; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |         elseif($this->subscription->group_settings && property_exists($this->subscription->group_settings->settings, 'currency_id')) { | ||||||
|  | 
 | ||||||
|  |             $currency = Cache::get('currencies')->filter(function ($item){ | ||||||
|  |                 return $item->id == $this->subscription->group_settings->settings->currency_id; | ||||||
|  |             })->first(); | ||||||
|  | 
 | ||||||
|  |             if($currency) | ||||||
|  |                 $data['settings']->currency_id = $currency->id; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (array_key_exists('locale', $this->request_data)) { | ||||||
|  |             $request = $this->request_data; | ||||||
|  | 
 | ||||||
|  |             $record = Cache::get('languages')->filter(function ($item) use ($request) { | ||||||
|  |                 return $item->locale == $request['locale']; | ||||||
|  |             })->first(); | ||||||
|  | 
 | ||||||
|  |             if ($record) { | ||||||
|  |                 $data['settings']['language_id'] = (string)$record->id; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $client = $client_repo->save($data, ClientFactory::create($company->id, $user->id)); | ||||||
|  | 
 | ||||||
|  |         return $client->fresh()->contacts->first(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetching payment methods from the client. | ||||||
|  |      * | ||||||
|  |      * @param ClientContact $contact | ||||||
|  |      * @return $this | ||||||
|  |      */ | ||||||
|  |     protected function getPaymentMethods(ClientContact $contact): self | ||||||
|  |     { | ||||||
|  |         Auth::guard('contact')->loginUsingId($contact->id, true); | ||||||
|  | 
 | ||||||
|  |         $this->contact = $contact; | ||||||
|  | 
 | ||||||
|  |         if ($this->subscription->trial_enabled) { | ||||||
|  |             $this->heading_text = ctrans('texts.plan_trial'); | ||||||
|  |             $this->steps['show_start_trial'] = true; | ||||||
|  | 
 | ||||||
|  |             return $this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ((int)$this->price == 0) | ||||||
|  |             $this->steps['payment_required'] = false; | ||||||
|  |         else | ||||||
|  |             $this->steps['fetched_payment_methods'] = true; | ||||||
|  | 
 | ||||||
|  |         $this->methods = $contact->client->service()->getPaymentMethods($this->price); | ||||||
|  | 
 | ||||||
|  |         $this->heading_text = ctrans('texts.payment_methods'); | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Middle method between selecting payment method & | ||||||
|  |      * submitting the from to the backend. | ||||||
|  |      * | ||||||
|  |      * @param $company_gateway_id | ||||||
|  |      * @param $gateway_type_id | ||||||
|  |      */ | ||||||
|  |     public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id) | ||||||
|  |     { | ||||||
|  |         $this->company_gateway_id = $company_gateway_id; | ||||||
|  |         $this->payment_method_id = $gateway_type_id; | ||||||
|  | 
 | ||||||
|  |         $this->handleBeforePaymentEvents(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Method to handle events before payments. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function handleBeforePaymentEvents() | ||||||
|  |     { | ||||||
|  |         $this->steps['started_payment'] = true; | ||||||
|  |         $this->steps['show_loading_bar'] = true; | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'client_id' => $this->contact->client->id, | ||||||
|  |             'date' => now()->format('Y-m-d'), | ||||||
|  |             'invitations' => [[ | ||||||
|  |                 'key' => '', | ||||||
|  |                 'client_contact_id' => $this->contact->hashed_id, | ||||||
|  |             ]], | ||||||
|  |             'user_input_promo_code' => $this->coupon, | ||||||
|  |             'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon, | ||||||
|  |             'quantity' => $this->quantity, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $is_eligible = $this->subscription->service()->isEligible($this->contact); | ||||||
|  | 
 | ||||||
|  |         if (is_array($is_eligible) && $is_eligible['message'] != 'Success') { | ||||||
|  |             $this->steps['not_eligible'] = true; | ||||||
|  |             $this->steps['not_eligible_message'] = $is_eligible['message']; | ||||||
|  |             $this->steps['show_loading_bar'] = false; | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->invoice = $this->subscription | ||||||
|  |             ->service() | ||||||
|  |             ->createInvoice($data, $this->quantity) | ||||||
|  |             ->service() | ||||||
|  |             ->markSent() | ||||||
|  |             ->fillDefaults() | ||||||
|  |             ->adjustInventory() | ||||||
|  |             ->save(); | ||||||
|  | 
 | ||||||
|  |         Cache::put($this->hash, [ | ||||||
|  |             'subscription_id' => $this->subscription->id, | ||||||
|  |             'email' => $this->email ?? $this->contact->email, | ||||||
|  |             'client_id' => $this->contact->client->id, | ||||||
|  |             'invoice_id' => $this->invoice->id, | ||||||
|  |             'context' => 'purchase', | ||||||
|  |             'campaign' => $this->campaign, | ||||||
|  |         ], now()->addMinutes(60)); | ||||||
|  | 
 | ||||||
|  |         $this->emit('beforePaymentEventsCompleted'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Proxy method for starting the trial. | ||||||
|  |      * | ||||||
|  |      * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||||||
|  |      */ | ||||||
|  |     public function handleTrial() | ||||||
|  |     { | ||||||
|  |         return $this->subscription->service()->startTrial([ | ||||||
|  |             'email' => $this->email ?? $this->contact->email, | ||||||
|  |             'quantity' => $this->quantity, | ||||||
|  |             'contact_id' => $this->contact->id, | ||||||
|  |             'client_id' => $this->contact->client->id, | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function handlePaymentNotRequired() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $is_eligible = $this->subscription->service()->isEligible($this->contact); | ||||||
|  |          | ||||||
|  |         if ($is_eligible['status_code'] != 200) { | ||||||
|  |             $this->steps['not_eligible'] = true; | ||||||
|  |             $this->steps['not_eligible_message'] = $is_eligible['message']; | ||||||
|  |             $this->steps['show_loading_bar'] = false; | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         return $this->subscription->service()->handleNoPaymentRequired([ | ||||||
|  |             'email' => $this->email ?? $this->contact->email, | ||||||
|  |             'quantity' => $this->quantity, | ||||||
|  |             'contact_id' => $this->contact->id, | ||||||
|  |             'client_id' => $this->contact->client->id, | ||||||
|  |             'coupon' => $this->coupon, | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Update quantity property. | ||||||
|  |      * | ||||||
|  |      * @param string $option | ||||||
|  |      * @return int | ||||||
|  |      */ | ||||||
|  |     public function updateQuantity(string $option): int | ||||||
|  |     { | ||||||
|  |         $this->handleCoupon(); | ||||||
|  | 
 | ||||||
|  |         if ($this->quantity == 1 && $option == 'decrement') { | ||||||
|  |             $this->price = $this->price * 1; | ||||||
|  |             return $this->quantity; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($this->quantity > $this->subscription->max_seats_limit && $option == 'increment') { | ||||||
|  |             $this->price = $this->price * $this->subscription->max_seats_limit; | ||||||
|  |             return $this->quantity; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($option == 'increment') { | ||||||
|  |             $this->quantity++; | ||||||
|  |             $this->price = $this->price * $this->quantity; | ||||||
|  |             return $this->quantity; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |             $this->quantity--; | ||||||
|  |             $this->price = $this->price * $this->quantity; | ||||||
|  | 
 | ||||||
|  |             return $this->quantity; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function handleCoupon() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         if($this->steps['discount_applied']){ | ||||||
|  |             $this->price = $this->subscription->promo_price; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($this->coupon == $this->subscription->promo_code) { | ||||||
|  |             $this->price = $this->subscription->promo_price; | ||||||
|  |             $this->quantity = 1; | ||||||
|  |             $this->steps['discount_applied'] = true; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             $this->price = $this->subscription->price; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function passwordlessLogin() | ||||||
|  |     { | ||||||
|  |         $this->passwordless_login_btn = true; | ||||||
|  | 
 | ||||||
|  |         $contact = ClientContact::query() | ||||||
|  |             ->where('email', $this->email) | ||||||
|  |             ->where('company_id', $this->subscription->company_id) | ||||||
|  |             ->first(); | ||||||
|  | 
 | ||||||
|  |         $mailer = new NinjaMailerObject(); | ||||||
|  |         $mailer->mailable = new ContactPasswordlessLogin($this->email, $this->subscription->company, (string)route('client.subscription.purchase', $this->subscription->hashed_id) . '?coupon=' . $this->coupon); | ||||||
|  |         $mailer->company = $this->subscription->company; | ||||||
|  |         $mailer->settings = $this->subscription->company->settings; | ||||||
|  |         $mailer->to_user = $contact; | ||||||
|  | 
 | ||||||
|  |         NinjaMailerJob::dispatch($mailer); | ||||||
|  | 
 | ||||||
|  |         $this->steps['passwordless_login_sent'] = true; | ||||||
|  |         $this->passwordless_login_btn = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         if (array_key_exists('email', $this->request_data)) { | ||||||
|  |             $this->email = $this->request_data['email']; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($this->contact instanceof ClientContact) { | ||||||
|  |             $this->getPaymentMethods($this->contact); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return render('components.livewire.billing-portal-purchasev2'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -24,7 +24,7 @@ class GenerateSmsRequest extends Request | |||||||
|      */ |      */ | ||||||
|     public function authorize() : bool |     public function authorize() : bool | ||||||
|     { |     { | ||||||
|         return auth()->user()->isAdmin(); |         return auth()->user(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -54,6 +54,9 @@ class Subscription extends BaseModel | |||||||
|         'price', |         'price', | ||||||
|         'name', |         'name', | ||||||
|         'currency_id', |         'currency_id', | ||||||
|  |         'registration_required', | ||||||
|  |         'optional_product_ids', | ||||||
|  |         'optional_recurring_product_ids', | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     protected $casts = [ |     protected $casts = [ | ||||||
|  | |||||||
| @ -67,6 +67,7 @@ class BankTransactionTransformer extends EntityTransformer | |||||||
|             'invoice_ids' => (string) $bank_transaction->invoice_ids ?: '', |             'invoice_ids' => (string) $bank_transaction->invoice_ids ?: '', | ||||||
|             'expense_id'=> (string) $this->encodePrimaryKey($bank_transaction->expense_id) ?: '', |             'expense_id'=> (string) $this->encodePrimaryKey($bank_transaction->expense_id) ?: '', | ||||||
|             'vendor_id'=> (string) $this->encodePrimaryKey($bank_transaction->vendor_id) ?: '', |             'vendor_id'=> (string) $this->encodePrimaryKey($bank_transaction->vendor_id) ?: '', | ||||||
|  |             'bank_rule_id' => (string) $this->encodePrimaryKey($bank_transaction->bank_rule_id) ?: '', | ||||||
|             'is_deleted' => (bool) $bank_transaction->is_deleted, |             'is_deleted' => (bool) $bank_transaction->is_deleted, | ||||||
|             'created_at' => (int) $bank_transaction->created_at, |             'created_at' => (int) $bank_transaction->created_at, | ||||||
|             'updated_at' => (int) $bank_transaction->updated_at, |             'updated_at' => (int) $bank_transaction->updated_at, | ||||||
|  | |||||||
| @ -27,6 +27,15 @@ return new class extends Migration | |||||||
|         { |         { | ||||||
|             $table->bigInteger('bank_rule_id')->nullable(); |             $table->bigInteger('bank_rule_id')->nullable(); | ||||||
|         }); |         }); | ||||||
|  | 
 | ||||||
|  |         Schema::table('subscriptions', function (Blueprint $table) | ||||||
|  |         { | ||||||
|  |             $table->boolean('registration_required')->default(false); | ||||||
|  |             $table->text('optional_product_ids')->nullable(); | ||||||
|  |             $table->text('optional_recurring_product_ids')->nullable(); | ||||||
|  |              | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
							
								
								
									
										65
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										65
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -5,6 +5,8 @@ | |||||||
|     "packages": { |     "packages": { | ||||||
|         "": { |         "": { | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|  |                 "@tailwindcss/forms": "^0.3.4", | ||||||
|  |                 "@tailwindcss/line-clamp": "^0.3.1", | ||||||
|                 "autoprefixer": "^10.3.7", |                 "autoprefixer": "^10.3.7", | ||||||
|                 "axios": "^0.25", |                 "axios": "^0.25", | ||||||
|                 "card-js": "^1.0.13", |                 "card-js": "^1.0.13", | ||||||
| @ -25,6 +27,7 @@ | |||||||
|             "devDependencies": { |             "devDependencies": { | ||||||
|                 "@babel/compat-data": "7.15.0", |                 "@babel/compat-data": "7.15.0", | ||||||
|                 "@babel/plugin-proposal-class-properties": "^7.14.5", |                 "@babel/plugin-proposal-class-properties": "^7.14.5", | ||||||
|  |                 "@tailwindcss/aspect-ratio": "^0.4.2", | ||||||
|                 "laravel-mix-purgecss": "^6.0.0", |                 "laravel-mix-purgecss": "^6.0.0", | ||||||
|                 "vue-template-compiler": "^2.6.14" |                 "vue-template-compiler": "^2.6.14" | ||||||
|             } |             } | ||||||
| @ -1682,6 +1685,34 @@ | |||||||
|                 "node": ">= 8" |                 "node": ">= 8" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/@tailwindcss/aspect-ratio": { | ||||||
|  |             "version": "0.4.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", | ||||||
|  |             "integrity": "sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "peerDependencies": { | ||||||
|  |                 "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/@tailwindcss/forms": { | ||||||
|  |             "version": "0.3.4", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.3.4.tgz", | ||||||
|  |             "integrity": "sha512-vlAoBifNJUkagB+PAdW4aHMe4pKmSLroH398UPgIogBFc91D2VlHUxe4pjxQhiJl0Nfw53sHSJSQBSTQBZP3vA==", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "mini-svg-data-uri": "^1.2.3" | ||||||
|  |             }, | ||||||
|  |             "peerDependencies": { | ||||||
|  |                 "tailwindcss": ">=2.0.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/@tailwindcss/line-clamp": { | ||||||
|  |             "version": "0.3.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.1.tgz", | ||||||
|  |             "integrity": "sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==", | ||||||
|  |             "peerDependencies": { | ||||||
|  |                 "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/@trysound/sax": { |         "node_modules/@trysound/sax": { | ||||||
|             "version": "0.2.0", |             "version": "0.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", |             "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", | ||||||
| @ -5742,6 +5773,14 @@ | |||||||
|                 "url": "https://opencollective.com/webpack" |                 "url": "https://opencollective.com/webpack" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/mini-svg-data-uri": { | ||||||
|  |             "version": "1.4.4", | ||||||
|  |             "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", | ||||||
|  |             "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", | ||||||
|  |             "bin": { | ||||||
|  |                 "mini-svg-data-uri": "cli.js" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/minimalistic-assert": { |         "node_modules/minimalistic-assert": { | ||||||
|             "version": "1.0.1", |             "version": "1.0.1", | ||||||
|             "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", |             "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", | ||||||
| @ -10285,6 +10324,27 @@ | |||||||
|                 "fastq": "^1.6.0" |                 "fastq": "^1.6.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "@tailwindcss/aspect-ratio": { | ||||||
|  |             "version": "0.4.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", | ||||||
|  |             "integrity": "sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": {} | ||||||
|  |         }, | ||||||
|  |         "@tailwindcss/forms": { | ||||||
|  |             "version": "0.3.4", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.3.4.tgz", | ||||||
|  |             "integrity": "sha512-vlAoBifNJUkagB+PAdW4aHMe4pKmSLroH398UPgIogBFc91D2VlHUxe4pjxQhiJl0Nfw53sHSJSQBSTQBZP3vA==", | ||||||
|  |             "requires": { | ||||||
|  |                 "mini-svg-data-uri": "^1.2.3" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "@tailwindcss/line-clamp": { | ||||||
|  |             "version": "0.3.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.1.tgz", | ||||||
|  |             "integrity": "sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==", | ||||||
|  |             "requires": {} | ||||||
|  |         }, | ||||||
|         "@trysound/sax": { |         "@trysound/sax": { | ||||||
|             "version": "0.2.0", |             "version": "0.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", |             "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", | ||||||
| @ -13399,6 +13459,11 @@ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "mini-svg-data-uri": { | ||||||
|  |             "version": "1.4.4", | ||||||
|  |             "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", | ||||||
|  |             "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" | ||||||
|  |         }, | ||||||
|         "minimalistic-assert": { |         "minimalistic-assert": { | ||||||
|             "version": "1.0.1", |             "version": "1.0.1", | ||||||
|             "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", |             "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", | ||||||
|  | |||||||
| @ -11,10 +11,13 @@ | |||||||
|     "devDependencies": { |     "devDependencies": { | ||||||
|         "@babel/compat-data": "7.15.0", |         "@babel/compat-data": "7.15.0", | ||||||
|         "@babel/plugin-proposal-class-properties": "^7.14.5", |         "@babel/plugin-proposal-class-properties": "^7.14.5", | ||||||
|  |         "@tailwindcss/aspect-ratio": "^0.4.2", | ||||||
|         "laravel-mix-purgecss": "^6.0.0", |         "laravel-mix-purgecss": "^6.0.0", | ||||||
|         "vue-template-compiler": "^2.6.14" |         "vue-template-compiler": "^2.6.14" | ||||||
|     }, |     }, | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|  |         "@tailwindcss/line-clamp": "^0.3.1", | ||||||
|  |         "@tailwindcss/forms": "^0.3.4", | ||||||
|         "autoprefixer": "^10.3.7", |         "autoprefixer": "^10.3.7", | ||||||
|         "axios": "^0.25", |         "axios": "^0.25", | ||||||
|         "card-js": "^1.0.13", |         "card-js": "^1.0.13", | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/js/app.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/js/app.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/js/setup/setup.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/js/setup/setup.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -1,5 +1,5 @@ | |||||||
| { | { | ||||||
|     "/js/app.js": "/js/app.js?id=384185bf9d293949134d09b890c81369", |     "/js/app.js": "/js/app.js?id=19300612c6880925e8043b61e8d49632", | ||||||
|     "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=9fb77e87fe0f85a367050e08f79ec9df", |     "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=9fb77e87fe0f85a367050e08f79ec9df", | ||||||
|     "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=803182f668c39d631ca5c55437876da4", |     "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=803182f668c39d631ca5c55437876da4", | ||||||
|     "/js/clients/payments/forte-credit-card-payment.js": "/js/clients/payments/forte-credit-card-payment.js?id=6e9f466c5504d3753f9b4ffc6f947095", |     "/js/clients/payments/forte-credit-card-payment.js": "/js/clients/payments/forte-credit-card-payment.js?id=6e9f466c5504d3753f9b4ffc6f947095", | ||||||
| @ -15,7 +15,7 @@ | |||||||
|     "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=6fb63bae43d077b5061f4dadfe8dffc8", |     "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=6fb63bae43d077b5061f4dadfe8dffc8", | ||||||
|     "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=cdc76607aaf0b47a5a4e554e4177713d", |     "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=cdc76607aaf0b47a5a4e554e4177713d", | ||||||
|     "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=809de47258a681f0ffebe787dd6a9a93", |     "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=809de47258a681f0ffebe787dd6a9a93", | ||||||
|     "/js/setup/setup.js": "/js/setup/setup.js?id=87367cce4927b42a92defdbae7a64711", |     "/js/setup/setup.js": "/js/setup/setup.js?id=27560b012f166f8b9417ced2188aab70", | ||||||
|     "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314fb8357e5be63b", |     "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314fb8357e5be63b", | ||||||
|     "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=be5307abc990bb44f2f92628103b1d98", |     "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=be5307abc990bb44f2f92628103b1d98", | ||||||
|     "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12ca45acddacd72", |     "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12ca45acddacd72", | ||||||
| @ -42,7 +42,7 @@ | |||||||
|     "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=3d53d2f7d0291d9f92cf7414dd2d351c", |     "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=3d53d2f7d0291d9f92cf7414dd2d351c", | ||||||
|     "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=db71055862995fd6ae21becfc587a3de", |     "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=db71055862995fd6ae21becfc587a3de", | ||||||
|     "/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js?id=914a6846ad1e5584635e7430fef76875", |     "/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js?id=914a6846ad1e5584635e7430fef76875", | ||||||
|     "/css/app.css": "/css/app.css?id=6bafb560444b3b12f8d1ce59bd7fd703", |     "/css/app.css": "/css/app.css?id=2c1ff2517e9909ca83760beb295535be", | ||||||
|     "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ada60afcedcb7c", |     "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ada60afcedcb7c", | ||||||
|     "/vendor/clipboard.min.js": "/vendor/clipboard.min.js?id=15f52a1ee547f2bdd46e56747332ca2d" |     "/vendor/clipboard.min.js": "/vendor/clipboard.min.js?id=15f52a1ee547f2bdd46e56747332ca2d" | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								resources/views/billing-portal/purchasev2.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								resources/views/billing-portal/purchasev2.blade.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | @extends('portal.ninja2020.layout.clean') | ||||||
|  | @section('meta_title', ctrans('texts.purchase')) | ||||||
|  | 
 | ||||||
|  | @section('body') | ||||||
|  |     @livewire('billing-portal-purchasev2', ['subscription' => $subscription, 'company' => $subscription->company, 'contact' => auth()->guard('contact')->user(), 'hash' => $hash, 'request_data' => $request_data, 'campaign' => request()->query('campaign') ?? null]) | ||||||
|  | @stop | ||||||
|  | 
 | ||||||
|  | @push('footer') | ||||||
|  |     <script> | ||||||
|  |         function updateGatewayFields(companyGatewayId, paymentMethodId) { | ||||||
|  |             document.getElementById('company_gateway_id').value = companyGatewayId; | ||||||
|  |             document.getElementById('payment_method_id').value = paymentMethodId; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Livewire.on('beforePaymentEventsCompleted', () => document.getElementById('payment-method-form').submit()); | ||||||
|  |     </script> | ||||||
|  | @endpush | ||||||
| @ -0,0 +1,152 @@ | |||||||
|  | <style type="text/css"> | ||||||
|  |      | ||||||
|  | </style> | ||||||
|  | 
 | ||||||
|  | <div class="grid grid-cols-12"> | ||||||
|  |     <div class="col-span-12 xl:col-span-8 bg-gray-50 flex flex-col max-h-100px items-center"> | ||||||
|  |         <div class="w-full p-8 md:max-w-3xl"> | ||||||
|  |             <img class="object-scale-down" style="max-height: 100px;"src="{{ $subscription->company->present()->logo }}" alt="{{ $subscription->company->present()->name }}"> | ||||||
|  | 
 | ||||||
|  |             <h1 id="billing-page-company-logo" class="text-3xl font-bold tracking-wide mt-6"> | ||||||
|  |             {{ $subscription->name }} | ||||||
|  |             </h1> | ||||||
|  |         </div> | ||||||
|  |         <div class="w-full p-4 md:max-w-3xl"> | ||||||
|  |             @if(!empty($subscription->recurring_product_ids)) | ||||||
|  |                 <p | ||||||
|  |                     class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center rounded-full text-xs font-medium"> | ||||||
|  |                     {{ ctrans('texts.recurring_purchases') }} | ||||||
|  |                 </p> | ||||||
|  |               <ul role="list" class="divide-y divide-gray-200 bg-white"> | ||||||
|  |                 @foreach($subscription->service()->recurring_products() as $product) | ||||||
|  |                 <li> | ||||||
|  |                   <a href="#" class="block hover:bg-gray-50"> | ||||||
|  |                     <div class="px-4 py-4 sm:px-6"> | ||||||
|  |                       <div class="flex items-center justify-between"> | ||||||
|  |                         <div class="ml-2 flex flex-shrink-0"> | ||||||
|  |                           <p class="inline-flex rounded-full bg-green-100 px-2 text-xs font-semibold leading-5 text-green-800"></p> | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                       <div class="mt-0 sm:flex sm:justify-between"> | ||||||
|  |                         <div class="sm:flex"> | ||||||
|  |                           <p class="text-sm font-medium text-gray-900 mt-0">{!! nl2br($product->notes) !!}</p> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0"> | ||||||
|  |                             <span data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }} / {{ App\Models\RecurringInvoice::frequencyForKey($subscription->frequency_id) }}</span> | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                     </div> | ||||||
|  |                   </a> | ||||||
|  |                 </li> | ||||||
|  |                 @endforeach | ||||||
|  |             </ul> | ||||||
|  |             @endif | ||||||
|  |         </div> | ||||||
|  |         <div class="w-full p-4 md:max-w-3xl"> | ||||||
|  | 
 | ||||||
|  |             @if(!empty($subscription->product_ids)) | ||||||
|  |                 <p | ||||||
|  |                     class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center rounded-full text-xs font-medium"> | ||||||
|  |                     {{ ctrans('texts.one_time_purchases') }} | ||||||
|  |                 </p> | ||||||
|  |                 <ul role="list" class="divide-y divide-gray-200 bg-white"> | ||||||
|  |                     @foreach($subscription->service()->products() as $product) | ||||||
|  |                     <li> | ||||||
|  |                       <a href="#" class="block hover:bg-gray-50"> | ||||||
|  |                         <div class="px-4 py-4 sm:px-6"> | ||||||
|  |                           <div class="flex items-center justify-between"> | ||||||
|  |                             <p class="truncate text-sm font-medium text-gray-600"></p> | ||||||
|  |                             <div class="ml-2 flex flex-shrink-0"> | ||||||
|  |                               <p class="inline-flex rounded-full bg-green-100 px-2 text-xs font-semibold leading-5 text-green-800"></p> | ||||||
|  |                             </div> | ||||||
|  |                           </div> | ||||||
|  |                           <div class="mt-2 sm:flex sm:justify-between"> | ||||||
|  |                             <div class="sm:flex"> | ||||||
|  |                               <p class="text-sm font-medium text-gray-900 mt-2">{!! nl2br($product->notes) !!}</p> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0"> | ||||||
|  |                                 <span data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span> | ||||||
|  |                             </div> | ||||||
|  |                           </div> | ||||||
|  |                         </div> | ||||||
|  |                       </a> | ||||||
|  |                     </li> | ||||||
|  |                     @endforeach | ||||||
|  |                 </ul> | ||||||
|  |             @endif | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="relative mt-8"> | ||||||
|  |             <div class="absolute inset-0 flex items-center"> | ||||||
|  |                 <div class="w-full border-t border-gray-300"></div> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <div class="relative flex justify-center text-sm leading-5"> | ||||||
|  |                 <h1 class="text-2xl font-bold tracking-wide bg-gray-50 px-6 py-0">Optional products</h1> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="w-full p-4 md:max-w-3xl"> | ||||||
|  |             @if(!empty($subscription->recurring_product_ids)) | ||||||
|  |                 @foreach($subscription->service()->recurring_products() as $product) | ||||||
|  |                 <div class="flex items-center justify-between mb-4 bg-white rounded px-6 py-4 shadow-sm border"> | ||||||
|  |                     <div class="text-sm">{!! nl2br($product->notes) !!}</div> | ||||||
|  |                     <div data-ref="price-and-quantity-container"> | ||||||
|  |                         <span data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }} / {{ App\Models\RecurringInvoice::frequencyForKey($subscription->frequency_id) }}</span> | ||||||
|  |                         {{--                                <span data-ref="quantity" class="text-sm">(1x)</span>--}} | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 @endforeach | ||||||
|  |             @endif | ||||||
|  |         </div> | ||||||
|  |         <div class="w-full p-4 md:max-w-3xl"> | ||||||
|  | 
 | ||||||
|  |             @if(!empty($subscription->product_ids)) | ||||||
|  |                 @foreach($subscription->service()->products() as $product) | ||||||
|  |                     <div class="flex items-center justify-between mb-4 bg-white rounded px-6 py-4 shadow-sm border"> | ||||||
|  |                         <div class="text-sm">{!! nl2br($product->notes) !!}</div> | ||||||
|  |                         <div data-ref="price-and-quantity-container"> | ||||||
|  |                             <span | ||||||
|  |                                 data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span> | ||||||
|  |                             {{--                                <span data-ref="quantity" class="text-sm">(1x)</span>--}} | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 @endforeach | ||||||
|  |             @endif | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     <div class="col-span-12 xl:col-span-4 bg-blue-500 flex flex-col item-center "> | ||||||
|  |         <div class="w-full p-4 md:max-w-3xl"> | ||||||
|  |             <div id="summary" class="w-1/4 px-8 text-white"> | ||||||
|  |                 <h1 class="font-semibold text-2xl border-b pb-8 text-white">Order Summary</h1> | ||||||
|  |                 <div class="flex justify-between mt-10 mb-5"> | ||||||
|  |                   <span class="font-semibold text-sm uppercase">Items 3</span> | ||||||
|  |                   <span class="font-semibold text-sm">590$</span> | ||||||
|  |                 </div> | ||||||
|  |                 <div> | ||||||
|  |                   <label class="font-medium inline-block mb-3 text-sm uppercase">Shipping</label> | ||||||
|  |                   <select class="block p-2 text-white w-full text-sm"> | ||||||
|  |                     <option>Standard shipping - $10.00</option> | ||||||
|  |                   </select> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="py-10"> | ||||||
|  |                   <label for="promo" class="font-semibold inline-block mb-3 text-sm uppercase">Promo Code</label> | ||||||
|  |                   <input type="text" id="promo" placeholder="Enter your code" class="p-2 text-sm w-full"> | ||||||
|  |                 </div> | ||||||
|  |                 <button class="bg-white hover:bg-gray-600 px-5 py-2 text-sm text-blue-500 uppercase">Apply</button> | ||||||
|  |                 <div class="border-t mt-8"> | ||||||
|  |                   <div class="flex font-semibold justify-between py-6 text-sm uppercase"> | ||||||
|  |                     <span>Total cost</span> | ||||||
|  |                     <span>$600</span> | ||||||
|  |                   </div> | ||||||
|  |                   <button class="bg-white font-semibold hover:bg-gray-600 py-3 text-sm text-blue-500 uppercase w-full">Checkout</button> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
| @ -115,6 +115,8 @@ Route::post('payments/process/response', [App\Http\Controllers\ClientPortal\Paym | |||||||
| Route::get('payments/process/response', [App\Http\Controllers\ClientPortal\PaymentController::class, 'response'])->name('client.payments.response.get')->middleware(['locale', 'domain_db', 'verify_hash']); | Route::get('payments/process/response', [App\Http\Controllers\ClientPortal\PaymentController::class, 'response'])->name('client.payments.response.get')->middleware(['locale', 'domain_db', 'verify_hash']); | ||||||
| 
 | 
 | ||||||
| Route::get('client/subscriptions/{subscription}/purchase', [App\Http\Controllers\ClientPortal\SubscriptionPurchaseController::class, 'index'])->name('client.subscription.purchase')->middleware('domain_db'); | Route::get('client/subscriptions/{subscription}/purchase', [App\Http\Controllers\ClientPortal\SubscriptionPurchaseController::class, 'index'])->name('client.subscription.purchase')->middleware('domain_db'); | ||||||
|  | Route::get('client/subscriptions/{subscription}/purchase/v2', [App\Http\Controllers\ClientPortal\SubscriptionPurchaseController::class, 'upgrade'])->name('client.subscription.upgrade')->middleware('domain_db'); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () { | Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () { | ||||||
|     /*Invitation catches*/ |     /*Invitation catches*/ | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								tailwind.config.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								tailwind.config.js
									
									
									
									
										vendored
									
									
								
							| @ -17,5 +17,9 @@ module.exports = { | |||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     variants: {}, |     variants: {}, | ||||||
|     plugins: [] |     plugins: [ | ||||||
|  |         require('@tailwindcss/line-clamp'), | ||||||
|  |         require('@tailwindcss/forms') | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user