git pushMerge branch 'v5-develop' of

https://github.com/turbo124/invoiceninja into v5-develop
This commit is contained in:
David Bomba 2021-03-11 09:37:04 +11:00
commit 23e4a3bcee
90 changed files with 65160 additions and 62360 deletions

View File

@ -1 +1 @@
5.1.15 5.1.19

View File

@ -11,6 +11,7 @@
namespace App\Console; namespace App\Console;
use App\Jobs\Cron\BillingSubscriptionCron;
use App\Jobs\Cron\RecurringInvoicesCron; use App\Jobs\Cron\RecurringInvoicesCron;
use App\Jobs\Ninja\AdjustEmailQuota; use App\Jobs\Ninja\AdjustEmailQuota;
use App\Jobs\Ninja\CompanySizeCheck; use App\Jobs\Ninja\CompanySizeCheck;
@ -53,6 +54,8 @@ class Kernel extends ConsoleKernel
$schedule->job(new UpdateExchangeRates)->daily()->withoutOverlapping(); $schedule->job(new UpdateExchangeRates)->daily()->withoutOverlapping();
$schedule->job(new BillingSubscriptionCron)->daily()->withoutOverlapping();
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping(); $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
/* Run hosted specific jobs */ /* Run hosted specific jobs */

View File

@ -0,0 +1,55 @@
<?php
namespace App\Events\BillingSubscription;
use App\Models\BillingSubscription;
use App\Models\Company;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BillingSubscriptionWasCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var BillingSubscription
*/
public $billing_subscription;
/**
* @var Company
*/
public $company;
/**
* @var array
*/
public $event_vars;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(BillingSubscription $billing_subscription, Company $company, array $event_vars)
{
$this->billing_subscription = $billing_subscription;
$this->company = $company;
$this->event_vars = $event_vars;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Events\ClientSubscription;
use App\Models\ClientSubscription;
use App\Models\Company;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ClientSubscriptionWasCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var ClientSubscription
*/
public $client_subscription;
/**
* @var Company
*/
public $company;
/**
* @var array
*/
public $event_vars;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(ClientSubscription $client_subscription, Company $company, array $event_vars)
{
$this->client_subscription = $client_subscription;
$this->company = $company;
$this->event_vars = $event_vars;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -0,0 +1,27 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Factory;
use App\Models\BillingSubscription;
class BillingSubscriptionFactory
{
public static function create(int $company_id, int $user_id): BillingSubscription
{
$billing_subscription = new BillingSubscription();
$billing_subscription->company_id = $company_id;
$billing_subscription->user_id = $user_id;
return $billing_subscription;
}
}

View File

@ -35,6 +35,7 @@ class CompanyFactory
$company->custom_fields = (object) []; $company->custom_fields = (object) [];
$company->subdomain = ''; $company->subdomain = '';
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095 $company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
$company->default_password_timeout = 30;
return $company; return $company;
} }

View File

@ -28,7 +28,6 @@ class UserFactory
$user->failed_logins = 0; $user->failed_logins = 0;
$user->signature = ''; $user->signature = '';
$user->theme_id = 0; $user->theme_id = 0;
$user->default_password_timeout = 30;
return $user; return $user;
} }

View File

