Merge pull request #6629 from turbo124/v5-stable

v5.3.10
This commit is contained in:
David Bomba 2021-09-12 15:43:14 +10:00 committed by GitHub
commit d125be6a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 305408 additions and 304556 deletions

View File

@ -1 +1 @@
5.3.9
5.3.10

View File

@ -0,0 +1,24 @@
<?php
namespace App\Exceptions;
use Exception;
class SystemError extends Exception
{
public function report()
{
// ..
}
public function render($request)
{
return view('errors.guest', [
'message' => $this->getMessage(),
'code' => $this->getCode(),
]);
}
}

View File

@ -106,12 +106,13 @@ class BaseController extends Controller
'user.company_user',
'token',
'company.activities',
'company.tax_rates',
'company.documents',
'company.company_gateways.gateway',
'company.users.company_user',
'company.tax_rates',
'company.groups',
'company.task_statuses',
'company.payment_terms',
'company.groups',
'company.designs.company',
'company.expense_categories',
'company.subscriptions',
@ -314,8 +315,8 @@ class BaseController extends Controller
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
},
'company.tax_rates' => function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
'company.tax_rates'=> function ($query) use ($updated_at, $user) {
$query->whereNotNull('updated_at');
},
'company.vendors'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
@ -328,7 +329,7 @@ class BaseController extends Controller
$query->where('updated_at', '>=', $updated_at);
},
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
$query->whereNotNull('updated_at');
},
'company.activities'=> function ($query) use($user) {
@ -390,7 +391,7 @@ class BaseController extends Controller
'company.documents'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
},
'company.groups' => function ($query) use ($created_at, $user) {
'company.groups'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
},
@ -398,8 +399,8 @@ class BaseController extends Controller
$query->where('created_at', '>=', $created_at);
},
'company.tax_rates' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
'company.tax_rates'=> function ($query) use ($created_at, $user) {
$query->whereNotNull('created_at');
},
'company.activities'=> function ($query) use($user) {
@ -768,6 +769,10 @@ class BaseController extends Controller
case 'profile':
return 'main.profile.dart.js';
default:
if(Ninja::isSelfHost())
return 'main.foss.dart.js';
return 'main.dart.js';
}

View File

