Merge pull request #9731 from turbo124/v5-develop

Adjustments for quote partial due dates
This commit is contained in:
David Bomba 2024-07-07 07:38:01 +10:00 committed by GitHub
commit 67f1166ccb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 183 additions and 120 deletions

View File

@ -62,10 +62,10 @@ class SendRemindersCron extends Command
public function handle()
{
Invoice::where('next_send_date', '<=', now()->toDateTimeString())
->whereNull('deleted_at')
->where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereNull('invoices.deleted_at')
->where('invoices.is_deleted', 0)
->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('invoices.balance', '>', 0)
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
@ -73,6 +73,7 @@ class SendRemindersCron extends Command
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('invitations')->cursor()->each(function ($invoice) {
if ($invoice->isPayable()) {
$reminder_template = $invoice->calculateTemplate('invoice');

View File

@ -320,7 +320,7 @@ class InvoiceFilters extends QueryFilters
{
$sort_col = explode('|', $sort);
if (!is_array($sort_col) || count($sort_col) != 2) {
if (!is_array($sort_col) || count($sort_col) != 2 || in_array($sort_col[0], ['documents'])) {
return $this->builder;
}

View File

@ -107,16 +107,16 @@ class InvoiceSumInclusive
private function calculateCustomValues()
{
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge1, $this->invoice->custom_surcharge_tax1);
// $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge1, $this->invoice->custom_surcharge_tax1);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge1);
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge2, $this->invoice->custom_surcharge_tax2);
// $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge2, $this->invoice->custom_surcharge_tax2);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge2);
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge3, $this->invoice->custom_surcharge_tax3);
// $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge3, $this->invoice->custom_surcharge_tax3);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge3);
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4);
// $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4);
$this->total += $this->total_custom_values;
@ -137,21 +137,21 @@ class InvoiceSumInclusive
}
//Handles cases where the surcharge is not taxed
// if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && !$this->invoice->custom_surcharge_tax1) {
// $amount += $this->invoice->custom_surcharge1;
// }
if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && $this->invoice->custom_surcharge_tax1) {
$amount += $this->invoice->custom_surcharge1;
}
// if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && !$this->invoice->custom_surcharge_tax2) {
// $amount += $this->invoice->custom_surcharge2;
// }
if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && $this->invoice->custom_surcharge_tax2) {
$amount += $this->invoice->custom_surcharge2;
}
// if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && !$this->invoice->custom_surcharge_tax3) {
// $amount += $this->invoice->custom_surcharge3;
// }
if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && $this->invoice->custom_surcharge_tax3) {
$amount += $this->invoice->custom_surcharge3;
}
// if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && !$this->invoice->custom_surcharge_tax4) {
// $amount += $this->invoice->custom_surcharge4;
// }
if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && $this->invoice->custom_surcharge_tax4) {
$amount += $this->invoice->custom_surcharge4;
}
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
$tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount);

View File

@ -81,14 +81,20 @@ class SearchController extends Controller
$invoices = Invoice::query()
->company()
->with('client')
->where('is_deleted', 0)
->whereHas('client', function ($q) {
$q->where('is_deleted', 0);
})
->where('invoices.is_deleted', 0)
// ->whereHas('client', function ($q) {
// $q->where('is_deleted', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0);
})
->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) {
$query->where('user_id', $user->id);
$query->where('invoices.user_id', $user->id);
})
->orderBy('id', 'desc')
->orderBy('invoices.id', 'desc')
->take(3000)
->get();

View File

@ -78,7 +78,9 @@ class StoreInvoiceRequest extends Request
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date'];
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date ?? '') > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
// $rules['amount'] = ['sometimes', 'bail', 'max:99999999999999'];

View File

@ -82,8 +82,8 @@ class UpdateInvoiceRequest extends Request
$rules['date'] = 'bail|sometimes|date:Y-m-d';
// $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
// $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
return $rules;
}

View File

@ -66,8 +66,8 @@ class StoreQuoteRequest extends Request
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['line_items'] = 'array';
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date ?? '') > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;

View File

@ -65,7 +65,7 @@ class UpdateQuoteRequest extends Request
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];

View File