@ -0,0 +1,410 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
use App\Events\BillingSubscription\BillingSubscriptionWasCreated;
use App\Factory\BillingSubscriptionFactory;
use App\Http\Requests\BillingSubscription\CreateBillingSubscriptionRequest;
use App\Http\Requests\BillingSubscription\DestroyBillingSubscriptionRequest;
use App\Http\Requests\BillingSubscription\EditBillingSubscriptionRequest;
use App\Http\Requests\BillingSubscription\ShowBillingSubscriptionRequest;
use App\Http\Requests\BillingSubscription\StoreBillingSubscriptionRequest;
use App\Http\Requests\BillingSubscription\UpdateBillingSubscriptionRequest;
use App\Models\BillingSubscription;
use App\Repositories\BillingSubscriptionRepository;
use App\Transformers\BillingSubscriptionTransformer;
use App\Utils\Ninja;
class BillingSubscriptionController extends BaseController
{
protected $entity_type = BillingSubscription::class;
protected $entity_transformer = BillingSubscriptionTransformer::class;
protected $billing_subscription_repo;
public function __construct(BillingSubscriptionRepository $billing_subscription_repo)
{
parent::__construct();
$this->billing_subscription_repo = $billing_subscription_repo;
}
/**
* Show the list of BillingSubscriptions.
*
* @return Response
*
* @OA\Get(
* path="/api/v1/billing_subscriptions",
* operationId="getBillingSubscriptions",
* tags={"billing_subscriptions"},
* summary="Gets a list of billing_subscriptions",
* description="Lists billing_subscriptions.",
*
* @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\Response(
* response=200,
* description="A list of billing_subscriptions",
* @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/BillingSubscription"),
* ),
* @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 index(): \Illuminate\Http\Response
{
$billing_subscriptions = BillingSubscription::query()->company();
return $this->listResponse($billing_subscriptions);
}
/**
* Show the form for creating a new resource.
*
* @param CreateBillingSubscriptionRequest $request The request
*
* @return Response
*
*
* @OA\Get(
* path="/api/v1/billing_subscriptions/create",
* operationId="getBillingSubscriptionsCreate",
* tags={"billing_subscriptions"},
* summary="Gets a new blank billing_subscriptions object",
* description="Returns a blank object with default values",
* @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\Response(
* response=200,
* description="A blank billing_subscriptions 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/BillingSubscription"),
* ),
* @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 create(CreateBillingSubscriptionRequest $request): \Illuminate\Http\Response
{
$billing_subscription = BillingSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id);
return $this->itemResponse($billing_subscription);
}
/**
* Store a newly created resource in storage.
*
* @param StoreBillingSubscriptionRequest $request The request
*
* @return Response
*
*
* @OA\Post(
* path="/api/v1/billing_subscriptions",
* operationId="storeBillingSubscription",
* tags={"billing_subscriptions"},
* summary="Adds a billing_subscriptions",
* description="Adds an billing_subscriptions to the system",
* @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\Response(
* response=200,
* description="Returns the saved billing_subscriptions 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/BillingSubscription"),
* ),
* @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 store(StoreBillingSubscriptionRequest $request): \Illuminate\Http\Response
{
$billing_subscription = $this->billing_subscription_repo->save($request->all(), BillingSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id));
event(new BillingsubscriptionWasCreated($billing_subscription, $billing_subscription->company, Ninja::eventVars()));
return $this->itemResponse($billing_subscription);
}
/**
* Display the specified resource.
*
* @param ShowBillingSubscriptionRequest $request The request
* @param Invoice $billing_subscription The invoice
*
* @return Response
*
*
* @OA\Get(
* path="/api/v1/billing_subscriptions/{id}",
* operationId="showBillingSubscription",
* tags={"billing_subscriptions"},
* summary="Shows an billing_subscriptions",
* description="Displays an billing_subscriptions by id",
* @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="id",
* in="path",
* description="The BillingSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the BillingSubscription 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/BillingSubscription"),
* ),
* @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 show(ShowBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response
{
return $this->itemResponse($billing_subscription);
}
/**
* Show the form for editing the specified resource.
*
* @param EditBillingSubscriptionRequest $request The request
* @param Invoice $billing_subscription The invoice
*
* @return Response
*
* @OA\Get(
* path="/api/v1/billing_subscriptions/{id}/edit",
* operationId="editBillingSubscription",
* tags={"billing_subscriptions"},
* summary="Shows an billing_subscriptions for editting",
* description="Displays an billing_subscriptions by id",
* @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="id",
* in="path",
* description="The BillingSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the invoice 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/BillingSubscription"),
* ),
* @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 edit(EditBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response
{
return $this->itemResponse($billing_subscription);
}
/**
* Update the specified resource in storage.
*
* @param UpdateBillingSubscriptionRequest $request The request
* @param BillingSubscription $billing_subscription The invoice
*
* @return Response
*
*
* @OA\Put(
* path="/api/v1/billing_subscriptions/{id}",
* operationId="updateBillingSubscription",
* tags={"billing_subscriptions"},
* summary="Updates an billing_subscriptions",
* description="Handles the updating of an billing_subscriptions by id",
* @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="id",
* in="path",
* description="The BillingSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the billing_subscriptions 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/BillingSubscription"),
* ),
* @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 update(UpdateBillingSubscriptionRequest $request, BillingSubscription $billing_subscription)
{
if ($request->entityIsDeleted($billing_subscription)) {
return $request->disallowUpdate();
}
$billing_subscription = $this->billing_subscription_repo->save($request->all(), $billing_subscription);
return $this->itemResponse($billing_subscription);
}
/**
* Remove the specified resource from storage.
*
* @param DestroyBillingSubscriptionRequest $request
* @param BillingSubscription $invoice
*
* @return Response
*
* @throws \Exception
* @OA\Delete(
* path="/api/v1/billing_subscriptions/{id}",
* operationId="deleteBillingSubscription",
* tags={"billing_subscriptions"},
* summary="Deletes a billing_subscriptions",
* description="Handles the deletion of an billing_subscriptions by id",
* @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="id",
* in="path",
* description="The BillingSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns a HTTP status",
* @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\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 destroy(DestroyBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response
{
$this->billing_subscription_repo->delete($billing_subscription);
return $this->itemResponse($billing_subscription->fresh());
}
}

View File

@ -0,0 +1,410 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
use App\Events\ClientSubscription\ClientSubscriptionWasCreated;
use App\Factory\ClientSubscriptionFactory;
use App\Http\Requests\ClientSubscription\CreateClientSubscriptionRequest;
use App\Http\Requests\ClientSubscription\DestroyClientSubscriptionRequest;
use App\Http\Requests\ClientSubscription\EditClientSubscriptionRequest;
use App\Http\Requests\ClientSubscription\ShowClientSubscriptionRequest;
use App\Http\Requests\ClientSubscription\StoreClientSubscriptionRequest;
use App\Http\Requests\ClientSubscription\UpdateClientSubscriptionRequest;
use App\Models\ClientSubscription;
use App\Repositories\ClientSubscriptionRepository;
use App\Transformers\ClientSubscriptionTransformer;
use App\Utils\Ninja;
class ClientSubscriptionController extends BaseController
{
protected $entity_type = ClientSubscription::class;
protected $entity_transformer = ClientSubscriptionTransformer::class;
protected $client_subscription_repo;
public function __construct(ClientSubscriptionRepository $client_subscription_repo)
{
parent::__construct();
$this->client_subscription_repo = $client_subscription_repo;
}
/**
* Show the list of ClientSubscriptions.
*
* @return Response
*
* @OA\Get(
* path="/api/v1/client_subscriptions",
* operationId="getClientSubscriptions",
* tags={"client_subscriptions"},
* summary="Gets a list of client_subscriptions",
* description="Lists client_subscriptions.",
*
* @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\Response(
* response=200,
* description="A list of client_subscriptions",
* @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/ClientSubscription"),
* ),
* @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 index(): \Illuminate\Http\Response
{
$client_subscriptions = ClientSubscription::query()->company();
return $this->listResponse($client_subscriptions);
}
/**
* Show the form for creating a new resource.
*
* @param CreateClientSubscriptionRequest $request The request
*
* @return Response
*
*
* @OA\Get(
* path="/api/v1/client_subscriptions/create",
* operationId="getClientSubscriptionsCreate",
* tags={"client_subscriptions"},
* summary="Gets a new blank client_subscriptions object",
* description="Returns a blank object with default values",
* @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\Response(
* response=200,
* description="A blank client_subscriptions 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/ClientSubscription"),
* ),
* @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 create(CreateClientSubscriptionRequest $request): \Illuminate\Http\Response
{
$client_subscription = ClientSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id);
return $this->itemResponse($client_subscription);
}
/**
* Store a newly created resource in storage.
*
* @param StoreClientSubscriptionRequest $request The request
*
* @return Response
*
*
* @OA\Post(
* path="/api/v1/client_subscriptions",
* operationId="storeClientSubscription",
* tags={"client_subscriptions"},
* summary="Adds a client_subscriptions",
* description="Adds an client_subscriptions to the system",
* @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\Response(
* response=200,
* description="Returns the saved client_subscriptions 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/ClientSubscription"),
* ),
* @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 store(StoreClientSubscriptionRequest $request): \Illuminate\Http\Response
{
$client_subscription = $this->client_subscription_repo->save($request->all(), ClientSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id));
event(new ClientsubscriptionWasCreated($client_subscription, $client_subscription->company, Ninja::eventVars()));
return $this->itemResponse($client_subscription);
}
/**
* Display the specified resource.
*
* @param ShowClientSubscriptionRequest $request The request
* @param Invoice $client_subscription The invoice
*
* @return Response
*
*
* @OA\Get(
* path="/api/v1/client_subscriptions/{id}",
* operationId="showClientSubscription",
* tags={"client_subscriptions"},
* summary="Shows an client_subscriptions",
* description="Displays an client_subscriptions by id",
* @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="id",
* in="path",
* description="The ClientSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the ClientSubscription 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/ClientSubscription"),
* ),
* @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 show(ShowClientSubscriptionRequest $request, ClientSubscription $client_subscription): \Illuminate\Http\Response
{
return $this->itemResponse($client_subscription);
}
/**
* Show the form for editing the specified resource.
*
* @param EditClientSubscriptionRequest $request The request
* @param Invoice $client_subscription The invoice
*
* @return Response
*
* @OA\Get(
* path="/api/v1/client_subscriptions/{id}/edit",
* operationId="editClientSubscription",
* tags={"client_subscriptions"},
* summary="Shows an client_subscriptions for editting",
* description="Displays an client_subscriptions by id",
* @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="id",
* in="path",
* description="The ClientSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the invoice 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/ClientSubscription"),
* ),
* @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 edit(EditClientSubscriptionRequest $request, ClientSubscription $client_subscription): \Illuminate\Http\Response
{
return $this->itemResponse($client_subscription);
}
/**
* Update the specified resource in storage.
*
* @param UpdateClientSubscriptionRequest $request The request
* @param ClientSubscription $client_subscription The invoice
*
* @return Response
*
*
* @OA\Put(
* path="/api/v1/client_subscriptions/{id}",
* operationId="updateClientSubscription",
* tags={"client_subscriptions"},
* summary="Updates an client_subscriptions",
* description="Handles the updating of an client_subscriptions by id",
* @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="id",
* in="path",
* description="The ClientSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the client_subscriptions 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/ClientSubscription"),
* ),
* @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 update(UpdateClientSubscriptionRequest $request, ClientSubscription $client_subscription)
{
if ($request->entityIsDeleted($client_subscription)) {
return $request->disallowUpdate();
}
$client_subscription = $this->client_subscription_repo->save($request->all(), $client_subscription);
return $this->itemResponse($client_subscription);
}
/**
* Remove the specified resource from storage.
*
* @param DestroyClientSubscriptionRequest $request
* @param ClientSubscription $invoice
*
* @return Response
*
* @throws \Exception
* @OA\Delete(
* path="/api/v1/client_subscriptions/{id}",
* operationId="deleteClientSubscription",
* tags={"client_subscriptions"},
* summary="Deletes a client_subscriptions",
* description="Handles the deletion of an client_subscriptions by id",
* @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="id",
* in="path",
* description="The ClientSubscription Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns a HTTP status",
* @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\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 destroy(DestroyClientSubscriptionRequest $request, ClientSubscription $client_subscription): \Illuminate\Http\Response
{
$this->client_subscription_repo->delete($client_subscription);
return $this->itemResponse($client_subscription->fresh());
}
}

View File

@ -13,12 +13,18 @@ namespace App\Http\Controllers;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Libraries\OAuth\Providers\Google; use App\Libraries\OAuth\Providers\Google;
use Illuminate\Http\Request; use App\Models\CompanyUser;
use App\Transformers\CompanyUserTransformer;
use Google_Client; use Google_Client;
use Illuminate\Http\Request;
class ConnectedAccountController extends BaseController class ConnectedAccountController extends BaseController
{ {
protected $entity_type = CompanyUser::class;
protected $entity_transformer = CompanyUserTransformer::class;
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -128,8 +134,10 @@ class ConnectedAccountController extends BaseController
auth()->user()->save(); auth()->user()->save();
//$ct = CompanyUser::whereUserId(auth()->user()->id); //$ct = CompanyUser::whereUserId(auth()->user()->id);
$ct = CompanyUser::whereUserId(auth()->user()->id);
return $this->listResponse(auth()->user()); return $this->listResponse($ct);
// return $this->listResponse(auth()->user());
} }
return response() return response()

View File

@ -376,6 +376,8 @@ class CreditController extends BaseController
$credit = $this->credit_repository->save($request->all(), $credit); $credit = $this->credit_repository->save($request->all(), $credit);
$credit->service()->deletePdf();
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars())); event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars()));
return $this->itemResponse($credit); return $this->itemResponse($credit);

View File

@ -396,6 +396,8 @@ class InvoiceController extends BaseController
} }
$invoice = $this->invoice_repo->save($request->all(), $invoice); $invoice = $this->invoice_repo->save($request->all(), $invoice);
$invoice->service()->deletePdf();
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars())); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars()));
@ -697,14 +699,14 @@ class InvoiceController extends BaseController
} }
break; break;
case 'cancel': case 'cancel':
$invoice = $invoice->service()->handleCancellation()->save(); $invoice = $invoice->service()->handleCancellation()->deletePdf()->save();
if (! $bulk) { if (! $bulk) {
$this->itemResponse($invoice); $this->itemResponse($invoice);
} }
break; break;
case 'reverse': case 'reverse':
$invoice = $invoice->service()->handleReversal()->save(); $invoice = $invoice->service()->handleReversal()->deletePdf()->save();
if (! $bulk) { if (! $bulk) {
$this->itemResponse($invoice); $this->itemResponse($invoice);
@ -720,7 +722,7 @@ class InvoiceController extends BaseController
} }
//touch reminder1,2,3_sent + last_sent here if the email is a reminder. //touch reminder1,2,3_sent + last_sent here if the email is a reminder.
$invoice->service()->touchReminder($this->reminder_template)->save(); $invoice->service()->touchReminder($this->reminder_template)->deletePdf()->save();
$invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($invoice) { $invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($invoice) {
EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template); EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template);

View File

@ -0,0 +1,35 @@
<?php
/**
* @OA\Schema(
* schema="BillingSubscription",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="product_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="recurring_invoice_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="is_recurring", type="boolean", example="true", description="______"),
* @OA\Property(property="frequency_id", type="string", example="1", description="integer const representation of the frequency"),
* @OA\Property(property="auto_bill", type="string", example="always", description="enum setting"),
* @OA\Property(property="promo_code", type="string", example="PROMOCODE4U", description="______"),
* @OA\Property(property="promo_discount", type="number", example=10, description="______"),
* @OA\Property(property="is_amount_discount", type="boolean", example="true", description="______"),
* @OA\Property(property="allow_cancellation", type="boolean", example="true", description="______"),
* @OA\Property(property="per_seat_enabled", type="boolean", example="true", description="______"),
* @OA\Property(property="currency_id", type="integer", example="1", description="______"),
* @OA\Property(property="min_seats_limit", type="integer", example="1", description="______"),
* @OA\Property(property="max_seats_limit", type="integer", example="100", description="______"),
* @OA\Property(property="trial_enabled", type="boolean", example="true", description="______"),
* @OA\Property(property="trial_duration", type="integer", example="2", description="______"),
* @OA\Property(property="allow_query_overrides", type="boolean", example="true", description="______"),
* @OA\Property(property="allow_plan_changes", type="boolean", example="true", description="______"),
* @OA\Property(property="plan_map", type="string", example="1", description="map describing the available upgrade/downgrade plans for this subscription"),
* @OA\Property(property="refund_period", type="integer", example="2", description="______"),
* @OA\Property(property="webhook_configuration", type="string", example="2", description="______"),
* @OA\Property(property="is_deleted", type="boolean", example="true", description="______"),
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* )
*/

View File

@ -0,0 +1,19 @@
<?php
/**
* @OA\Schema(
* schema="ClientSubscription",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="subscription_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="recurring_invoice_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="trial_started", type="string", example="10-10-2021", description="______"),
* @OA\Property(property="trial_ends", type="string", example="12-10-2021", description="______"),
* @OA\Property(property="is_deleted", type="boolean", example="true", description="______"),
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* )
*/

View File

@ -387,6 +387,8 @@ class QuoteController extends BaseController
$quote = $this->quote_repo->save($request->all(), $quote); $quote = $this->quote_repo->save($request->all(), $quote);
$quote->service()->deletePdf();
event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars())); event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars()));
return $this->itemResponse($quote); return $this->itemResponse($quote);