@ -35,7 +35,7 @@ class NinjaPlanController extends Controller
$account = $company->account;
if (Ninja::isHosted() && MultiDB::findAndSetDbByContactKey($contact_key) && $client_contact = ClientContact::where('contact_key', $contact_key)->first())
if (MultiDB::findAndSetDbByContactKey($contact_key) && $client_contact = ClientContact::where('contact_key', $contact_key)->first())
{
nlog("Ninja Plan Controller - Found and set Client Contact");

View File

@ -15,6 +15,7 @@ use App\DataMapper\Analytics\AccountDeleted;
use App\DataMapper\CompanySettings;
use App\DataMapper\DefaultSettings;
use App\Http\Requests\Company\CreateCompanyRequest;
use App\Http\Requests\Company\DefaultCompanyRequest;
use App\Http\Requests\Company\DestroyCompanyRequest;
use App\Http\Requests\Company\EditCompanyRequest;
use App\Http\Requests\Company\ShowCompanyRequest;
@ -598,8 +599,66 @@ class CompanyController extends BaseController
}
// public function default(DefaultCompanyRequest $request, Company $company)
// {
// }
/**
* Update the specified resource in storage.
*
* @param UploadCompanyRequest $request
* @param Company $client
* @return Response
*
*
*
* @OA\Post(
* path="/api/v1/companies/{company}/default",
* operationId="setDefaultCompany",
* tags={"companies"},
* summary="Sets the company as the default company.",
* description="Sets the company as the default company.",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="company",
* in="path",
* description="The Company Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the company object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Company"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function default(DefaultCompanyRequest $request, Company $company)
{
$account = $company->account;
$account->default_company_id = $company->id;
$account->save();
return $this->itemResponse($company->fresh());
}
}

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers;
use App\DataMapper\FeesAndLimits;
use App\Exceptions\SystemError;
use App\Factory\CompanyGatewayFactory;
use App\Http\Requests\StripeConnect\InitializeStripeConnectRequest;
use App\Libraries\MultiDB;
@ -20,6 +21,7 @@ use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\GatewayType;
use App\PaymentDrivers\Stripe\Connect\Account;
use Exception;
use Illuminate\Http\Request;
use Stripe\Exception\ApiErrorException;
@ -78,7 +80,7 @@ class StripeConnectController extends BaseController
{
nlog($e->getMessage());
throw new SystemError($e->getMessage(), 500);
}
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);

View File

@ -44,6 +44,7 @@ class CreditsTable extends Component
->orWhereNull('due_date');
})
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.credits-table', [

View File

@ -53,7 +53,10 @@ class DocumentsTable extends Component
public function render()
{
return render('components.livewire.documents-table', [
'documents' => $this->query->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')->paginate($this->per_page),
'documents' => $this->query
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page),
]);
}

View File

@ -76,6 +76,7 @@ class InvoicesTable extends Component
$query = $query
->where('client_id', auth('contact')->user()->client->id)
->where('status_id', '<>', Invoice::STATUS_DRAFT)
->where('status_id', '<>', Invoice::STATUS_CANCELLED)
->withTrashed()
->paginate($this->per_page);

View File

@ -37,6 +37,7 @@ class PaymentMethodsTable extends Component
->where('company_id', $this->company->id)
->where('client_id', $this->client->id)
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.payment-methods-table', [

View File

@ -48,6 +48,7 @@ class QuotesTable extends Component
->where('company_id', $this->company->id)
->where('client_id', auth('contact')->user()->client->id)
->where('status_id', '<>', Quote::STATUS_DRAFT)
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.quotes-table', [

View File

@ -46,6 +46,7 @@ class RecurringInvoicesTable extends Component
->orderBy('status_id', 'asc')
->with('client')
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.recurring-invoices-table', [

View File

@ -39,6 +39,7 @@ class SubscriptionRecurringInvoicesTable extends Component
->where('company_id', $this->company->id)
->whereNotNull('subscription_id')
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.subscriptions-recurring-invoices-table', [

View File

@ -48,6 +48,7 @@ class TasksTable extends Component
$query = $query
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.tasks-table', [

View File

@ -0,0 +1,36 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\Company;
use App\Http\Requests\Request;
class DefaultCompanyRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin()
}
public function rules()
{
$rules = [];
return $rules;
}
}

View File

@ -233,7 +233,7 @@ class Import implements ShouldQueue
$account->save();
//company size check
if ($this->company->invoices()->count() > 1000 || $this->company->products()->count() > 1000 || $this->company->clients()->count() > 1000) {
if ($this->company->invoices()->count() > 500 || $this->company->products()->count() > 500 || $this->company->clients()->count() > 500) {
$this->company->is_large = true;
$this->company->save();
}
@ -261,9 +261,6 @@ class Import implements ShouldQueue
/*After a migration first some basic jobs to ensure the system is up to date*/
VersionCheck::dispatch();
// CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
// CreateCompanyTaskStatuses::dispatchNow($this->company, $this->user);
info('Completed🚀🚀🚀🚀🚀 at '.now());

View File

@ -0,0 +1,47 @@
<?php
/**
* Quote Ninja (https://quoteninja.com).
*
* @link https://github.com/quoteninja/quoteninja source repository
*
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://quoteninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Listeners\Quote;
use App\Jobs\Util\WebhookHandler;
use App\Libraries\MultiDB;
use App\Models\Webhook;
use Illuminate\Contracts\Queue\ShouldQueue;
class QuoteApprovedWebhook implements ShouldQueue
{
public function __construct()
{
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
MultiDB::setDb($event->company->db);
$quote = $event->quote;
$subscriptions = Webhook::where('company_id', $quote->company_id)
->where('event_id', Webhook::EVENT_APPROVE_QUOTE)
->exists();
if ($subscriptions) {
WebhookHandler::dispatch(Webhook::EVENT_APPROVE_QUOTE, $quote, $quote->company);
}
}
}

View File

@ -123,7 +123,7 @@ class TemplateEmail extends Mailable
}
if($this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
if($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
$ubl_string = CreateUbl::dispatchNow($this->invitation->invoice);

View File

@ -361,6 +361,9 @@ class Client extends BaseModel implements HasLocalePreference
if (is_string($this->settings->{$setting}) && (iconv_strlen($this->settings->{$setting}) >= 1)) {
return $this->settings->{$setting};
}
elseif(is_bool($this->settings->{$setting})){
return $this->settings->{$setting};
}
}
/*Group Settings*/