@ -48,12 +48,12 @@ class RecurringInvoicesCron
Auth::logout();
if (! config('ninja.db.multi_db_enabled')) {
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->where('is_deleted', false)
->where('remaining_cycles', '!=', '0')
->whereNotNull('next_send_date')
->whereNull('deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString())
$recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
->where('recurring_invoices.is_deleted', false)
->where('recurring_invoices.remaining_cycles', '!=', '0')
->whereNotNull('recurring_invoices.next_send_date')
->whereNull('recurring_invoices.deleted_at')
->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
@ -87,18 +87,27 @@ class RecurringInvoicesCron
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->where('is_deleted', false)
->where('remaining_cycles', '!=', '0')
->whereNull('deleted_at')
->whereNotNull('next_send_date')
->where('next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
$recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
->where('recurring_invoices.is_deleted', false)
->where('recurring_invoices.remaining_cycles', '!=', '0')
->whereNull('recurring_invoices.deleted_at')
->whereNotNull('recurring_invoices.next_send_date')
->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
// ->whereHas('client', function ($query) {
// $query->where('is_deleted', 0)
// ->where('deleted_at', null);
// })
// ->whereHas('company', function ($query) {
// $query->where('is_disabled', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('recurring_invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0)
->whereNull('clients.deleted_at');
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
->leftJoin('companies', function ($join) {
$join->on('recurring_invoices.company_id', '=', 'companies.id')
->where('companies.is_disabled', 0);
})
->with('company')
->cursor();

View File

@ -51,20 +51,29 @@ class InvoiceCheckLateWebhook implements ShouldQueue
->pluck('company_id');
Invoice::query()
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereIn('company_id', $company_ids)
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->where('invoices.is_deleted', false)
->whereNull('invoices.deleted_at')
->whereNotNull('invoices.due_date')
->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('invoices.balance', '>', 0)
->whereIn('invoices.company_id', $company_ids)
// ->whereHas('client', function ($query) {
// $query->where('is_deleted', 0)
// ->where('deleted_at', null);
// })
// ->whereHas('company', function ($query) {
// $query->where('is_disabled', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0)
->whereNull('clients.deleted_at');
})
->leftJoin('companies', function ($join) {
$join->on('invoices.company_id', '=', 'companies.id')
->where('companies.is_disabled', 0);
})
->whereBetween('invoices.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($invoice) {
(new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();
@ -78,20 +87,29 @@ class InvoiceCheckLateWebhook implements ShouldQueue
->pluck('company_id');
Invoice::query()
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereIn('company_id', $company_ids)
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->where('invoices.is_deleted', false)
->whereNull('invoices.deleted_at')
->whereNotNull('invoices.due_date')
->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('invoices.balance', '>', 0)
->whereIn('invoices.company_id', $company_ids)
// ->whereHas('client', function ($query) {
// $query->where('is_deleted', 0)
// ->where('deleted_at', null);
// })
// ->whereHas('company', function ($query) {
// $query->where('is_disabled', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0)
->whereNull('clients.deleted_at');
})
->leftJoin('companies', function ($join) {
$join->on('invoices.company_id', '=', 'companies.id')
->where('companies.is_disabled', 0);
})
->whereBetween('invoices.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($invoice) {
(new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();

View File

@ -49,10 +49,10 @@ class QuoteCheckExpired implements ShouldQueue
{
if (! config('ninja.db.multi_db_enabled')) {
Quote::query()
->where('status_id', Quote::STATUS_SENT)
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->where('quotes.status_id', Quote::STATUS_SENT)
->where('quotes.is_deleted', false)
->whereNull('quotes.deleted_at')
->whereNotNull('quotes.due_date')
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
@ -60,8 +60,8 @@ class QuoteCheckExpired implements ShouldQueue
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->whereBetween('quotes.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($quote) {
$this->queueExpiredQuoteNotification($quote);
@ -71,10 +71,10 @@ class QuoteCheckExpired implements ShouldQueue
MultiDB::setDB($db);
Quote::query()
->where('status_id', Quote::STATUS_SENT)
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->where('quotes.status_id', Quote::STATUS_SENT)
->where('quotes.is_deleted', false)
->whereNull('quotes.deleted_at')
->whereNotNull('quotes.due_date')
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
@ -82,8 +82,8 @@ class QuoteCheckExpired implements ShouldQueue
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->whereBetween('quotes.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($quote) {
$this->queueExpiredQuoteNotification($quote);

View File

@ -61,10 +61,10 @@ class QuoteReminderJob implements ShouldQueue
nrlog("Sending quote reminders on ".now()->format('Y-m-d h:i:s'));
Quote::query()
->where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT])
->whereNull('deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString())
->where('quotes.is_deleted', 0)
->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
->whereNull('quotes.deleted_at')
->where('quotes.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
@ -88,10 +88,10 @@ class QuoteReminderJob implements ShouldQueue
nrlog("Sending quote reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
Quote::query()
->where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT])
->whereNull('deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString())
->where('quotes.is_deleted', 0)
->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
->whereNull('quotes.deleted_at')
->where('quotes.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
@ -99,6 +99,7 @@ class QuoteReminderJob implements ShouldQueue
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('invitations')->chunk(50, function ($quotes) {
foreach ($quotes as $quote) {

View File

@ -47,11 +47,10 @@ class ExpenseRepository extends BaseRepository
$user = auth()->user();
$payment_date = &$data['payment_date'];
$vendor_id = &$data['vendor_id'];
if($payment_date && $payment_date == $expense->payment_date) {
//do nothing
} elseif($payment_date && strlen($payment_date) > 1 && $user->company()->notify_vendor_when_paid && ($vendor_id || $expense->vendor_id)) {
} elseif($payment_date && strlen($payment_date) > 1 && $user->company()->notify_vendor_when_paid && (isset($data['vendor_id']) || $expense->vendor_id)) {
$this->notify_vendor = true;
}

View File

@ -90,15 +90,15 @@ class ARDetailReport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Invoice::query()
->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->withTrashed()
->whereHas('client', function ($query) {
$query->where('is_deleted', 0);
})
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->where('balance', '>', 0)
->orderBy('due_date', 'ASC')
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
->where('invoices.company_id', $this->company->id)
->where('invoices.is_deleted', 0)
->where('invoices.balance', '>', 0)
->orderBy('invoices.due_date', 'ASC');
$query = $this->addDateRange($query, 'invoices');

View File

@ -39,7 +39,7 @@ trait PdfMaker
$pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
}
$html = str_replace(['file:/', 'iframe', '&lt;object', '<object', '127.0.0.1', 'localhost'], ['','','','','',''], $html);
$html = str_ireplace(['file:/', 'iframe', '<embed', '&lt;embed', '&lt;object', '<object', '127.0.0.1', 'localhost'], '', $html);
$generated = $pdf
->setHtml($html)

View File

@ -101,18 +101,7 @@
</dl>
</div>
@if($invoice->subscription && $invoice->subscription?->allow_cancellation)
{{-- INV2-591 --}}
{{-- @if(false) --}}
@if($invoice->subscription && $invoice->subscription?->allow_cancellation && $invoice->status_id == 2)
<div class="bg-white shadow sm:rounded-lg mt-4">
<div class="px-4 py-5 sm:p-6">
<div class="sm:flex sm:items-start sm:justify-between">

View File

@ -73,7 +73,7 @@
</div>
@endif
@if($invoice->subscription && $invoice->subscription?->allow_cancellation)
@if($invoice->subscription && $invoice->subscription?->allow_cancellation && $invoice->status_id == 2)
<div class="bg-white shadow sm:rounded-lg mt-4">
<div class="px-4 py-5 sm:p-6">
<div class="sm:flex sm:items-start sm:justify-between">
@ -92,7 +92,7 @@
</div>
@endif
@if($invoice->subscription && $invoice->subscription->allow_plan_changes)
@if($invoice->subscription && $invoice->subscription->allow_plan_changes && count($invoice->subscription->service()->getPlans()) > 0)
<div class="bg-white shadow overflow-hidden px-4 py-5 lg:rounded-lg mt-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">{{ ctrans('texts.change_plan') }}</h3>
<p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">{{ ctrans('texts.change_plan_description') }}</p>

View File

@ -47,6 +47,44 @@ class ExpenseApiTest extends TestCase
Model::reguard();
}
public function testExpensePutWithVendorStatus()
{
$data =
[
'vendor_id' => $this->vendor->hashed_id,
'amount' => 10,
'date' => '2021-10-01',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/expenses', $data);
$arr = $response->json();
$response->assertStatus(200);
$this->assertEquals($this->vendor->hashed_id, $arr['data']['vendor_id']);
$data = [
'payment_date' => now()->format('Y-m-d')
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/expenses/'.$arr['data']['id'], $data);
$arr = $response->json();
$response->assertStatus(200);
$this->assertEquals($this->vendor->hashed_id, $arr['data']['vendor_id']);
}
public function testTransactionIdClearedOnDelete()
{
$bi = BankIntegration::factory()->create([