View File

@ -374,6 +374,8 @@ class RecurringInvoiceController extends BaseController
$recurring_invoice = $this->recurring_invoice_repo->save($request->all(), $recurring_invoice); $recurring_invoice = $this->recurring_invoice_repo->save($request->all(), $recurring_invoice);
$recurring_invoice->service()->deletePdf()->save();
return $this->itemResponse($recurring_invoice); return $this->itemResponse($recurring_invoice);
} }
@ -439,7 +441,7 @@ class RecurringInvoiceController extends BaseController
/** /**
* @OA\Get( * @OA\Get(
* path="/api/v1/recurring_invoice/{invitation_key}/download", * path="/api/v1/recurring_invoice/{invitation_key}/download",
* operationId="downloadInvoice", * operationId="downloadRecurringInvoice",
* tags={"invoices"}, * tags={"invoices"},
* summary="Download a specific invoice by invitation key", * summary="Download a specific invoice by invitation key",
* description="Downloads a specific invoice", * description="Downloads a specific invoice",

View File

@ -155,6 +155,7 @@ class SetupController extends Controller
return redirect('/'); return redirect('/');
} catch (Exception $e) { } catch (Exception $e) {
nlog($e->getMessage()); nlog($e->getMessage());
return redirect() return redirect()

View File

@ -38,7 +38,7 @@ class TwoFactorController extends BaseController
$data = [ $data = [
'secret' => $secret, 'secret' => $secret,
'qrCode' => $qrCode, 'qrCode' => $qr_code,
]; ];
return response()->json(['data' => $data], 200); return response()->json(['data' => $data], 200);

View File

@ -209,6 +209,8 @@ class UserController extends BaseController
$ct = CreateCompanyToken::dispatchNow($company, $user, $user_agent); $ct = CreateCompanyToken::dispatchNow($company, $user, $user_agent);
nlog("in the store method of the usercontroller class");
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars())); event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars()));
return $this->itemResponse($user->fresh()); return $this->itemResponse($user->fresh());
@ -626,7 +628,7 @@ class UserController extends BaseController
} }
/** /**
* Detach an existing user to a company. * Invite an existing user to a company.
* *
* @OA\Post( * @OA\Post(
* path="/api/v1/users/{user}/invite", * path="/api/v1/users/{user}/invite",
@ -682,4 +684,59 @@ class UserController extends BaseController
} }
/**
* Invite an existing user to a company.
*
* @OA\Post(
* path="/api/v1/users/{user}/reconfirm",
* operationId="inviteUserReconfirm",
* tags={"users"},
* summary="Reconfirm an existing user to a company",
* description="Reconfirm an existing user from a 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="user",
* in="path",
* description="The user hashed_id",
* example="FD767dfd7",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Success response",
* @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\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"),
* ),
* )
* @param ReconfirmUserRequest $request
* @param User $user
* @return \Illuminate\Http\JsonResponse
*/
public function reconfirm(ReconfirmUserRequest $request, User $user)
{
$user->service()->invite($user->company());
return response()->json(['message' => ctrans('texts.confirmation_resent')], 200);
}
} }

View File

@ -31,22 +31,23 @@ class PasswordProtection
*/ */
public function handle($request, Closure $next) public function handle($request, Closure $next)
{ {
// {nlog($request->headers->all());
// nlog($request->all());
$error = [ $error = [
'message' => 'Invalid Password', 'message' => 'Invalid Password',
'errors' => new stdClass, 'errors' => new stdClass,
]; ];
nlog(Cache::get(auth()->user()->hashed_id.'_logged_in')); $timeout = auth()->user()->company()->default_password_timeout;
nlog($request->header('X-API-OAUTH-PASSWORD'));
if($timeout == 0)
$timeout = null;
else
$timeout = now()->addMinutes($timeout);
if (Cache::get(auth()->user()->hashed_id.'_logged_in')) { if (Cache::get(auth()->user()->hashed_id.'_logged_in')) {
Cache::pull(auth()->user()->hashed_id.'_logged_in'); Cache::pull(auth()->user()->hashed_id.'_logged_in');
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), now()->addMinutes(30)); Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request); return $next($request);
@ -68,12 +69,12 @@ class PasswordProtection
//If OAuth and user also has a password set - check both //If OAuth and user also has a password set - check both
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) { if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), now()->addMinutes(30)); Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request); return $next($request);
} }
elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->has_password){ elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->has_password){
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), now()->addMinutes(30)); Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request); return $next($request);
} }
} }
@ -83,7 +84,7 @@ class PasswordProtection
}elseif ($request->header('X-API-PASSWORD') && Hash::check($request->header('X-API-PASSWORD'), auth()->user()->password)) { }elseif ($request->header('X-API-PASSWORD') && Hash::check($request->header('X-API-PASSWORD'), auth()->user()->password)) {
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), now()->addMinutes(30)); Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request); return $next($request);
@ -94,4 +95,4 @@ class PasswordProtection
} }
} }

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\BillingSubscription;
use App\Http\Requests\Request;
use App\Models\BillingSubscription;
class CreateBillingSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return auth()->user()->can('create', BillingSubscription::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\BillingSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class DestroyBillingSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->billing_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\BillingSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class EditBillingSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->billing_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\BillingSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class ShowBillingSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('view', $this->billing_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,60 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\BillingSubscription;
use App\Http\Requests\Request;
use App\Models\BillingSubscription;
class StoreBillingSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('create', BillingSubscription::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'user_id' => ['sometimes'],
'product_id' => ['sometimes'],
'assigned_user_id' => ['sometimes'],
'company_id' => ['sometimes'],
'is_recurring' => ['sometimes'],
'frequency_id' => ['sometimes'],
'auto_bill' => ['sometimes'],
'promo_code' => ['sometimes'],
'promo_discount' => ['sometimes'],
'is_amount_discount' => ['sometimes'],
'allow_cancellation' => ['sometimes'],
'per_set_enabled' => ['sometimes'],
'min_seats_limit' => ['sometimes'],
'max_seats_limit' => ['sometimes'],
'trial_enabled' => ['sometimes'],
'trial_duration' => ['sometimes'],
'allow_query_overrides' => ['sometimes'],
'allow_plan_changes' => ['sometimes'],
'plan_map' => ['sometimes'],
'refund_period' => ['sometimes'],
'webhook_configuration' => ['sometimes'],
];
}
}

View File

@ -0,0 +1,42 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\BillingSubscription;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
class UpdateBillingSubscriptionRequest extends Request
{
use ChecksEntityStatus;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->billing_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use App\Models\ClientSubscription;
class CreateClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return auth()->user()->can('create', ClientSubscription::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class DestroyClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class EditClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class ShowClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('view', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,38 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use App\Models\ClientSubscription;
class StoreClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('create', ClientSubscription::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [];
}
}

View File

@ -0,0 +1,42 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
class UpdateClientSubscriptionRequest extends Request
{
use ChecksEntityStatus;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -54,6 +54,10 @@ class StoreDesignRequest extends Request
$input['design']['footer'] = ''; $input['design']['footer'] = '';
} }
if (! array_key_exists('header', $input['design']) || is_null($input['design']['header'])) {
$input['design']['header'] = '';
}
$this->replace($input); $this->replace($input);
} }
} }

View File

@ -53,6 +53,10 @@ class UpdateDesignRequest extends Request
$input['design']['footer'] = ''; $input['design']['footer'] = '';
} }
if (! array_key_exists('header', $input['design']) || is_null($input['design']['header'])) {
$input['design']['header'] = '';
}
$this->replace($input); $this->replace($input);
} }
} }

View File

@ -70,6 +70,10 @@ class Request extends FormRequest
public function decodePrimaryKeys($input) public function decodePrimaryKeys($input)
{ {
if (array_key_exists('subscription_id', $input) && is_string($input['subscription_id'])) {
$input['subscription_id'] = $this->decodePrimaryKey($input['subscription_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) { if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']); $input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
} }

View File

@ -23,6 +23,6 @@ class ReconfirmUserRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->isAdmin(); return auth()->user()->id == $this->user->id;
} }
} }

View File