View File

@ -281,7 +281,7 @@ class Company extends BaseModel
*/
public function company_gateways()
{
return $this->hasMany(CompanyGateway::class);
return $this->hasMany(CompanyGateway::class)->withTrashed();
}
/**
@ -289,7 +289,7 @@ class Company extends BaseModel
*/
public function tax_rates()
{
return $this->hasMany(TaxRate::class);
return $this->hasMany(TaxRate::class)->withTrashed();
}
/**
@ -297,7 +297,7 @@ class Company extends BaseModel
*/
public function products()
{
return $this->hasMany(Product::class);
return $this->hasMany(Product::class)->withTrashed();
}
/**
@ -311,7 +311,7 @@ class Company extends BaseModel
public function group_settings()
{
return $this->hasMany(GroupSetting::class);
return $this->hasMany(GroupSetting::class)->withTrashed();
}
public function timezone()

View File

@ -56,10 +56,10 @@ class CompanyUser extends Pivot
return self::class;
}
public function tax_rates()
{
return $this->hasMany(TaxRate::class, 'company_id', 'company_id');
}
// public function tax_rates()
// {
// return $this->hasMany(TaxRate::class, 'company_id', 'company_id');
// }
public function account()
{

View File

@ -24,7 +24,7 @@ class TaxRate extends BaseModel
'rate',
];
protected $appends = ['tax_rate_id'];
// protected $appends = ['tax_rate_id'];
public function getEntityType()
{

View File

@ -136,30 +136,19 @@ class CreditCard
$gateway_response = \json_decode($data['gateway_response']);
try {
$payment_method = $this->braintree->gateway->paymentMethod()->create([
'customerId' => $customerId,
'paymentMethodNonce' => $gateway_response->nonce,
'options' => [
'verifyCard' => true,
],
]);
return $payment_method->paymentMethod->token;
} catch(\Exception $e) {
SystemLogger::dispatch(
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_BRAINTREE,
$this->braintree->client,
$this->braintree->client->company,
);
$response = $this->braintree->gateway->paymentMethod()->create([
'customerId' => $customerId,
'paymentMethodNonce' => $gateway_response->nonce,
'options' => [
'verifyCard' => true,
],
]);
nlog(['e' => $e->getMessage(), 'class' => \get_class($e)]);
throw new PaymentFailed($e->getMessage(), $e->getCode());
if ($response->success) {
return $response->paymentMethod->token;
}
throw new PaymentFailed($response->message);
}
private function processSuccessfulPayment($response)

View File

@ -49,7 +49,7 @@ class PayFastPaymentDriver extends BaseDriver
{
$types = [];
if($this->client->currency()->code == 'ZAR')
if($this->client->currency()->code == 'ZAR' || $this->client->currency()->code == 'USD')
$types[] = GatewayType::CREDIT_CARD;
return $types;

View File

@ -161,6 +161,7 @@ use App\Listeners\Payment\PaymentEmailedActivity;
use App\Listeners\Payment\PaymentNotification;
use App\Listeners\Payment\PaymentRestoredActivity;
use App\Listeners\Quote\QuoteApprovedActivity;
use App\Listeners\Quote\QuoteApprovedWebhook;
use App\Listeners\Quote\QuoteArchivedActivity;
use App\Listeners\Quote\QuoteCreatedNotification;
use App\Listeners\Quote\QuoteDeletedActivity;
@ -385,6 +386,7 @@ class EventServiceProvider extends ServiceProvider
QuoteWasApproved::class => [
ReachWorkflowSettings::class,
QuoteApprovedActivity::class,
QuoteApprovedWebhook::class,
],
QuoteWasCreated::class => [
CreatedQuoteActivity::class,

View File

@ -73,7 +73,9 @@ class ClientContactRepository extends BaseRepository
$client->company->client_contacts()->where('email', $update_contact->email)->update(['password' => $update_contact->password]);
}
$update_contact->email = trim($contact['email']);
if(array_key_exists('email', $contact))
$update_contact->email = trim($contact['email']);
$update_contact->save();
});

View File

@ -40,7 +40,7 @@
"coconutcraig/laravel-postmark": "^2.10",
"codedge/laravel-selfupdater": "^3.2",
"composer/composer": "^2",
"doctrine/dbal": "^2.10",
"doctrine/dbal": "^3.0",
"eway/eway-rapid-php": "^1.3",
"fakerphp/faker": "^1.14",
"fideloper/proxy": "^4.2",

722
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.9',
'app_tag' => '5.3.9',
'app_version' => '5.3.10',
'app_tag' => '5.3.10',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -3,12 +3,12 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"main.dart.js": "0124649b1dd42c7fd93b9489eaf99b1e",
"main.dart.js": "14aa83cab19203026dfa556e62b000df",
"version.json": "9ec5e3813adc4bfd8713556c5059e97d",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"/": "debe3f3301b29dcbdc324fb3f330965a",
"/": "658e6036cde8a90f1e7c080b12b0c635",
"assets/NOTICES": "9eb7e2eb2888ea5bae5f536720db37cd",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",

82137
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

80987
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

176632
public/main.last.dart.js vendored

File diff suppressed because one or more lines are too long

185834
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

82997
public/main.wasm.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,7 @@ input:checked ~ .dot {
background-color: #48bb78;
}
</style>
<div class="container flex flex-wrap pt-4 pb-10 m-auto mt-6 md:mt-15 lg:px-12 xl:px-16" x-data="{show: true}">
<div id="datadiv" class="container flex flex-wrap pt-2 pb-10 m-auto mt-2 md:mt-5 lg:px-16 xl:px-16" x-data="{show: true}">
<div class="w-full px-0 lg:px-4">
<h2 class="px-12 text-base font-bold text-center md:text-2xl text-blue-700">
Choose your plan
@ -61,14 +61,28 @@ input:checked ~ .dot {
<p class="text-xs text-center uppercase text-white">
monthly
</p>
<div class="py-2 text-sm my-3 text-white">Unlimited clients, invoices, quotes, recurring invoices</div>
<hr>
<div class="py-2 text-sm my-3 text-white">10 professional invoice & quote template designs</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Remove "Created by Invoice Ninja" from invoices</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Enable emails to be sent via Gmail</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Integrate with Zapier, Integromat or API</div>
<hr>
<div class="py-2 text-sm my-3 text-white">+ Much more!</div>
</div>
<div class="flex flex-col items-center justify-center w-full h-full py-6 rounded-b-lg bg-blue-700">
<p class="text-xl text-white">
Sign up!
Single User
</p>
<a type="button" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500" href="https://invoiceninja.invoicing.co/client/subscriptions/WJxbojagwO/purchase">
<button id="handleProMonthlyClick" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500">
Purchase
</a>
</button>
</div>
</label>
</div>
@ -78,33 +92,46 @@ input:checked ~ .dot {
<label class="flex flex-col rounded-lg shadow-lg relative cursor-pointer hover:shadow-2xl">
<div class="w-full px-4 py-8 rounded-t-lg bg-blue-500">
<h3 class="mx-auto text-base font-semibold text-center underline text-white group-hover:text-white">
Enterprise (1-2 Users)
Enterprise Plan
</h3>
<p class="text-5xl font-bold text-center text-white">
<p class="text-5xl font-bold text-center text-white" id="m_plan_price">
$14
</p>
<p class="text-xs text-center uppercase text-white">
monthly
</p>
<div class="py-2 text-sm my-3 text-white">Multiple users and advanced permissions per user</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Attach documents to emails & client side portal!</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Branded client portal: "https://billing.yourcompany.com"</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Custom background for invoices & quotes</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Integrate with Zapier, Integromat or API</div>
<hr>
<div class="py-2 text-sm my-3 text-white">+ Much more!</div>
</div>
<div class="flex flex-col items-center justify-center w-full h-full py-6 rounded-b-lg bg-blue-700">
<p class="text-xl text-white">
Sign up!
<select id="users_monthly" class="bg-white text-black appearance-none border-none inline-block py-0 pl-3 pr-2 rounded leading-tight w-full">
<option value="7LDdwRb1YK" selected>1-2 Users</option>
<option value="MVyb8mdvAZ">3-5 Users</option>
<option value="WpmbkR5azJ">6-10 Users</option>
<option value="k8mepY2aMy">11-20 Users</option>
</select>
</p>
<a type="button" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500" href="https://invoiceninja.invoicing.co/client/subscriptions/7LDdwRb1YK/purchase">
<button id="handleMonthlyClick" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500">
Purchase
</a>
</button>
</div>
</label>
</div>
</div>
<!-- Annual Plans -->
<div class="flex flex-wrap items-center justify-center py-4 pt-0" x-show=" !show ">
<div class="w-full p-4 md:w-1/2 lg:w-1/2">
@ -119,6 +146,19 @@ input:checked ~ .dot {
<p class="text-xs text-center uppercase text-white">
yearly
</p>
<div class="py-2 text-sm my-3 text-white">Unlimited clients, invoices, quotes, recurring invoices</div>
<hr>
<div class="py-2 text-sm my-3 text-white">10 professional invoice & quote template designs</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Remove "Created by Invoice Ninja" from invoices</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Enable emails to be sent via Gmail</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Integrate with Zapier, Integromat or API</div>
<hr>
<div class="py-2 text-sm my-3 text-white">+ Much more!</div>
</div>
<div
class="flex flex-col items-center justify-center w-full h-full py-6 rounded-b-lg bg-blue-700"
@ -126,9 +166,9 @@ input:checked ~ .dot {
<p class="text-xl text-white">
Buy 10 months get 2 free!
</p>
<a type="button" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500" href="https://invoiceninja.invoicing.co/client/subscriptions/q9wdL9wejP/purchase">
<button id="handleProYearlyClick" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500">
Purchase
</a>
</button>
</div>
</label>
</div>
@ -137,24 +177,42 @@ input:checked ~ .dot {
<label class="flex flex-col rounded-lg shadow-lg relative cursor-pointer hover:shadow-2xl">
<div class="w-full px-4 py-8 rounded-t-lg bg-blue-500">
<h3 class="mx-auto text-base font-semibold text-center underline text-white group-hover:text-white">
Enterprise (1-2 Users)
Enterprise Plan
</h3>
<p class="text-5xl font-bold text-center text-white">
<p class="text-5xl font-bold text-center text-white" id="y_plan_price">
$140
</p>
<p class="text-xs text-center uppercase text-white">
yearly
</p>
<div class="py-2 text-sm my-3 text-white">Multiple users and advanced permissions per user</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Attach documents to emails & client side portal!</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Branded client portal: "https://billing.yourcompany.com"</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Custom background for invoices & quotes</div>
<hr>
<div class="py-2 text-sm my-3 text-white">Integrate with Zapier, Integromat or API</div>
<hr>
<div class="py-2 text-sm my-3 text-white">+ Much more!</div>
</div>
<div
class="flex flex-col items-center justify-center w-full h-full py-6 rounded-b-lg bg-blue-700"
>
<p class="text-xl text-white">
Buy 10 months get 2 free!
<select id="users_yearly" class="bg-white text-black appearance-none border-none inline-block py-0 pl-3 pr-2 rounded leading-tight w-full">
<option value="LYqaQWldnj" selected>1-2 Users</option>
<option value="kQBeX6mbyK">3-5 Users</option>
<option value="GELe32Qd69">6-10 Users</option>
<option value="MVyb86oevA">11-20 Users</option>
</select>
</p>
<a type="button" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500" href="https://invoiceninja.invoicing.co/client/subscriptions/LYqaQWldnj/purchase">
<button id="handleYearlyClick" class="w-5/6 py-2 mt-2 font-semibold text-center uppercase bg-white border border-transparent rounded text-blue-500" >
Purchase
</a>
</button>
</div>
</label>
</div>
@ -167,5 +225,68 @@ input:checked ~ .dot {
@push('footer')
<script type="text/javascript">
var users_yearly = 'LYqaQWldnj';
var users_monthly = '7LDdwRb1YK';
document.getElementById('users_yearly').options[0].selected = true;
document.getElementById('users_monthly').options[0].selected = true;
document.getElementById("toggleB").addEventListener('change', function() {
document.getElementById('users_yearly').options[0].selected = true;
document.getElementById('users_monthly').options[0].selected = true;
document.getElementById('y_plan_price').innerHTML = price_map.get('LYqaQWldnj');
document.getElementById('m_plan_price').innerHTML = price_map.get('7LDdwRb1YK');
users_yearly = 'LYqaQWldnj';
users_monthly = '7LDdwRb1YK';
});
document.getElementById('users_yearly').addEventListener('change', function() {
users_yearly = this.value;
document.getElementById('y_plan_price').innerHTML = price_map.get(this.value);
});
document.getElementById('users_monthly').addEventListener('change', function() {
users_monthly = this.value;
document.getElementById('m_plan_price').innerHTML = price_map.get(this.value);
});
document.getElementById('handleYearlyClick').addEventListener('click', function() {
document.getElementById("toggleB").checked = false;
location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/' + users_yearly + '/purchase';
});
document.getElementById('handleMonthlyClick').addEventListener('click', function() {
document.getElementById("toggleB").checked = false;
location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/' + users_monthly + '/purchase';
});
document.getElementById('handleProMonthlyClick').addEventListener('click', function() {
document.getElementById("toggleB").checked = false;
location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/WJxbojagwO/purchase';
});
document.getElementById('handleProYearlyClick').addEventListener('click', function() {
document.getElementById("toggleB").checked = false;
location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/q9wdL9wejP/purchase';
});
const price_map = new Map();
//monthly
price_map.set('7LDdwRb1YK', '$14');
price_map.set('MVyb8mdvAZ', '$26');
price_map.set('WpmbkR5azJ', '$36');
price_map.set('k8mepY2aMy', '$44');
//yearly
price_map.set('LYqaQWldnj', '$140');
price_map.set('kQBeX6mbyK', '$260');
price_map.set('GELe32Qd69', '$360');
price_map.set('MVyb86oevA', '$440');
</script>
@endpush

View File

@ -51,6 +51,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::resource('companies', 'CompanyController'); // name = (companies. index / create / show / update / destroy / edit
Route::put('companies/{company}/upload', 'CompanyController@upload');
Route::post('companies/{company}/default', 'CompanyController@default');
Route::get('company_ledger', 'CompanyLedgerController@index')->name('company_ledger.index');

View File

@ -98,7 +98,7 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie
Route::get('quote/{invitation_key}/download_pdf', 'QuoteController@downloadPdf')->name('quote.download_invitation_key');
Route::get('credit/{invitation_key}/download_pdf', 'CreditController@downloadPdf')->name('credit.download_invitation_key');
Route::get('{entity}/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
Route::get('{entity}/{client_hash}/{invitation_key}', 'ClientPortal\InvitationController@routerForIframe')->name('invoice.client_hash_and_invitation_key'); //should never need this
// Route::get('{entity}/{client_hash}/{invitation_key}', 'ClientPortal\InvitationController@routerForIframe')->name('invoice.client_hash_and_invitation_key'); //should never need this
});