1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-14 07:02:34 +01:00

Merge pull request #6944 from turbo124/v5-develop

Fixes for User Add rules in hosted
This commit is contained in:
David Bomba 2021-11-09 21:15:35 +11:00 committed by GitHub
commit c5516fcb87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 235 additions and 88 deletions

View File

@ -51,9 +51,9 @@ class RecurringExpenseToExpenseFactory
$expense->payment_type_id = $recurring_expense->payment_type_id; $expense->payment_type_id = $recurring_expense->payment_type_id;
$expense->project_id = $recurring_expense->project_id; $expense->project_id = $recurring_expense->project_id;
$expense->invoice_documents = $recurring_expense->invoice_documents; $expense->invoice_documents = $recurring_expense->invoice_documents;
$expense->tax_amount1 = $recurring_expense->tax_amount1; $expense->tax_amount1 = $recurring_expense->tax_amount1 ?: 0;
$expense->tax_amount2 = $recurring_expense->tax_amount2; $expense->tax_amount2 = $recurring_expense->tax_amount2 ?: 0;
$expense->tax_amount3 = $recurring_expense->tax_amount3; $expense->tax_amount3 = $recurring_expense->tax_amount3 ?: 0;
$expense->uses_inclusive_taxes = $recurring_expense->uses_inclusive_taxes; $expense->uses_inclusive_taxes = $recurring_expense->uses_inclusive_taxes;
$expense->calculate_tax_by_amount = $recurring_expense->calculate_tax_by_amount; $expense->calculate_tax_by_amount = $recurring_expense->calculate_tax_by_amount;

View File

@ -80,7 +80,10 @@ class InvitationController extends Controller
$query->where('is_deleted',0); $query->where('is_deleted',0);
}) })
->with('contact.client') ->with('contact.client')
->firstOrFail(); ->first();
if(!$invitation)
return abort(404,'The resource is no longer available.');
/* Return early if we have the correct client_hash embedded */ /* Return early if we have the correct client_hash embedded */
$client_contact = $invitation->contact; $client_contact = $invitation->contact;

View File

@ -13,8 +13,12 @@ namespace App\Http\Controllers;
use App\Jobs\Account\CreateAccount; use App\Jobs\Account\CreateAccount;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyToken; use App\Models\CompanyToken;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
class HostedMigrationController extends Controller class HostedMigrationController extends Controller
{ {
@ -49,4 +53,39 @@ class HostedMigrationController extends Controller
} }
public function confirmForwarding(Request $request)
{
if($request->header('X-API-HOSTED-SECRET') != config('ninja.ninja_hosted_secret'))
return;
$input = $request->all();
MultiDB::findAndSetDbByCompanyKey($input['account_key']);
$company = Company::with('account')->where('company_key', $input['account_key'])->first();
$account = $company->account;
$client_id = false;
if($contact = ClientContact::on('db-ninja-01')->where(['email' => $input['email'], 'company_id' => config('ninja.ninja_default_company_id')])->first()){
$client_id = $contact->client_id;
}
else if($client = Client::on('db-ninja-01')->where(['custom_value2' => $account->key, 'company_id' => config('ninja.ninja_default_company_id')])->first()){
$client_id = $client->id;
}
//get ninja client_id;
if(strlen($input['gateway_reference']) >1 && $client_id){
Artisan::call('ninja:add-token', [
'--customer' => $input['gateway_reference'], '--client_id' => 1
]);
}
$forward_url = $company->domain();
return response()->json(['forward_url' => $forward_url], 200);
}
} }

View File