@ -96,22 +96,6 @@ class CreateAccount
//todo implement SLACK notifications //todo implement SLACK notifications
//$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja(); //$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja();
// $nmo = new NinjaMailerObject;
// $nmo->mailable = new NinjaMailer((new AccountCreatedObject($spaa9f78, $sp035a66))->build());
// $nmo->company = $sp035a66;
// $nmo->to_user = $spaa9f78;
// $nmo->settings = $sp035a66->settings;
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new VerifyUserObject($spaa9f78, $sp035a66))->build());
$nmo->company = $sp035a66;
$nmo->to_user = $spaa9f78;
$nmo->settings = $sp035a66->settings;
NinjaMailerJob::dispatch($nmo);
// NinjaMailerJob::dispatchNow($nmo);
VersionCheck::dispatchNow(); VersionCheck::dispatchNow();
LightLogs::create(new AnalyticsAccountCreated()) LightLogs::create(new AnalyticsAccountCreated())

View File

@ -0,0 +1,72 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Jobs\Cron;
use App\Libraries\MultiDB;
use App\Models\ClientSubscription;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Carbon;
class BillingSubscriptionCron
{
use Dispatchable;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
}
/**
* Execute the job.
*
* @return void
*/
public function handle() : void
{
if (! config('ninja.db.multi_db_enabled')) {
$this->loopSubscriptions();
} else {
//multiDB environment, need to
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$this->loopSubscriptions();
}
}
}
private function loopSubscriptions()
{
$client_subs = ClientSubscription::whereNull('deleted_at')
->cursor()
->each(function ($cs){
$this->processSubscription($cs);
});
}
/* Our daily cron should check
1. Is the subscription still in trial phase?
2. Check the recurring invoice and its remaining_cycles to see whether we need to cancel or perform any other function.
3. Any notifications that need to fire?
*/
private function processSubscription($client_subscription)
{
}
}

View File

@ -82,8 +82,10 @@ class CreateUser
'settings' => null, 'settings' => null,
]); ]);
if(!Ninja::isSelfHost()) if(!Ninja::isSelfHost()){
nlog("in the create user class");
event(new UserWasCreated($user, $user, $this->company, Ninja::eventVars())); event(new UserWasCreated($user, $user, $this->company, Ninja::eventVars()));
}
return $user; return $user;
} }

View File

@ -59,7 +59,7 @@ class PaymentNotification implements ShouldQueue
foreach ($payment->company->company_users as $company_user) { foreach ($payment->company->company_users as $company_user) {
$user = $company_user->user; $user = $company_user->user;
$methods = $this->findUserEntityNotificationType($payment, $company_user, ['all_notifications']); $methods = $this->findUserEntityNotificationType($payment, $company_user, ['payment_success_all', 'all_notifications']);
if (($key = array_search('mail', $methods)) !== false) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); unset($methods[$key]);
@ -69,19 +69,8 @@ class PaymentNotification implements ShouldQueue
NinjaMailerJob::dispatch($nmo); NinjaMailerJob::dispatch($nmo);
} }
// $notification = new NewPaymentNotification($payment, $payment->company);
// $notification->method = $methods;
// if ($user) {
// $user->notify($notification);
// }
} }
/*Company Notifications*/
// if (isset($payment->company->slack_webhook_url)) {
// Notification::route('slack', $payment->company->slack_webhook_url)
// ->notify(new NewPaymentNotification($payment, $payment->company, true));
// }
/*Google Analytics Track Revenue*/ /*Google Analytics Track Revenue*/
if (isset($payment->company->google_analytics_key)) { if (isset($payment->company->google_analytics_key)) {

View File

@ -46,7 +46,8 @@ class SendVerificationNotification implements ShouldQueue
*/ */
public function handle($event) public function handle($event)
{ {
nlog("In Send Verification Notification");
MultiDB::setDB($event->company->db); MultiDB::setDB($event->company->db);
$event->user->service()->invite($event->company); $event->user->service()->invite($event->company);

View File

@ -0,0 +1,71 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class BillingSubscription extends BaseModel
{
use HasFactory, SoftDeletes;
protected $fillable = [
'user_id',
'product_id',
'company_id',
'product_id',
'is_recurring',
'frequency_id',
'auto_bill',
'promo_code',
'promo_discount',
'is_amount_discount',
'allow_cancellation',
'per_set_enabled',
'min_seats_limit',
'max_seats_limit',
'trial_enabled',
'trial_duration',
'allow_query_overrides',
'allow_plan_changes',
'plan_map',
'refund_period',
'webhook_configuration',
'currency_id',
];
protected $casts = [
'is_deleted' => 'boolean',
'plan_map' => 'object',
'webhook_configuration' => 'object',
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
];
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Company::class);
}
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(User::class);
}
public function product(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Product::class);
}
}

View File

@ -0,0 +1,55 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class ClientSubscription extends BaseModel
{
use HasFactory;
protected $fillable = [
// 'subscription_id',
// 'recurring_invoice_id',
// 'client_id',
// 'trial_started',
// 'trial_ends',
// 'is_deleted',
];
protected $casts = [
'is_deleted' => 'boolean',
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
];
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Company::class);
}
public function recurring_invoice(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(RecurringInvoice::class);
}
public function client(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Client::class);
}
public function subscription(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(BillingSubscription::class);
}
}

View File

@ -86,6 +86,7 @@ class Company extends BaseModel
'session_timeout', 'session_timeout',
'oauth_password_required', 'oauth_password_required',
'invoice_task_datelog', 'invoice_task_datelog',
'default_password_timeout',
]; ];
protected $hidden = [ protected $hidden = [
@ -158,6 +159,16 @@ class Company extends BaseModel
return $this->hasMany(ExpenseCategory::class)->withTrashed(); return $this->hasMany(ExpenseCategory::class)->withTrashed();
} }
public function client_subscriptions()
{
return $this->hasMany(ClientSubscription::class)->withTrashed();
}
public function billing_subscriptions()
{
return $this->hasMany(BillingSubscription::class)->withTrashed();
}
public function task_statuses() public function task_statuses()
{ {
return $this->hasMany(TaskStatus::class)->withTrashed(); return $this->hasMany(TaskStatus::class)->withTrashed();

View File

@ -77,6 +77,7 @@ class Credit extends BaseModel
'design_id', 'design_id',
'assigned_user_id', 'assigned_user_id',
'exchange_rate', 'exchange_rate',
'subscription_id',
]; ];
protected $casts = [ protected $casts = [

View File

@ -91,6 +91,7 @@ class Invoice extends BaseModel
'design_id', 'design_id',
'assigned_user_id', 'assigned_user_id',
'exchange_rate', 'exchange_rate',
'subscription_id',
]; ];
protected $casts = [ protected $casts = [

View File

@ -36,7 +36,7 @@ class CompanyPresenter extends EntityPresenter
$settings = $this->entity->settings; $settings = $this->entity->settings;
} }
if(strlen($settings->company_logo) >= 1 && strpos($settings->company_logo, 'http')) if(strlen($settings->company_logo) >= 1 && (strpos($settings->company_logo, 'http') !== false))
return $settings->company_logo; return $settings->company_logo;
else if(strlen($settings->company_logo) >= 1) else if(strlen($settings->company_logo) >= 1)
return url('') . $settings->company_logo; return url('') . $settings->company_logo;

View File

@ -78,6 +78,7 @@ class Quote extends BaseModel
'design_id', 'design_id',
'assigned_user_id', 'assigned_user_id',
'exchange_rate', 'exchange_rate',
'subscription_id',
]; ];
protected $casts = [ protected $casts = [

View File

@ -82,7 +82,6 @@ class User extends Authenticatable implements MustVerifyEmail
'custom_value4', 'custom_value4',
'is_deleted', 'is_deleted',
'google_2fa_secret', 'google_2fa_secret',
'default_password_timeout',
]; ];
/** /**

View File

@ -0,0 +1,72 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Observers;
use App\Models\BillingSubscription;
class BillingSubscriptionObserver
{
/**
* Handle the billing_subscription "created" event.
*
* @param BillingSubscription $billing_subscription
* @return void
*/
public function created(BillingSubscription $billing_subscription)
{
//
}
/**
* Handle the billing_subscription "updated" event.
*
* @param BillingSubscription $billing_subscription
* @return void
*/
public function updated(BillingSubscription $billing_subscription)
{
//
}
/**
* Handle the billing_subscription "deleted" event.
*
* @param BillingSubscription $billing_subscription
* @return void
*/
public function deleted(BillingSubscription $billing_subscription)
{
//
}
/**
* Handle the billing_subscription "restored" event.
*
* @param BillingSubscription $billing_subscription
* @return void
*/
public function restored(BillingSubscription $billing_subscription)
{
//
}
/**
* Handle the billing_subscription "force deleted" event.
*
* @param BillingSubscription $billing_subscription
* @return void
*/
public function forceDeleted(BillingSubscription $billing_subscription)
{
//
}
}

View File

