diff --git a/app/Events/RecurringInvoice/RecurringInvoiceWasArchived.php b/app/Events/RecurringInvoice/RecurringInvoiceWasArchived.php new file mode 100644 index 0000000000..61fc9f4e98 --- /dev/null +++ b/app/Events/RecurringInvoice/RecurringInvoiceWasArchived.php @@ -0,0 +1,47 @@ +recurring_invoice = $recurring_invoice; + $this->company = $company; + $this->event_vars = $event_vars; + } +} diff --git a/app/Events/RecurringInvoice/RecurringInvoiceWasCreated.php b/app/Events/RecurringInvoice/RecurringInvoiceWasCreated.php new file mode 100644 index 0000000000..caf760d37c --- /dev/null +++ b/app/Events/RecurringInvoice/RecurringInvoiceWasCreated.php @@ -0,0 +1,47 @@ +recurring_invoice = $recurring_invoice; + $this->company = $company; + $this->event_vars = $event_vars; + } +} diff --git a/app/Events/RecurringInvoice/RecurringInvoiceWasDeleted.php b/app/Events/RecurringInvoice/RecurringInvoiceWasDeleted.php new file mode 100644 index 0000000000..f4c6be1538 --- /dev/null +++ b/app/Events/RecurringInvoice/RecurringInvoiceWasDeleted.php @@ -0,0 +1,47 @@ +recurring_invoice = $recurring_invoice; + $this->company = $company; + $this->event_vars = $event_vars; + } +} diff --git a/app/Events/RecurringInvoice/RecurringInvoiceWasRestored.php b/app/Events/RecurringInvoice/RecurringInvoiceWasRestored.php new file mode 100644 index 0000000000..a037e65c86 --- /dev/null +++ b/app/Events/RecurringInvoice/RecurringInvoiceWasRestored.php @@ -0,0 +1,51 @@ +recurring_invoice = $recurring_invoice; + $this->fromDeleted = $fromDeleted; + $this->company = $company; + $this->event_vars = $event_vars; + } +} diff --git a/app/Events/RecurringInvoice/RecurringInvoiceWasUpdated.php b/app/Events/RecurringInvoice/RecurringInvoiceWasUpdated.php new file mode 100644 index 0000000000..f7b496f663 --- /dev/null +++ b/app/Events/RecurringInvoice/RecurringInvoiceWasUpdated.php @@ -0,0 +1,49 @@ +recurring_invoice = $recurring_invoice; + $this->company = $company; + $this->event_vars = $event_vars; + } +} diff --git a/app/Helpers/Generic.php b/app/Helpers/Generic.php index e12505e3c6..3210afd1d9 100644 --- a/app/Helpers/Generic.php +++ b/app/Helpers/Generic.php @@ -30,6 +30,7 @@ function nlog($output, $context = []): void } $trace = debug_backtrace(); + //nlog( debug_backtrace()[1]['function']); // \Illuminate\Support\Facades\Log::channel('invoiceninja')->info(print_r($trace[1]['class'],1), []); \Illuminate\Support\Facades\Log::channel('invoiceninja')->info($output, $context); diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 890f5a64fa..dfe95b82af 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -334,8 +334,6 @@ class BaseController extends Controller 'company.task_statuses'=> function ($query) use ($updated_at, $user) { $query->where('updated_at', '>=', $updated_at); - if(!$user->isAdmin()) - $query->where('task_statuses.user_id', $user->id); }, 'company.activities'=> function ($query) use($user) { @@ -518,9 +516,6 @@ class BaseController extends Controller 'company.task_statuses'=> function ($query) use ($created_at, $user) { $query->where('created_at', '>=', $created_at); - if(!$user->isAdmin()) - $query->where('task_statuses.user_id', $user->id); - }, 'company.activities'=> function ($query) use($user) { diff --git a/app/Http/Controllers/RecurringInvoiceController.php b/app/Http/Controllers/RecurringInvoiceController.php index e5a94e25ac..cbe08ba890 100644 --- a/app/Http/Controllers/RecurringInvoiceController.php +++ b/app/Http/Controllers/RecurringInvoiceController.php @@ -11,6 +11,8 @@ namespace App\Http\Controllers; +use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; +use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated; use App\Factory\RecurringInvoiceFactory; use App\Filters\RecurringInvoiceFilters; use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest; @@ -25,6 +27,7 @@ use App\Models\Account; use App\Models\RecurringInvoice; use App\Repositories\RecurringInvoiceRepository; use App\Transformers\RecurringInvoiceTransformer; +use App\Utils\Ninja; use App\Utils\Traits\MakesHash; use App\Utils\Traits\SavesDocuments; use Illuminate\Http\Request; @@ -200,6 +203,8 @@ class RecurringInvoiceController extends BaseController { $recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id)); + event(new RecurringInvoiceWasCreated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); + return $this->itemResponse($recurring_invoice); } @@ -376,6 +381,8 @@ class RecurringInvoiceController extends BaseController $recurring_invoice->service()->deletePdf()->save(); + event(new RecurringInvoiceWasUpdated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); + return $this->itemResponse($recurring_invoice); } diff --git a/app/Http/Controllers/StripeConnectController.php b/app/Http/Controllers/StripeConnectController.php index 345268400a..055748b8c0 100644 --- a/app/Http/Controllers/StripeConnectController.php +++ b/app/Http/Controllers/StripeConnectController.php @@ -59,12 +59,8 @@ class StripeConnectController extends BaseController $account = Account::create($data); -nlog($account); - $link = Account::link($account->id, $token); -nlog($link); - if(!$company_gateway) $company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->id); diff --git a/app/Http/Middleware/ContactKeyLogin.php b/app/Http/Middleware/ContactKeyLogin.php index ff7a164a6c..9de9034bda 100644 --- a/app/Http/Middleware/ContactKeyLogin.php +++ b/app/Http/Middleware/ContactKeyLogin.php @@ -37,10 +37,6 @@ class ContactKeyLogin if (Auth::guard('contact')->check()) { Auth::guard('contact')->logout(); } -nlog("merp"); -nlog($request->has('magic_link')); -nlog($request->input('magic_link')); -nlog($request->all()); if ($request->segment(3) && config('ninja.db.multi_db_enabled')) { if (MultiDB::findAndSetDbByContactKey($request->segment(3))) { @@ -71,7 +67,6 @@ nlog($request->all()); } } elseif ($request->segment(2) && $request->segment(2) == 'magic_link' && $request->segment(3)) { $contact_email = Cache::get($request->segment(3)); -nlog("double merp"); if($client_contact = ClientContact::where('email', $contact_email)->first()){ Auth::guard('contact')->login($client_contact, true); @@ -83,7 +78,6 @@ nlog("double merp"); } } -nlog("exit"); return $next($request); } diff --git a/app/Http/Middleware/Cors.php b/app/Http/Middleware/Cors.php index 86f10e0049..f4dee108ee 100644 --- a/app/Http/Middleware/Cors.php +++ b/app/Http/Middleware/Cors.php @@ -22,15 +22,6 @@ class Cors return Response::make('OK', 200, $headers); } - /* Work around for file downloads where the response cannot contain have headers set */ - // if($request instanceOf BinaryFileResponse) - // return $next($request); - // else - // return $next($request) - // ->header('Access-Control-Allow-Origin', '*') - // ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') - // ->header('Access-Control-Allow-Headers', 'X-API-SECRET,X-API-TOKEN,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'); - $response = $next($request); $response->headers->set('Access-Control-Allow-Origin', '*'); diff --git a/app/Http/Middleware/QueryLogging.php b/app/Http/Middleware/QueryLogging.php index 488bfc7dae..6cb3123fc2 100644 --- a/app/Http/Middleware/QueryLogging.php +++ b/app/Http/Middleware/QueryLogging.php @@ -53,7 +53,6 @@ class QueryLogging $time = $timeEnd - $timeStart; nlog($request->method().' - '.urldecode($request->url()).": $count queries - ".$time); - // nlog($request->method().' - '.urldecode($request->fullUrl()).": $count queries - ".$time); // if($count > 50) //nlog($queries); diff --git a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php index 9e15687d32..d346684532 100644 --- a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php @@ -64,16 +64,6 @@ class StoreRecurringInvoiceRequest extends Request { $input = $this->all(); - // foreach($this->input('documents') as $document) - // { - // if($document instanceof UploadedFile){ - // nlog("i am an uploaded file"); - // nlog($document); - // } - // else - // nlog($document); - // } - if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { $input['design_id'] = $this->decodePrimaryKey($input['design_id']); } diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php index d61b46e597..fd91149a79 100644 --- a/app/Http/Requests/User/StoreUserRequest.php +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -59,10 +59,6 @@ class StoreUserRequest extends Request //unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false -//nlog($this->all()); -//nlog($this->input('company_user.account')); -// nlog($this->input('company_user.account.id')); -// nlog($this->input('company_user.account.id')); if (isset($input['company_user'])) { if (! isset($input['company_user']['is_admin'])) { diff --git a/app/Listeners/RecurringInvoice/CreateRecurringInvoiceActivity.php b/app/Listeners/RecurringInvoice/CreateRecurringInvoiceActivity.php new file mode 100644 index 0000000000..0bae19a9c4 --- /dev/null +++ b/app/Listeners/RecurringInvoice/CreateRecurringInvoiceActivity.php @@ -0,0 +1,54 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + MultiDB::setDb($event->company->db); + + $fields = new stdClass; + + $fields->recurring_invoice_id = $event->recurring_invoice->id; + $fields->client_id = $event->recurring_invoice->client_id; + $fields->user_id = $event->recurring_invoice->user_id; + $fields->company_id = $event->recurring_invoice->company_id; + $fields->activity_type_id = Activity::CREATE_RECURRING_INVOICE; + + $this->activity_repo->save($fields, $event->recurring_invoice, $event->event_vars); + } +} diff --git a/app/Listeners/RecurringInvoice/RecurringInvoiceArchivedActivity.php b/app/Listeners/RecurringInvoice/RecurringInvoiceArchivedActivity.php new file mode 100644 index 0000000000..24a8e5869c --- /dev/null +++ b/app/Listeners/RecurringInvoice/RecurringInvoiceArchivedActivity.php @@ -0,0 +1,56 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + MultiDB::setDb($event->company->db); + + $event->recurring_invoice->service()->deletePdf(); + + $fields = new stdClass; + + $fields->recurring_invoice_id = $event->recurring_invoice->id; + $fields->client_id = $event->recurring_invoice->client_id; + $fields->user_id = $event->recurring_invoice->user_id; + $fields->company_id = $event->recurring_invoice->company_id; + $fields->activity_type_id = Activity::ARCHIVE_RECURRING_INVOICE; + + $this->activity_repo->save($fields, $event->recurring_invoice, $event->event_vars); + } +} diff --git a/app/Listeners/RecurringInvoice/RecurringInvoiceDeletedActivity.php b/app/Listeners/RecurringInvoice/RecurringInvoiceDeletedActivity.php new file mode 100644 index 0000000000..4f9c8834a3 --- /dev/null +++ b/app/Listeners/RecurringInvoice/RecurringInvoiceDeletedActivity.php @@ -0,0 +1,54 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + MultiDB::setDb($event->company->db); + + $fields = new stdClass; + + $fields->recurring_invoice_id = $event->recurring_invoice->id; + $fields->client_id = $event->recurring_invoice->client_id; + $fields->user_id = $event->recurring_invoice->user_id; + $fields->company_id = $event->recurring_invoice->company_id; + $fields->activity_type_id = Activity::DELETE_RECURRING_INVOICE; + + $this->activity_repo->save($fields, $event->recurring_invoice, $event->event_vars); + } +} diff --git a/app/Listeners/RecurringInvoice/RecurringInvoiceRestoredActivity.php b/app/Listeners/RecurringInvoice/RecurringInvoiceRestoredActivity.php new file mode 100644 index 0000000000..8e95cc49f1 --- /dev/null +++ b/app/Listeners/RecurringInvoice/RecurringInvoiceRestoredActivity.php @@ -0,0 +1,54 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + MultiDB::setDb($event->company->db); + + $fields = new stdClass; + + $fields->recurring_invoice_id = $event->recurring_invoice->id; + $fields->client_id = $event->recurring_invoice->client_id; + $fields->user_id = $event->recurring_invoice->user_id; + $fields->company_id = $event->recurring_invoice->company_id; + $fields->activity_type_id = Activity::RESTORE_RECURRING_INVOICE; + + $this->activity_repo->save($fields, $event->recurring_invoice, $event->event_vars); + } +} diff --git a/app/Listeners/RecurringInvoice/UpdateRecurringInvoiceActivity.php b/app/Listeners/RecurringInvoice/UpdateRecurringInvoiceActivity.php new file mode 100644 index 0000000000..86fb596e53 --- /dev/null +++ b/app/Listeners/RecurringInvoice/UpdateRecurringInvoiceActivity.php @@ -0,0 +1,54 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + MultiDB::setDB($event->company->db); + + $fields = new stdClass; + + $fields->client_id = $event->recurring_invoice->client_id; + $fields->user_id = $event->recurring_invoice->user_id; + $fields->company_id = $event->recurring_invoice->company_id; + $fields->activity_type_id = Activity::UPDATE_RECURRING_INVOICE; + $fields->recurring_invoice_id = $event->recurring_invoice->id; + + $this->activity_repo->save($fields, $event->recurring_invoice, $event->event_vars); + } +} diff --git a/app/Models/Activity.php b/app/Models/Activity.php index 9f42edac17..77bb733383 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -90,6 +90,12 @@ class Activity extends StaticModel const DELETE_SUBSCRIPTION = 83; const RESTORE_SUBSCRIPTION = 84; + const CREATE_RECURRING_INVOICE = 100; + const UPDATE_RECURRING_INVOICE = 101; + const ARCHIVE_RECURRING_INVOICE = 102; + const DELETE_RECURRING_INVOICE = 103; + const RESTORE_RECURRING_INVOICE = 104; + protected $casts = [ 'is_system' => 'boolean', 'updated_at' => 'timestamp', diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 3790ca91cf..44b9427e50 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -157,6 +157,16 @@ class RecurringInvoice extends BaseModel return $value; } + public function activities() + { + return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300); + } + + public function history() + { + return $this->hasManyThrough(Backup::class, Activity::class); + } + public function company() { return $this->belongsTo(Company::class); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a5f0e5c40d..d1d25d8106 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -68,6 +68,11 @@ use App\Events\Quote\QuoteWasEmailed; use App\Events\Quote\QuoteWasRestored; use App\Events\Quote\QuoteWasUpdated; use App\Events\Quote\QuoteWasViewed; +use App\Events\RecurringInvoice\RecurringInvoiceWasArchived; +use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; +use App\Events\RecurringInvoice\RecurringInvoiceWasDeleted; +use App\Events\RecurringInvoice\RecurringInvoiceWasRestored; +use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated; use App\Events\Subscription\SubscriptionWasArchived; use App\Events\Subscription\SubscriptionWasCreated; use App\Events\Subscription\SubscriptionWasDeleted; @@ -164,6 +169,11 @@ use App\Listeners\Quote\QuoteEmailedNotification; use App\Listeners\Quote\QuoteRestoredActivity; use App\Listeners\Quote\QuoteViewedActivity; use App\Listeners\Quote\ReachWorkflowSettings; +use App\Listeners\RecurringInvoice\CreateRecurringInvoiceActivity; +use App\Listeners\RecurringInvoice\RecurringInvoiceArchivedActivity; +use App\Listeners\RecurringInvoice\RecurringInvoiceDeletedActivity; +use App\Listeners\RecurringInvoice\RecurringInvoiceRestoredActivity; +use App\Listeners\RecurringInvoice\UpdateRecurringInvoiceActivity; use App\Listeners\SendVerificationNotification; use App\Listeners\User\ArchivedUserActivity; use App\Listeners\User\CreatedUserActivity; @@ -400,6 +410,21 @@ class EventServiceProvider extends ServiceProvider QuoteWasRestored::class => [ QuoteRestoredActivity::class, ], + RecurringInvoiceWasUpdated::class => [ + UpdateRecurringInvoiceActivity::class, + ], + RecurringInvoiceWasCreated::class => [ + CreateRecurringInvoiceActivity::class, + ], + RecurringInvoiceWasDeleted::class => [ + RecurringInvoiceDeletedActivity::class, + ], + RecurringInvoiceWasArchived::class => [ + RecurringInvoiceArchivedActivity::class, + ], + RecurringInvoiceWasRestored::class => [ + RecurringInvoiceRestoredActivity::class, + ], TaskWasCreated::class => [ CreatedTaskActivity::class, ], diff --git a/app/Repositories/ActivityRepository.php b/app/Repositories/ActivityRepository.php index d32c9de6a2..5ba4373e7f 100644 --- a/app/Repositories/ActivityRepository.php +++ b/app/Repositories/ActivityRepository.php @@ -80,7 +80,11 @@ class ActivityRepository extends BaseRepository $backup = new Backup(); - if (get_class($entity) == Invoice::class || get_class($entity) == Quote::class || get_class($entity) == Credit::class) { + if (get_class($entity) == Invoice::class + || get_class($entity) == Quote::class + || get_class($entity) == Credit::class + || get_class($entity) == RecurringInvoice::class + ) { $contact = $entity->client->primary_contact()->first(); $backup->html_backup = $this->generateHtml($entity); $backup->amount = $entity->amount; diff --git a/database/migrations/2021_05_10_041528_add_recurring_invoice_id_to_activities_table.php b/database/migrations/2021_05_10_041528_add_recurring_invoice_id_to_activities_table.php new file mode 100644 index 0000000000..4ecef8899d --- /dev/null +++ b/database/migrations/2021_05_10_041528_add_recurring_invoice_id_to_activities_table.php @@ -0,0 +1,30 @@ +unsignedInteger('recurring_invoice_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 4adf5fa1d7..909be7c6fd 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4240,6 +4240,11 @@ $LANG = array( 'migration_already_completed' => 'Company already migrated', 'migration_already_completed_desc' => 'Looks like you already migrated :company_name to the V5 version of the Invoice Ninja. In case you want to start over, you can force migrate to wipe existing data.', + 'activity_100' => ':user created recurring invoice :recurring_invoice', + 'activity_101' => ':user updated recurring invoice :recurring_invoice', + 'activity_102' => ':user archived recurring invoice :recurring_invoice', + 'activity_103' => ':user deleted recurring invoice :recurring_invoice', + 'activity_104' => ':user restored recurring invoice :recurring_invoice', ); return $LANG; diff --git a/tests/Integration/EventTest.php b/tests/Integration/EventTest.php index 740b4f5593..a9ab59282f 100644 --- a/tests/Integration/EventTest.php +++ b/tests/Integration/EventTest.php @@ -41,6 +41,11 @@ use App\Events\Quote\QuoteWasCreated; use App\Events\Quote\QuoteWasDeleted; use App\Events\Quote\QuoteWasRestored; use App\Events\Quote\QuoteWasUpdated; +use App\Events\RecurringInvoice\RecurringInvoiceWasArchived; +use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; +use App\Events\RecurringInvoice\RecurringInvoiceWasDeleted; +use App\Events\RecurringInvoice\RecurringInvoiceWasRestored; +use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated; use App\Events\Subscription\SubscriptionWasArchived; use App\Events\Subscription\SubscriptionWasCreated; use App\Events\Subscription\SubscriptionWasDeleted; @@ -68,6 +73,7 @@ use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Routing\Middleware\ThrottleRequests; +use Illuminate\Validation\ValidationException; use Tests\MockAccountData; use Tests\TestCase; @@ -538,6 +544,76 @@ class EventTest extends TestCase } + + public function testRecurringInvoiceEvents() + { + /* Test fire new invoice */ + $data = [ + 'client_id' => $this->client->hashed_id, + 'number' => 'dudex', + 'frequency_id' => 1, + ]; + + $this->expectsEvents([ + RecurringInvoiceWasCreated::class, + RecurringInvoiceWasUpdated::class, + RecurringInvoiceWasArchived::class, + RecurringInvoiceWasRestored::class, + RecurringInvoiceWasDeleted::class, + ]); + + try { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/recurring_invoices/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + } + + $response->assertStatus(200); + + + $arr = $response->json(); + + $data = [ + 'client_id' => $this->client->hashed_id, + 'number' => 'dude2', + 'frequency_id' => 1, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/recurring_invoices/' . $arr['data']['id'], $data) + ->assertStatus(200); + + + $data = [ + 'ids' => [$arr['data']['id']], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/recurring_invoices/bulk?action=archive', $data) + ->assertStatus(200); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/recurring_invoices/bulk?action=restore', $data) + ->assertStatus(200); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/recurring_invoices/bulk?action=delete', $data) + ->assertStatus(200); + } + + + public function testClientEvents() { $this->expectsEvents([