@ -522,6 +522,9 @@ class InvoiceController extends BaseController
$ids = request()->input('ids'); $ids = request()->input('ids');
nlog($action);
nlog($ids);
$invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); $invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $invoices) { if (! $invoices) {
@ -532,13 +535,14 @@ class InvoiceController extends BaseController
* Download Invoice/s * Download Invoice/s
*/ */
if ($action == 'download' && $invoices->count() > 1) { if ($action == 'bulk_download' && $invoices->count() > 1) {
$invoices->each(function ($invoice) { $invoices->each(function ($invoice) {
if (auth()->user()->cannot('view', $invoice)) { if (auth()->user()->cannot('view', $invoice)) {
nlog("access denied");
return response()->json(['message' => ctrans('text.access_denied')]); return response()->json(['message' => ctrans('text.access_denied')]);
} }
}); });
nlog("bulky");
ZipInvoices::dispatch($invoices, $invoices->first()->company, auth()->user()); ZipInvoices::dispatch($invoices, $invoices->first()->company, auth()->user());
return response()->json(['message' => ctrans('texts.sent_message')], 200); return response()->json(['message' => ctrans('texts.sent_message')], 200);
@ -713,13 +717,13 @@ class InvoiceController extends BaseController
$this->itemResponse($invoice); $this->itemResponse($invoice);
} }
break; break;
case 'reverse': // case 'reverse':
$invoice = $invoice->service()->handleReversal()->deletePdf()->save(); // $invoice = $invoice->service()->handleReversal()->deletePdf()->save();
if (! $bulk) { // if (! $bulk) {
$this->itemResponse($invoice); // $this->itemResponse($invoice);
} // }
break; // break;
case 'email': case 'email':
//check query parameter for email_type and set the template else use calculateTemplate //check query parameter for email_type and set the template else use calculateTemplate

View File

@ -217,8 +217,6 @@ class PreviewController extends BaseController
if(!$request->has('entity_id')) if(!$request->has('entity_id'))
$entity_obj->service()->fillDefaults()->save(); $entity_obj->service()->fillDefaults()->save();
// $entity_obj->load('client.contacts','client.company');
App::forgetInstance('translator'); App::forgetInstance('translator');
$t = app('translator'); $t = app('translator');
App::setLocale($entity_obj->client->locale()); App::setLocale($entity_obj->client->locale());

View File

@ -31,6 +31,7 @@ class SubdomainController extends BaseController
'cname', 'cname',
'sandbox', 'sandbox',
'stage', 'stage',
'html',
]; ];
public function __construct() public function __construct()

View File

@ -26,6 +26,8 @@ use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\DataMapper\ClientSettings;
use Livewire\Component; use Livewire\Component;
class BillingPortalPurchase extends Component class BillingPortalPurchase extends Component
@ -241,7 +243,8 @@ class BillingPortalPurchase extends Component
'contacts' => [ 'contacts' => [
['email' => $this->email], ['email' => $this->email],
], ],
'settings' => [], 'client_hash' => Str::random(40),
'settings' => ClientSettings::defaults(),
]; ];
foreach ($this->request_data as $field => $value) { foreach ($this->request_data as $field => $value) {

View File

@ -74,7 +74,7 @@ class SetInviteDb
if (request()->json) { if (request()->json) {
return response()->json($error, 403); return response()->json($error, 403);
} else { } else {
abort(404,'I could not find the database for this object.'); abort(404,'I could not find this resource.');
} }
} }

View File

@ -33,25 +33,16 @@ class CanAddUserRule implements Rule
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
// $count = CompanyUser::query() $count = CompanyUser::query()
// ->with('user') ->where('company_user.account_id', auth()->user()->account_id)
// ->where('account_id', auth()->user()->account_id) ->join('users', 'users.id', '=', 'company_user.user_id')
// ->distinct() ->whereNull('users.deleted_at')
// ->select('user_id') ->whereNull('company_user.deleted_at')
// ->count();
$count = User::query()
->with(['company_user' => function ($query){
return $query->whereNull('company_user.deleted_at');
}])
->where('account_id', auth()->user()->account_id)
->distinct() ->distinct()
->select('users.id') ->count('company_user.user_id');
->count();
return $count < auth()->user()->company()->account->num_users; return $count < auth()->user()->company()->account->num_users;
//return auth()->user()->company()->account->users->count() < auth()->user()->company()->account->num_users;
} }
/** /**

View File

@ -33,17 +33,16 @@ class CanRestoreUserRule implements Rule
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
$count = User::query() $count = CompanyUser::query()
->with(['company_user' => function ($query){ ->where('company_user.account_id', auth()->user()->account_id)
return $query->whereNull('company_user.deleted_at'); ->join('users', 'users.id', '=', 'company_user.user_id')
}]) ->whereNull('users.deleted_at')
->where('account_id', 1) ->whereNull('company_user.deleted_at')
->distinct() ->distinct()
->select('users.id') ->count('company_user.user_id');
->count();
return $count < auth()->user()->company()->account->num_users; return $count < auth()->user()->company()->account->num_users;
//return auth()->user()->company()->account->users->count() < auth()->user()->company()->account->num_users;
} }
/** /**

View File

@ -24,6 +24,8 @@ class ValidProjectForClient implements Rule
public $input; public $input;
public $message;
public function __construct($input) public function __construct($input)
{ {
$this->input = $input; $this->input = $input;
@ -35,15 +37,20 @@ class ValidProjectForClient implements Rule
*/ */
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
$this->message = ctrans('texts.project_client_do_not_match');
if (empty($this->input['project_id'])) { if (empty($this->input['project_id'])) {
return true; return true;
} }
if (is_string($this->input['project_id'])) { // if (is_string($this->input['project_id'])) {
$this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']); // $this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']);
} // }
$project = Project::findOrFail($this->input['project_id']); $project = Project::find($this->input['project_id']);
if(!$project)
$this->message = "Project not found";
return $project->client_id == $this->input['client_id']; return $project->client_id == $this->input['client_id'];
} }
@ -53,6 +60,6 @@ class ValidProjectForClient implements Rule
*/ */
public function message() public function message()
{ {
return ctrans('texts.project_client_do_not_match'); return $this->message;
} }
} }