@ -0,0 +1,72 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Observers;
use App\Models\ClientSubscription;
class ClientSubscriptionObserver
{
/**
* Handle the client_subscription "created" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function created(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "updated" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function updated(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "deleted" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function deleted(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "restored" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function restored(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "force deleted" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function forceDeleted(ClientSubscription $client_subscription)
{
//
}
}

View File

@ -0,0 +1,31 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Policies;
use App\Models\User;
/**
* Class BillingSubscriptionPolicy.
*/
class BillingSubscriptionPolicy extends EntityPolicy
{
/**
* Checks if the user has create permissions.
*
* @param User $user
* @return bool
*/
public function create(User $user) : bool
{
return $user->isAdmin() || $user->hasPermission('create_billing_subscription') || $user->hasPermission('create_all');
}
}

View File

@ -0,0 +1,31 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Policies;
use App\Models\User;
/**
* Class ClientSubscriptionPolicy.
*/
class ClientSubscriptionPolicy extends EntityPolicy
{
/**
* Checks if the user has create permissions.
*
* @param User $user
* @return bool
*/
public function create(User $user) : bool
{
return $user->isAdmin() || $user->hasPermission('create_client_subscription') || $user->hasPermission('create_all');
}
}

View File

@ -12,7 +12,9 @@
namespace App\Providers; namespace App\Providers;
use App\Models\Account; use App\Models\Account;
use App\Models\BillingSubscription;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\Company; use App\Models\Company;
use App\Models\CompanyGateway; use App\Models\CompanyGateway;
use App\Models\CompanyToken; use App\Models\CompanyToken;
@ -26,7 +28,9 @@ use App\Models\Quote;
use App\Models\Task; use App\Models\Task;
use App\Models\User; use App\Models\User;
use App\Observers\AccountObserver; use App\Observers\AccountObserver;
use App\Observers\BillingSubscriptionObserver;
use App\Observers\ClientObserver; use App\Observers\ClientObserver;
use App\Observers\ClientSubscriptionObserver;
use App\Observers\CompanyGatewayObserver; use App\Observers\CompanyGatewayObserver;
use App\Observers\CompanyObserver; use App\Observers\CompanyObserver;
use App\Observers\CompanyTokenObserver; use App\Observers\CompanyTokenObserver;
@ -75,9 +79,10 @@ class AppServiceProvider extends ServiceProvider
Schema::defaultStringLength(191); Schema::defaultStringLength(191);
User::observe(UserObserver::class);
Account::observe(AccountObserver::class); Account::observe(AccountObserver::class);
BillingSubscription::observe(BillingSubscriptionObserver::class);
Client::observe(ClientObserver::class); Client::observe(ClientObserver::class);
ClientSubscription::observe(ClientSubscriptionObserver::class);
Company::observe(CompanyObserver::class); Company::observe(CompanyObserver::class);
CompanyGateway::observe(CompanyGatewayObserver::class); CompanyGateway::observe(CompanyGatewayObserver::class);
CompanyToken::observe(CompanyTokenObserver::class); CompanyToken::observe(CompanyTokenObserver::class);
@ -89,6 +94,7 @@ class AppServiceProvider extends ServiceProvider
Proposal::observe(ProposalObserver::class); Proposal::observe(ProposalObserver::class);
Quote::observe(QuoteObserver::class); Quote::observe(QuoteObserver::class);
Task::observe(TaskObserver::class); Task::observe(TaskObserver::class);
User::observe(UserObserver::class);
// Queue::before(function (JobProcessing $event) { // Queue::before(function (JobProcessing $event) {
// // \Log::info('Event Job '.$event->connectionName); // // \Log::info('Event Job '.$event->connectionName);

View File

@ -12,7 +12,9 @@
namespace App\Providers; namespace App\Providers;
use App\Models\Activity; use App\Models\Activity;
use App\Models\BillingSubscription;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\Company; use App\Models\Company;
use App\Models\CompanyGateway; use App\Models\CompanyGateway;
use App\Models\CompanyToken; use App\Models\CompanyToken;
@ -37,7 +39,9 @@ use App\Models\User;
use App\Models\Vendor; use App\Models\Vendor;
use App\Models\Webhook; use App\Models\Webhook;
use App\Policies\ActivityPolicy; use App\Policies\ActivityPolicy;
use App\Policies\BillingSubscriptionPolicy;
use App\Policies\ClientPolicy; use App\Policies\ClientPolicy;
use App\Policies\ClientSubscriptionPolicy;
use App\Policies\CompanyGatewayPolicy; use App\Policies\CompanyGatewayPolicy;
use App\Policies\CompanyPolicy; use App\Policies\CompanyPolicy;
use App\Policies\CompanyTokenPolicy; use App\Policies\CompanyTokenPolicy;
@ -73,7 +77,9 @@ class AuthServiceProvider extends ServiceProvider
*/ */
protected $policies = [ protected $policies = [
Activity::class => ActivityPolicy::class, Activity::class => ActivityPolicy::class,
BillingSubscription::class => BillingSubscriptionPolicy::class,
Client::class => ClientPolicy::class, Client::class => ClientPolicy::class,
ClientSubscription::class => ClientSubscriptionPolicy::class,
Company::class => CompanyPolicy::class, Company::class => CompanyPolicy::class,
CompanyToken::class => CompanyTokenPolicy::class, CompanyToken::class => CompanyTokenPolicy::class,
CompanyGateway::class => CompanyGatewayPolicy::class, CompanyGateway::class => CompanyGatewayPolicy::class,

View File

@ -0,0 +1,28 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Repositories;
use App\Models\BillingSubscription;
class BillingSubscriptionRepository extends BaseRepository
{
public function save($data, BillingSubscription $billing_subscription): ?BillingSubscription
{
$billing_subscription
->fill($data)
->save();
return $billing_subscription;
}
}

View File

@ -0,0 +1,28 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Repositories;
use App\Models\ClientSubscription;
class ClientSubscriptionRepository extends BaseRepository
{
public function save($data, ClientSubscription $client_subscription): ?ClientSubscription
{
$client_subscription
->fill($data)
->save();
return $client_subscription;
}
}

View File

@ -88,6 +88,8 @@ class CreditService
{ {
$this->credit = (new ApplyPayment($this->credit, $invoice, $amount, $payment))->run(); $this->credit = (new ApplyPayment($this->credit, $invoice, $amount, $payment))->run();
$this->deletePdf();
return $this; return $this;
} }

View File

@ -44,6 +44,7 @@ class MarkSent
->setStatus(Credit::STATUS_SENT) ->setStatus(Credit::STATUS_SENT)
->applyNumber() ->applyNumber()
->adjustBalance($this->credit->amount) ->adjustBalance($this->credit->amount)
->deletePdf()
->save(); ->save();

View File

@ -22,6 +22,7 @@ use App\Models\Task;
use App\Services\Client\ClientService; use App\Services\Client\ClientService;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
class InvoiceService class InvoiceService
{ {
@ -273,8 +274,9 @@ class InvoiceService
public function deletePdf() public function deletePdf()
{ {
UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->number.'.pdf'); //UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->number.'.pdf');
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath() . $this->invoice->number.'.pdf');
return $this; return $this;
} }

View File

@ -74,6 +74,7 @@ class MarkPaid extends AbstractService
->updatePaidToDate($payment->amount) ->updatePaidToDate($payment->amount)
->setStatus(Invoice::STATUS_PAID) ->setStatus(Invoice::STATUS_PAID)
->applyNumber() ->applyNumber()
->deletePdf()
->save(); ->save();
if ($this->invoice->client->getSetting('client_manual_payment_notification')) if ($this->invoice->client->getSetting('client_manual_payment_notification'))

View File

@ -47,6 +47,7 @@ class MarkSent extends AbstractService
->applyNumber() ->applyNumber()
->setDueDate() ->setDueDate()
->updateBalance($this->invoice->amount) ->updateBalance($this->invoice->amount)
->deletePdf()
->save(); ->save();
$this->client->service()->updateBalance($this->invoice->balance)->save(); $this->client->service()->updateBalance($this->invoice->balance)->save();

View File

@ -43,6 +43,7 @@ class MarkSent
->service() ->service()
->setStatus(Quote::STATUS_SENT) ->setStatus(Quote::STATUS_SENT)
->applyNumber() ->applyNumber()
->deletePdf()
->save(); ->save();
return $this->quote; return $this->quote;

View File

@ -39,21 +39,6 @@ class QuoteService
return $this; return $this;
} }
// public function markApproved()
// {
// $mark_approved = new MarkApproved($this->quote->client);
// $this->quote = $mark_approved->run($this->quote);
// if ($this->quote->client->getSetting('auto_convert_quote') == true) {
// $this->convert();
// }
// $this->markSent()
// ->createInvitations();
// return $this;
// }
public function convert() :self public function convert() :self
{ {
if ($this->quote->invoice_id) { if ($this->quote->invoice_id) {
@ -127,6 +112,7 @@ class QuoteService
->service() ->service()
->markSent() ->markSent()
->createInvitations() ->createInvitations()
->deletePdf()
->save(); ->save();
} }

View File

@ -0,0 +1,73 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Transformers;
use App\Models\BillingSubscription;
use App\Utils\Traits\MakesHash;
class BillingSubscriptionTransformer extends EntityTransformer
{
use MakesHash;
/**
* @var array
*/
protected $defaultIncludes = [];
/**
* @var array
*/
protected $availableIncludes = [
'product',
];
public function transform(BillingSubscription $billing_subscription): array
{
return [
'id' => $this->encodePrimaryKey($billing_subscription->id),
'user_id' => $this->encodePrimaryKey($billing_subscription->user_id),
'product_id' => $this->encodePrimaryKey($billing_subscription->product_id),
'assigned_user_id' => $this->encodePrimaryKey($billing_subscription->assigned_user_id),
'company_id' => $this->encodePrimaryKey($billing_subscription->company_id),
'is_recurring' => (bool)$billing_subscription->is_recurring,
'frequency_id' => (string)$billing_subscription->frequency_id,
'auto_bill' => (string)$billing_subscription->auto_bill,
'promo_code' => (string)$billing_subscription->promo_code,
'promo_discount' => (float)$billing_subscription->promo_discount,
'is_amount_discount' => (bool)$billing_subscription->is_amount_discount,
'allow_cancellation' => (bool)$billing_subscription->allow_cancellation,
'per_seat_enabled' => (bool)$billing_subscription->per_set_enabled,
'min_seats_limit' => (int)$billing_subscription->min_seats_limit,
'max_seats_limit' => (int)$billing_subscription->max_seats_limit,
'trial_enabled' => (bool)$billing_subscription->trial_enabled,
'trial_duration' => (int)$billing_subscription->trial_duration,
'allow_query_overrides' => (bool)$billing_subscription->allow_query_overrides,
'allow_plan_changes' => (bool)$billing_subscription->allow_plan_changes,
'plan_map' => (string)$billing_subscription->plan_map,
'refund_period' => (int)$billing_subscription->refund_period,
'webhook_configuration' => (string)$billing_subscription->webhook_configuration,
'is_deleted' => (bool)$billing_subscription->is_deleted,
'created_at' => (int)$billing_subscription->created_at,
'updated_at' => (int)$billing_subscription->updated_at,
'archived_at' => (int)$billing_subscription->deleted_at,
];
}
public function includeProduct(BillingSubscription $billing_subscription): \League\Fractal\Resource\Item
{
$transformer = new ProductTransformer($this->serializer);
return $this->includeItem($billing_subscription->product, $transformer, Product::class);
}
}

View File

@ -0,0 +1,78 @@
<?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://opensource.org/licenses/AAL
*/
namespace App\Transformers;
use App\Models\BillingSubscription;
use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\RecurringInvoice;
use App\Utils\Traits\MakesHash;
class ClientSubscriptionTransformer extends EntityTransformer
{
use MakesHash;
/**
* @var array
*/
protected $defaultIncludes = [];
/**
* @var array
*/
protected $availableIncludes = [
'client',
'recurring_invoice',
'subscription',
];
public function transform(ClientSubscription $client_subscription): array
{
return [
'id' => $this->encodePrimaryKey($client_subscription->id),
'subscription_id' => $this->encodePrimaryKey($client_subscription->subscription_id),
'company_id' => $this->encodePrimaryKey($client_subscription->company_id),
'recurring_invoice_id' => $this->encodePrimaryKey($client_subscription->recurring_invoice_id),
'client_id' => $this->encodePrimaryKey($client_subscription->client_id),
'trial_started' => (string)$client_subscription->trial_started ?: '',
'trial_ends' => (string)$client_subscription->trial_ends ?: '',
'is_deleted' => (bool)$client_subscription->is_deleted,
'created_at' => (int)$client_subscription->created_at,
'updated_at' => (int)$client_subscription->updated_at,
'archived_at' => (int)$client_subscription->deleted_at,
];
}
public function includeClient(ClientSubscription $client_subscription): \League\Fractal\Resource\Item
{
$transformer = new ClientTransformer($this->serializer);
return $this->includeItem($client_subscription->client, $transformer, Client::class);
}
public function includeRecurringInvoice(ClientSubscription $client_subscription): \League\Fractal\Resource\Item
{
$transformer = new RecurringInvoiceTransformer($this->serializer);
return $this->includeItem($client_subscription->recurring_invoice, $transformer, RecurringInvoice::class);
}
public function includeSubscription(ClientSubscription $client_subscription): \League\Fractal\Resource\Item
{
$transformer = new BillingSubscriptionTransformer($this->serializer);
return $this->includeItem($client_subscription->subscription, $transformer, BillingSubscription::class);
}
}

View File

@ -13,7 +13,9 @@ namespace App\Transformers;
use App\Models\Account; use App\Models\Account;
use App\Models\Activity; use App\Models\Activity;
use App\Models\BillingSubscription;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\Company; use App\Models\Company;
use App\Models\CompanyGateway; use App\Models\CompanyGateway;
use App\Models\CompanyLedger; use App\Models\CompanyLedger;
@ -90,6 +92,7 @@ class CompanyTransformer extends EntityTransformer
'system_logs', 'system_logs',
'expense_categories', 'expense_categories',
'task_statuses', 'task_statuses',
'client_subscriptions',
]; ];
/** /**
@ -154,6 +157,7 @@ class CompanyTransformer extends EntityTransformer
'expense_amount_is_pretax' =>(bool)true, //@deprecate 1-2-2021 'expense_amount_is_pretax' =>(bool)true, //@deprecate 1-2-2021
'oauth_password_required' => (bool)$company->oauth_password_required, 'oauth_password_required' => (bool)$company->oauth_password_required,
'session_timeout' => (int)$company->session_timeout, 'session_timeout' => (int)$company->session_timeout,
'default_password_timeout' => (int) $company->default_password_timeout,
]; ];
} }
@ -357,4 +361,18 @@ class CompanyTransformer extends EntityTransformer
return $this->includeCollection($company->system_logs, $transformer, SystemLog::class); return $this->includeCollection($company->system_logs, $transformer, SystemLog::class);
} }
public function includeClientSubscriptions(Company $company)
{
$transformer = new ClientSubscriptionTransformer($this->serializer);
return $this->includeCollection($company->client_subscriptions, $transformer, ClientSubscription::class);
}
public function includeBillingSubscriptions(Company $company)
{
$transformer = new BillingSubscriptionTransformer($this->serializer);
return $this->includeCollection($company->billing_subscriptions, $transformer, BillingSubscription::class);
}
} }

View File

@ -137,6 +137,7 @@ class CreditTransformer extends EntityTransformer
'entity_type' => 'credit', 'entity_type' => 'credit',
'exchange_rate' => (float) $credit->exchange_rate, 'exchange_rate' => (float) $credit->exchange_rate,
'paid_to_date' => (float) $credit->paid_to_date, 'paid_to_date' => (float) $credit->paid_to_date,
'subscription_id' => $this->encodePrimaryKey($credit->subscription_id),
]; ];
} }
} }

View File

@ -141,6 +141,7 @@ class InvoiceTransformer extends EntityTransformer
'reminder3_sent' => $invoice->reminder3_sent ?: '', 'reminder3_sent' => $invoice->reminder3_sent ?: '',
'reminder_last_sent' => $invoice->reminder_last_sent ?: '', 'reminder_last_sent' => $invoice->reminder_last_sent ?: '',
'paid_to_date' => (float) $invoice->paid_to_date, 'paid_to_date' => (float) $invoice->paid_to_date,
'subscription_id' => $this->encodePrimaryKey($invoice->subscription_id),
]; ];
} }

View File

@ -139,6 +139,8 @@ class QuoteTransformer extends EntityTransformer
'exchange_rate' => (float) $quote->exchange_rate, 'exchange_rate' => (float) $quote->exchange_rate,
'paid_to_date' => (float) $quote->paid_to_date, 'paid_to_date' => (float) $quote->paid_to_date,
'project_id' => $this->encodePrimaryKey($quote->project_id), 'project_id' => $this->encodePrimaryKey($quote->project_id),
'subscription_id' => $this->encodePrimaryKey($quote->subscription_id),
]; ];
} }
} }

View File

@ -26,7 +26,7 @@ class Helpers
$elements['signature'] = ''; $elements['signature'] = '';
$elements['settings'] = new stdClass; $elements['settings'] = new stdClass;
$elements['whitelabel'] = true; $elements['whitelabel'] = true;
$elements['company'] = '';
return $elements; return $elements;
} }
@ -35,7 +35,8 @@ class Helpers
$elements['signature'] = $_settings->email_signature; $elements['signature'] = $_settings->email_signature;
$elements['settings'] = $_settings; $elements['settings'] = $_settings;
$elements['whitelabel'] = $client->user->account->isPaid() ? true : false; $elements['whitelabel'] = $client->user->account->isPaid() ? true : false;
$elements['company'] = $client->company;
return $elements; return $elements;
} }

View File

@ -65,7 +65,7 @@
"predis/predis": "^1.1", "predis/predis": "^1.1",
"sentry/sentry-laravel": "^2", "sentry/sentry-laravel": "^2",
"stripe/stripe-php": "^7.50", "stripe/stripe-php": "^7.50",
"turbo124/beacon": "^1", "turbo124/beacon": "^1.0",
"webpatser/laravel-countries": "dev-master#75992ad", "webpatser/laravel-countries": "dev-master#75992ad",
"wildbit/swiftmailer-postmark": "^3.3" "wildbit/swiftmailer-postmark": "^3.3"
}, },

713
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', ''), 'app_domain' => env('APP_DOMAIN', ''),
'app_version' => '5.1.15', 'app_version' => '5.1.19',
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false), 'api_secret' => env('API_SECRET', false),

View File

@ -13,7 +13,7 @@ return [
| |
*/ */
'default' => env('QUEUE_CONNECTION', 'database'), 'default' => env('QUEUE_CONNECTION', 'sync'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -0,0 +1,38 @@
<?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://opensource.org/licenses/AAL
*/
namespace Database\Factories;
use App\Models\BillingSubscription;
use Illuminate\Database\Eloquent\Factories\Factory;
class BillingSubscriptionFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = BillingSubscription::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
];
}
}

View File

@ -37,7 +37,6 @@ class UserFactory extends Factory
'email_verified_at' => now(), 'email_verified_at' => now(),
'password' => bcrypt(config('ninja.testvars.password')), // secret 'password' => bcrypt(config('ninja.testvars.password')), // secret
'remember_token' => \Illuminate\Support\Str::random(10), 'remember_token' => \Illuminate\Support\Str::random(10),
'default_password_timeout' => 30,
]; ];
} }
} }

View File

@ -0,0 +1,76 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBillingSubscriptionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('billing_subscriptions', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('assigned_user_id');
$table->unsignedInteger('company_id');
$table->unsignedInteger('product_id');
$table->boolean('is_recurring')->default(false);
$table->unsignedInteger('frequency_id');
$table->string('auto_bill')->default('');
$table->string('promo_code')->default('');
$table->float('promo_discount')->default(0);
$table->boolean('is_amount_discount')->default(false);
$table->boolean('allow_cancellation')->default(true);
$table->boolean('per_seat_enabled')->default(false);
$table->unsignedInteger('min_seats_limit');
$table->unsignedInteger('max_seats_limit');
$table->boolean('trial_enabled')->default(false);
$table->unsignedInteger('trial_duration');
$table->boolean('allow_query_overrides')->default(false);
$table->boolean('allow_plan_changes')->default(false);
$table->mediumText('plan_map');
$table->unsignedInteger('refund_period')->nullable();
$table->mediumText('webhook_configuration');
$table->softDeletes('deleted_at', 6);
$table->boolean('is_deleted')->default(false);
$table->timestamps();
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->index(['company_id', 'deleted_at']);
});
Schema::create('client_subscriptions', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('company_id');
$table->unsignedInteger('subscription_id');
$table->unsignedInteger('recurring_invoice_id');
$table->unsignedInteger('client_id');
$table->unsignedInteger('trial_started')->nullable();
$table->unsignedInteger('trial_ends')->nullable();
$table->boolean('is_deleted')->default(false);
$table->softDeletes('deleted_at', 6);
$table->timestamps();
$table->foreign('subscription_id')->references('id')->on('billing_subscriptions');
$table->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices');
$table->foreign('client_id')->references('id')->on('clients');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->index(['company_id', 'deleted_at']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('billing_subscriptions');
Schema::dropIfExists('client_subscriptions');
}
}