View File

@ -338,14 +338,14 @@ class CompanyImport implements ShouldQueue
if($this->account->plan == 'enterprise'){ if($this->account->plan == 'enterprise'){
$total_import_users = count($backup_users_emails); // $total_import_users = count($backup_users_emails);
$account_plan_num_user = $this->account->num_users; // $account_plan_num_user = $this->account->num_users;
if($total_import_users > $account_plan_num_user){ // if($total_import_users > $account_plan_num_user){
$this->message = "Total user count ({$total_import_users}) greater than your plan allows ({$account_plan_num_user})"; // $this->message = "Total user count ({$total_import_users}) greater than your plan allows ({$account_plan_num_user})";
$this->pre_flight_checks_pass = false; // $this->pre_flight_checks_pass = false;
} // }
} }
} }

View File

@ -134,10 +134,6 @@ class CSVImport implements ShouldQueue {
'company' => $this->company, 'company' => $this->company,
]; ];
App::forgetInstance('translator');
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new ImportCompleted($this->company, $data); $nmo->mailable = new ImportCompleted($this->company, $data);
$nmo->company = $this->company; $nmo->company = $this->company;

View File

@ -70,7 +70,7 @@ class ZipInvoices implements ShouldQueue
*/ */
public function handle() public function handle()
{ {nlog("bulky");
# create new zip object # create new zip object
$zip = new ZipArchive(); $zip = new ZipArchive();

View File

@ -120,6 +120,7 @@ class SendRecurring implements ShouldQueue
*/ */
event('eloquent.created: App\Models\Invoice', $invoice);
//Admin notification for recurring invoice sent. //Admin notification for recurring invoice sent.
if ($invoice->invitations->count() >= 1 ) { if ($invoice->invitations->count() >= 1 ) {

View File

@ -127,8 +127,8 @@ class WebhookHandler implements ShouldQueue
$this->company $this->company
); );
// if ($response->getStatusCode() == 410 || $response->getStatusCode() == 200) // if ($response->getStatusCode() == 410)
// return true;// $subscription->delete(); // return true; $subscription->delete();
} }
catch(\Exception $e){ catch(\Exception $e){

View File

@ -56,6 +56,10 @@ class ClientPaymentFailureObject
public function build() public function build()
{ {
if(!$this->payment_hash){
nlog("no payment has for failure notification - ClientPaymentFailureObject");
return;
}
App::forgetInstance('translator'); App::forgetInstance('translator');
/* Init a new copy of the translator*/ /* Init a new copy of the translator*/

View File

@ -13,9 +13,11 @@
namespace App\Mail\Import; namespace App\Mail\Import;
use App\Models\Company; use App\Models\Company;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;
class ImportCompleted extends Mailable class ImportCompleted extends Mailable
{ {
@ -45,6 +47,11 @@ class ImportCompleted extends Mailable
*/ */
public function build() public function build()
{ {
App::forgetInstance('translator');
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$data = array_merge($this->data, [ $data = array_merge($this->data, [
'logo' => $this->company->present()->logo(), 'logo' => $this->company->present()->logo(),
'settings' => $this->company->settings, 'settings' => $this->company->settings,

View File

@ -65,9 +65,10 @@ class SupportMessageSent extends Mailable
$is_large = $company->is_large ? "L" : "S"; $is_large = $company->is_large ? "L" : "S";
$platform = array_key_exists('platform', $this->data) ? $this->data['platform'] : "U"; $platform = array_key_exists('platform', $this->data) ? $this->data['platform'] : "U";
$migrated = strlen($company->company_key) == 32 ? "M" : ""; $migrated = strlen($company->company_key) == 32 ? "M" : "";
$trial = $account->isTrial() ? "T" : "";
if(Ninja::isHosted()) if(Ninja::isHosted())
$subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated} :: {$plan} :: ".date('M jS, g:ia'); $subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated}{$trial} :: {$plan} :: ".date('M jS, g:ia');
else else
$subject = "{$priority}Self Hosted :: {$plan} :: {$is_large}{$platform}{$migrated} :: ".date('M jS, g:ia'); $subject = "{$priority}Self Hosted :: {$plan} :: {$is_large}{$platform}{$migrated} :: ".date('M jS, g:ia');

View File

@ -88,7 +88,7 @@ class CompanyUser extends Pivot
public function users() public function users()
{ {
return $this->hasMany(User::class); return $this->hasMany(User::class)->withTrashed();
} }
/*todo monitor this function - may fail under certain conditions*/ /*todo monitor this function - may fail under certain conditions*/

View File

@ -71,7 +71,7 @@ class BaseDriver extends AbstractPaymentDriver
public $payment_method; public $payment_method;
/* PaymentHash */ /* PaymentHash */
public ?PaymentHash $payment_hash; public $payment_hash;
/* Array of payment methods */ /* Array of payment methods */
public static $methods = []; public static $methods = [];

View File

@ -304,7 +304,7 @@ class MolliePaymentDriver extends BaseDriver
try { try {
$payment = $this->gateway->payments->get($request->id); $payment = $this->gateway->payments->get($request->id);
$record = Payment::where('transaction_reference', $payment->id)->firstOrFail(); $record = Payment::withTrashed()->where('transaction_reference', $request->id)->firstOrFail();
$record->status_id = $codes[$payment->status]; $record->status_id = $codes[$payment->status];
$record->save(); $record->save();

View File

@ -23,7 +23,7 @@ class CreateInvitations extends AbstractService
{ {
use MakesHash; use MakesHash;
private $credit; public $credit;
public function __construct(Credit $credit) public function __construct(Credit $credit)
{ {
@ -45,6 +45,7 @@ class CreateInvitations extends AbstractService
$invitation = CreditInvitation::whereCompanyId($this->credit->company_id) $invitation = CreditInvitation::whereCompanyId($this->credit->company_id)
->whereClientContactId($contact->id) ->whereClientContactId($contact->id)
->whereCreditId($this->credit->id) ->whereCreditId($this->credit->id)
->withTrashed()
->first(); ->first();
if (! $invitation) { if (! $invitation) {
@ -58,6 +59,34 @@ class CreateInvitations extends AbstractService
} }
}); });
if($this->credit->invitations()->count() == 0) {
if($contacts->count() == 0){
$contact = $this->createBlankContact();
}
else{
$contact = $contacts->first();
$invitation = CreditInvitation::where('company_id', $this->credit->company_id)
->where('client_contact_id', $contact->id)
->where('credit_id', $this->credit->id)
->withTrashed()
->first();
if($invitation){
$invitation->restore();
return $this->credit;
}
}
$ii = CreditInvitationFactory::create($this->credit->company_id, $this->credit->user_id);
$ii->key = $this->createDbHash($this->credit->company->db);
$ii->credit_id = $this->credit->id;
$ii->client_contact_id = $contact->id;
$ii->save();
}
return $this->credit; return $this->credit;
} }

View File

@ -13,13 +13,14 @@ namespace App\Services\Credit;
use App\Jobs\Util\UnlinkFile; use App\Jobs\Util\UnlinkFile;
use App\Models\Credit; use App\Models\Credit;
use App\Services\Credit\CreateInvitations;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
class CreditService class CreditService
{ {
use MakesHash; use MakesHash;
protected $credit; public $credit;
public function __construct($credit) public function __construct($credit)
{ {

View File

@ -62,7 +62,23 @@ class CreateInvitations extends AbstractService
if($this->invoice->invitations()->count() == 0) { if($this->invoice->invitations()->count() == 0) {
if($contacts->count() == 0){
$contact = $this->createBlankContact(); $contact = $this->createBlankContact();
}
else{
$contact = $contacts->first();
$invitation = InvoiceInvitation::where('company_id', $this->invoice->company_id)
->where('client_contact_id', $contact->id)
->where('invoice_id', $this->invoice->id)
->withTrashed()
->first();
if($invitation){
$invitation->restore();
return $this->invoice;
}
}
$ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id); $ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id);
$ii->key = $this->createDbHash($this->invoice->company->db); $ii->key = $this->createDbHash($this->invoice->company->db);

View File

@ -33,7 +33,7 @@ class InvoiceService
{ {
use MakesHash; use MakesHash;
private $invoice; public $invoice;
public function __construct($invoice) public function __construct($invoice)
{ {

View File

@ -59,6 +59,33 @@ class CreateInvitations
} }
}); });
if($this->quote->invitations()->count() == 0) {
if($contacts->count() == 0){
$contact = $this->createBlankContact();
}
else{
$contact = $contacts->first();
$invitation = QuoteInvitation::where('company_id', $this->quote->company_id)
->where('client_contact_id', $contact->id)
->where('quote_id', $this->quote->id)
->withTrashed()
->first();
if($invitation){
$invitation->restore();
return $this->quote;
}
}
$ii = QuoteInvitationFactory::create($this->quote->company_id, $this->quote->user_id);
$ii->key = $this->createDbHash($this->quote->company->db);
$ii->quote_id = $this->quote->id;
$ii->client_contact_id = $contact->id;
$ii->save();
}
return $this->quote->fresh(); return $this->quote->fresh();
} }

View File

@ -151,6 +151,7 @@ class HtmlEngine
if($this->entity->project) { if($this->entity->project) {
$data['$project.name'] = ['value' => $this->entity->project->name, 'label' => ctrans('texts.project_name')]; $data['$project.name'] = ['value' => $this->entity->project->name, 'label' => ctrans('texts.project_name')];
$data['$invoice.project'] = &$data['$project.name'];
} }
} }

View File

@ -16,6 +16,8 @@ use App\Models\Client;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -254,6 +256,9 @@ class TemplateEngine
'send_email' => true, 'send_email' => true,
]); ]);
if($this->entity == 'invoice')
{
$this->entity_obj = Invoice::factory()->create([ $this->entity_obj = Invoice::factory()->create([
'user_id' => auth()->user()->id, 'user_id' => auth()->user()->id,
'company_id' => auth()->user()->company()->id, 'company_id' => auth()->user()->company()->id,
@ -266,6 +271,23 @@ class TemplateEngine
'invoice_id' => $this->entity_obj->id, 'invoice_id' => $this->entity_obj->id,
'client_contact_id' => $contact->id, 'client_contact_id' => $contact->id,
]); ]);
}
if($this->entity == 'quote')
{
$this->entity_obj = Quote::factory()->create([
'user_id' => auth()->user()->id,
'company_id' => auth()->user()->company()->id,
'client_id' => $client->id,
]);
$invitation = QuoteInvitation::factory()->create([
'user_id' => auth()->user()->id,
'company_id' => auth()->user()->company()->id,
'quote_id' => $this->entity_obj->id,
'client_contact_id' => $contact->id,
]);
}
$this->entity_obj->setRelation('invitations', $invitation); $this->entity_obj->setRelation('invitations', $invitation);
$this->entity_obj->setRelation('client', $client); $this->entity_obj->setRelation('client', $client);

View File

@ -220,5 +220,6 @@ Route::post('api/v1/postmark_webhook', 'PostMarkController@webhook')->middleware
Route::get('token_hash_router', 'OneTimeTokenController@router'); Route::get('token_hash_router', 'OneTimeTokenController@router');
Route::get('webcron', 'WebCronController@index'); Route::get('webcron', 'WebCronController@index');
Route::post('api/v1/get_migration_account', 'HostedMigrationController@getAccount')->middleware('guest'); Route::post('api/v1/get_migration_account', 'HostedMigrationController@getAccount')->middleware('guest');
Route::post('api/v1/confirm_forwarding', 'HostedMigrationController@confirmForwarding')->middleware('guest');
Route::fallback('BaseController@notFound'); Route::fallback('BaseController@notFound');

View File

@ -33,18 +33,12 @@ use Tests\TestCase;
class InvitationTest extends TestCase class InvitationTest extends TestCase
{ {
use MakesHash; use MakesHash;
//use DatabaseTransactions; use DatabaseTransactions;
//use RefreshDatabase; // use RefreshDatabase;
public function setUp() :void public function setUp() :void
{ {
parent::setUp(); parent::setUp();
Session::start();
$this->faker = \Faker\Factory::create();
Model::reguard();
} }
public function testInvoiceCreationAfterInvoiceMarkedSent() public function testInvoiceCreationAfterInvoiceMarkedSent()

View File

@ -677,7 +677,6 @@ class EventTest extends TestCase
{ {
$this->withoutMiddleware(PasswordProtection::class); $this->withoutMiddleware(PasswordProtection::class);
$this->expectsEvents([ $this->expectsEvents([
UserWasCreated::class, UserWasCreated::class,
UserWasUpdated::class, UserWasUpdated::class,

View File

@ -170,6 +170,9 @@ trait MockAccountData
} }
$this->account = Account::factory()->create(); $this->account = Account::factory()->create();
$this->account->num_users = 3;
$this->account->save();
$this->company = Company::factory()->create([ $this->company = Company::factory()->create([
'account_id' => $this->account->id, 'account_id' => $this->account->id,
]); ]);