View File

@ -1,5 +1,6 @@
<?php <?php
use App\Models\Company;
use App\Models\Language; use App\Models\Language;
use App\Models\User; use App\Models\User;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
@ -21,11 +22,11 @@ class AddRussianLang extends Migration
Language::unguard(); Language::unguard();
Language::create($russian); Language::create($russian);
Schema::table('users', function (Blueprint $table) { Schema::table('companies', function (Blueprint $table) {
$table->integer('default_password_timeout')->default(30); $table->integer('default_password_timeout')->default(30);
}); });
User::whereNotNull('id')->update(['default_password_timeout' => 30]); Company::whereNotNull('id')->update(['default_password_timeout' => 30]);
} }

View File

@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddCurrencyIdToBillingSubscriptionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('billing_subscriptions', function (Blueprint $table) {
$table->unsignedInteger('currency_id')->nullable();
});
Schema::table('invoices', function (Blueprint $table) {
$table->unsignedInteger('subscription_id')->nullable();
});
Schema::table('quotes', function (Blueprint $table) {
$table->unsignedInteger('subscription_id')->nullable();
});
Schema::table('credits', function (Blueprint $table) {
$table->unsignedInteger('subscription_id')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -30,7 +30,7 @@ const RESOURCES = {
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff", "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"/": "23224b5e03519aaa87594403d54412cf", "/": "23224b5e03519aaa87594403d54412cf",
"main.dart.js": "53da6fecadfcc13e83a501a510a76f81", "main.dart.js": "4f262274e979ed7c414d394fc5f0baea",
"version.json": "b7c8971e1ab5b627fd2a4317c52b843e", "version.json": "b7c8971e1ab5b627fd2a4317c52b843e",
"favicon.png": "dca91c54388f52eded692718d5a98b8b" "favicon.png": "dca91c54388f52eded692718d5a98b8b"
}; };

124063
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
@component('email.template.master', ['design' => 'light', 'whitelabel' => false]) @component('email.template.master', ['design' => 'light', 'whitelabel' => false, 'company' => $company])
@slot('header') @slot('header')
@include('email.components.header', ['logo' => $logo]) @include('email.components.header', ['logo' => $logo])

View File

@ -11,73 +11,70 @@
<p><img src="{{ $company->present()->logo() }}"></p> <p><img src="{{ $company->present()->logo() }}"></p>
@if(isset($company) && $company->clients->count() >=1) @if(isset($company) && $company->clients->count() >=1)
<p><b>Clients Imported:</b> {{ $company->clients->count() }} </p> <p><b>{{ ctrans('texts.clients') }}:</b> {{ $company->clients->count() }} </p>
@endif @endif
@if(isset($company) && count($company->products) >=1) @if(isset($company) && count($company->products) >=1)
<p><b>Products Imported:</b> {{ count($company->products) }} </p> <p><b>{{ ctrans('texts.products') }}:</b> {{ count($company->products) }} </p>
@endif @endif
@if(isset($company) && count($company->invoices) >=1) @if(isset($company) && count($company->invoices) >=1)
<p><b>Invoices Imported:</b> {{ count($company->invoices) }} </p> <p><b>{{ ctrans('texts.invoices') }}:</b> {{ count($company->invoices) }} </p>
<p>To test your PDF generation is working correctly, click <a href="{{$company->invoices->first()->invitations->first()->getLink() }}">here</a>. We've also attempted to attach the PDF to this email.
@endif @endif
@if(isset($company) && count($company->payments) >=1) @if(isset($company) && count($company->payments) >=1)
<p><b>Payments Imported:</b> {{ count($company->payments) }} </p> <p><b>{{ ctrans('texts.payments') }}:</b> {{ count($company->payments) }} </p>
@endif @endif
@if(isset($company) && count($company->recurring_invoices) >=1) @if(isset($company) && count($company->recurring_invoices) >=1)
<p><b>Recurring Invoices Imported:</b> {{ count($company->recurring_invoices) }} </p> <p><b>{{ ctrans('texts.recurring_invoices') }}:</b> {{ count($company->recurring_invoices) }} </p>
@endif @endif
@if(isset($company) && count($company->quotes) >=1) @if(isset($company) && count($company->quotes) >=1)
<p><b>Quotes Imported:</b> {{ count($company->quotes) }} </p> <p><b>{{ ctrans('texts.quotes') }}:</b> {{ count($company->quotes) }} </p>
@endif @endif
@if(isset($company) && count($company->credits) >=1) @if(isset($company) && count($company->credits) >=1)
<p><b>Credits Imported:</b> {{ count($company->credits) }} </p> <p><b>{{ ctrans('texts.credits') }}:</b> {{ count($company->credits) }} </p>
@endif @endif
@if(isset($company) && count($company->projects) >=1) @if(isset($company) && count($company->projects) >=1)
<p><b>Projects Imported:</b> {{ count($company->projects) }} </p> <p><b>{{ ctrans('texts.projects') }}:</b> {{ count($company->projects) }} </p>
@endif @endif
@if(isset($company) && count($company->tasks) >=1) @if(isset($company) && count($company->tasks) >=1)
<p><b>Tasks Imported:</b> {{ count($company->tasks) }} </p> <p><b>{{ ctrans('texts.tasks') }}:</b> {{ count($company->tasks) }} </p>
@endif @endif
@if(isset($company) && count($company->vendors) >=1) @if(isset($company) && count($company->vendors) >=1)
<p><b>Vendors Imported:</b> {{ count($company->vendors) }} </p> <p><b>{{ ctrans('texts.vendors') }}:</b> {{ count($company->vendors) }} </p>
@endif @endif
@if(isset($company) && count($company->expenses) >=1) @if(isset($company) && count($company->expenses) >=1)
<p><b>Expenses Imported:</b> {{ count($company->expenses) }} </p> <p><b>{{ ctrans('texts.expenses') }}:</b> {{ count($company->expenses) }} </p>
@endif @endif
@if(isset($company) && count($company->company_gateways) >=1) @if(isset($company) && count($company->company_gateways) >=1)
<p><b>Gateways Imported:</b> {{ count($company->company_gateways) }} </p> <p><b>{{ ctrans('texts.gateways') }}:</b> {{ count($company->company_gateways) }} </p>
@endif @endif
@if(isset($company) && count($company->client_gateway_tokens) >=1) @if(isset($company) && count($company->client_gateway_tokens) >=1)
<p><b>Client Gateway Tokens Imported:</b> {{ count($company->client_gateway_tokens) }} </p> <p><b>{{ ctrans('texts.tokens') }}:</b> {{ count($company->client_gateway_tokens) }} </p>
@endif @endif
@if(isset($company) && count($company->tax_rates) >=1) @if(isset($company) && count($company->tax_rates) >=1)
<p><b>Tax Rates Imported:</b> {{ count($company->tax_rates) }} </p> <p><b>{{ ctrans('texts.tax_rates') }}:</b> {{ count($company->tax_rates) }} </p>
@endif @endif
@if(isset($company) && count($company->documents) >=1) @if(isset($company) && count($company->documents) >=1)
<p><b>Documents Imported:</b> {{ count($company->documents) }} </p> <p><b>{{ ctrans('texts.documents') }}:</b> {{ count($company->documents) }} </p>
@endif @endif
<p><b>Data Quality:</b></p> <p><b>Data Quality:</b></p>
<p> {!! $check_data !!} </p> <p> {!! $check_data !!} </p>
@if(!empty($errors) ) @if(!empty($errors) )
<p>The following import errors occurred:</p> <p>{{ ctrans('texts.errors') }}:</p>
<table> <table>
<thead> <thead>
<tr> <tr>

View File

@ -1,7 +1,7 @@
@component('email.template.master', ['design' => 'dark', 'settings' => $settings, 'whitelabel' => $whitelabel]) @component('email.template.master', ['design' => 'dark', 'settings' => $settings, 'whitelabel' => $whitelabel])
@slot('header') @slot('header')
@include('email.components.header', ['logo' => (strlen($settings->company_logo) > 1) ? url('') . $settings->company_logo : 'https://www.invoiceninja.com/wp-content/uploads/2015/10/logo-white-horizontal-1.png']) @include('email.components.header', ['logo' => $company->present()->logo($settings)])
@endslot @endslot
{!! $body !!} {!! $body !!}

View File

@ -1,7 +1,7 @@
@component('email.template.master', ['design' => 'light', 'settings' => $settings, 'whitelabel' => $whitelabel]) @component('email.template.master', ['design' => 'light', 'settings' => $settings, 'whitelabel' => $whitelabel])
@slot('header') @slot('header')
@include('email.components.header', ['logo' => (strlen($settings->company_logo) > 1) ? url('') . $settings->company_logo : 'https://www.invoiceninja.com/wp-content/uploads/2015/10/logo-white-horizontal-1.png']) @include('email.components.header', ['logo' => $company->present()->logo($settings)])
@endslot @endslot
{!! $body !!} {!! $body !!}

View File

@ -2,7 +2,7 @@
<div class="flex flex-col w-64"> <div class="flex flex-col w-64">
<div class="flex items-center h-16 flex-shrink-0 px-4 bg-primary-darken justify-center"> <div class="flex items-center h-16 flex-shrink-0 px-4 bg-primary-darken justify-center">
<a href="{{ route('client.dashboard') }}"> <a href="{{ route('client.dashboard') }}">
<img class="h-8 w-auto" src="{!! $settings->company_logo ? url('') . $settings->company_logo : asset('images/invoiceninja-white-logo.png') !!}" alt="{{ config('app.name') }}" /> <img class="h-8 w-auto" src="{!! auth('contact')->user()->company->present()->logo($settings) !!}" alt="{{ config('app.name') }}" />
</a> </a>
</div> </div>
<div class="h-0 flex-1 flex flex-col overflow-y-auto"> <div class="h-0 flex-1 flex flex-col overflow-y-auto">

View File

@ -9,7 +9,7 @@
</button> </button>
</div> </div>
<div class="flex-shrink-0 flex items-center px-4"> <div class="flex-shrink-0 flex items-center px-4">
<img class="h-6 w-auto" src="{!! $settings->company_logo ? url('') . $settings->company_logo : asset('images/invoiceninja-white-logo.png') !!}" alt="{{ config('app.name') }}" /> <img class="h-6 w-auto" src="{!! auth('contact')->user()->company->present()->logo($settings) !!}" alt="{{ config('app.name') }}" />
</div> </div>
<div class="mt-5 flex-1 h-0 overflow-y-auto"> <div class="mt-5 flex-1 h-0 overflow-y-auto">
<nav class="flex-1 py-4 bg-primary"> <nav class="flex-1 py-4 bg-primary">

View File

@ -68,7 +68,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::post('emails', 'EmailController@send')->name('email.send')->middleware('user_verified'); Route::post('emails', 'EmailController@send')->name('email.send')->middleware('user_verified');
Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit
Route::put('expenses/{expense}/upload', 'ExpenseController@upload'); Route::put('expenses/{expense}/upload', 'ExpenseController@upload');
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
Route::resource('expense_categories', 'ExpenseCategoryController'); // name = (expense_categories. index / create / show / update / destroy / edit Route::resource('expense_categories', 'ExpenseCategoryController'); // name = (expense_categories. index / create / show / update / destroy / edit
@ -98,7 +98,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit
Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund'); Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund');
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
Route::put('payments/{payment}/upload', 'PaymentController@upload'); Route::put('payments/{payment}/upload', 'PaymentController@upload');
Route::resource('payment_terms', 'PaymentTermController'); // name = (payments. index / create / show / update / destroy / edit Route::resource('payment_terms', 'PaymentTermController'); // name = (payments. index / create / show / update / destroy / edit
Route::post('payment_terms/bulk', 'PaymentTermController@bulk')->name('payment_terms.bulk'); Route::post('payment_terms/bulk', 'PaymentTermController@bulk')->name('payment_terms.bulk');
@ -107,20 +107,20 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit
Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk');
Route::put('products/{product}/upload', 'ProductController@upload'); Route::put('products/{product}/upload', 'ProductController@upload');
Route::resource('projects', 'ProjectController'); // name = (projects. index / create / show / update / destroy / edit Route::resource('projects', 'ProjectController'); // name = (projects. index / create / show / update / destroy / edit
Route::post('projects/bulk', 'ProjectController@bulk')->name('projects.bulk'); Route::post('projects/bulk', 'ProjectController@bulk')->name('projects.bulk');
Route::put('projects/{project}/upload', 'ProjectController@upload')->name('projects.upload'); Route::put('projects/{project}/upload', 'ProjectController@upload')->name('projects.upload');
Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit
Route::get('quotes/{quote}/{action}', 'QuoteController@action')->name('quotes.action'); Route::get('quotes/{quote}/{action}', 'QuoteController@action')->name('quotes.action');
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk'); Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
Route::put('quotes/{quote}/upload', 'QuoteController@upload'); Route::put('quotes/{quote}/upload', 'QuoteController@upload');
Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk'); Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk');
Route::put('recurring_invoices/{recurring_invoice}/upload', 'RecurringInvoiceController@upload'); Route::put('recurring_invoices/{recurring_invoice}/upload', 'RecurringInvoiceController@upload');
Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk'); Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk');
@ -137,7 +137,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
Route::put('tasks/{task}/upload', 'TaskController@upload'); Route::put('tasks/{task}/upload', 'TaskController@upload');
Route::resource('task_statuses', 'TaskStatusController'); // name = (task_statuses. index / create / show / update / destroy / edit Route::resource('task_statuses', 'TaskStatusController'); // name = (task_statuses. index / create / show / update / destroy / edit
Route::post('task_statuses/bulk', 'TaskStatusController@bulk')->name('task_statuses.bulk'); Route::post('task_statuses/bulk', 'TaskStatusController@bulk')->name('task_statuses.bulk');
@ -155,7 +155,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk'); Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
Route::put('vendors/{vendor}/upload', 'VendorController@upload'); Route::put('vendors/{vendor}/upload', 'VendorController@upload');
Route::get('users', 'UserController@index'); Route::get('users', 'UserController@index');
Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); Route::put('users/{user}', 'UserController@update')->middleware('password_protected');
@ -165,6 +165,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected'); Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected');
Route::post('/users/{user}/invite', 'UserController@invite')->middleware('password_protected'); Route::post('/users/{user}/invite', 'UserController@invite')->middleware('password_protected');
Route::post('/user/{user}/reconfirm', 'UserController@reconfirm');
Route::resource('webhooks', 'WebhookController'); Route::resource('webhooks', 'WebhookController');
Route::post('webhooks/bulk', 'WebhookController@bulk')->name('webhooks.bulk'); Route::post('webhooks/bulk', 'WebhookController@bulk')->name('webhooks.bulk');
@ -173,7 +174,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
// Route::post('hooks', 'SubscriptionController@subscribe')->name('hooks.subscribe'); // Route::post('hooks', 'SubscriptionController@subscribe')->name('hooks.subscribe');
// Route::delete('hooks/{subscription_id}', 'SubscriptionController@unsubscribe')->name('hooks.unsubscribe'); // Route::delete('hooks/{subscription_id}', 'SubscriptionController@unsubscribe')->name('hooks.unsubscribe');
Route::resource('billing_subscriptions', 'BillingSubscriptionController');
Route::resource('cliente_subscriptions', 'ClientSubscriptionController');
}); });
Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id}', 'PaymentWebhookController') Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id}', 'PaymentWebhookController')

View File

@ -0,0 +1,134 @@
<?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://opensource.org/licenses/AAL
*/
namespace Tests\Feature;
use App\Models\BillingSubscription;
use App\Models\Product;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Session;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
* @covers App\Http\Controllers\BillingSubscriptionController
*/
class BillingSubscriptionApiTest extends TestCase
{
use MakesHash;
use DatabaseTransactions;
use MockAccountData;
public function setUp(): void
{
parent::setUp();
$this->makeTestData();
Session::start();
$this->faker = \Faker\Factory::create();
Model::reguard();
}
public function testExpenseGet()
{
$product = Product::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
$billing_subscription = BillingSubscription::factory()->create([
'product_id' => $product->id,
'company_id' => $this->company->id,
]);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/billing_subscriptions/' . $this->encodePrimaryKey($billing_subscription->id));
$response->assertStatus(200);
}
public function testBillingSubscriptionsPost()
{
$product = Product::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/billing_subscriptions', ['product_id' => $product->id, 'allow_cancellation' => true]);
$response->assertStatus(200);
}
public function testBillingSubscriptionPut()
{
$product = Product::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
$response1 = $this
->withHeaders(['X-API-SECRET' => config('ninja.api_secret'),'X-API-TOKEN' => $this->token])
->post('/api/v1/billing_subscriptions', ['product_id' => $product->id])
->assertStatus(200)
->json();
$response2 = $this
->withHeaders(['X-API-SECRET' => config('ninja.api_secret'),'X-API-TOKEN' => $this->token])
->put('/api/v1/billing_subscriptions/' . $response1['data']['id'], ['allow_cancellation' => true])
->assertStatus(200)
->json();
$this->assertNotEquals($response1['data']['allow_cancellation'], $response2['data']['allow_cancellation']);
}
/*
TypeError : Argument 1 passed to App\Transformers\BillingSubscriptionTransformer::transform() must be an instance of App\Models\BillingSubscription, bool given, called in /var/www/html/vendor/league/fractal/src/Scope.php on line 407
/var/www/html/app/Transformers/BillingSubscriptionTransformer.php:35
/var/www/html/vendor/league/fractal/src/Scope.php:407
/var/www/html/vendor/league/fractal/src/Scope.php:349
/var/www/html/vendor/league/fractal/src/Scope.php:235
/var/www/html/app/Http/Controllers/BaseController.php:395
/var/www/html/app/Http/Controllers/BillingSubscriptionController.php:408
*/
public function testBillingSubscriptionDeleted()
{
$product = Product::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
$billing_subscription = BillingSubscription::factory()->create([
'product_id' => $product->id,
'company_id' => $this->company->id,
]);
$response = $this
->withHeaders(['X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token])
->delete('/api/v1/billing_subscriptions/' . $this->encodePrimaryKey($billing_subscription->id))
->assertStatus(200)
->json();
}
}