Merge pull request #8703 from turbo124/v5-develop

xau,xag currencies
This commit is contained in:
David Bomba 2023-08-09 08:59:57 +10:00 committed by GitHub
commit 78af5eab30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 289 additions and 181 deletions

View File

@ -185,6 +185,9 @@ class CheckData extends Command
if ($cu->company && $cu->user) { if ($cu->company && $cu->user) {
(new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle(); (new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
} }
else {
// $cu->forceDelete();
}
} }
}); });
} }

View File

@ -64,9 +64,9 @@ class BillingPortalPurchase extends Component
/** /**
* Instance of subscription. * Instance of subscription.
* *
* @var Subscription * @var \App\Models\Subscription $subscription
*/ */
public $subscription; public Subscription $subscription;
/** /**
* Instance of client contact. * Instance of client contact.
@ -155,7 +155,7 @@ class BillingPortalPurchase extends Component
public $request_data; public $request_data;
/** /**
* @var string * @var float
*/ */
public $price; public $price;

View File

@ -31,7 +31,10 @@ class StoreCreditRequest extends Request
*/ */
public function authorize() public function authorize()
{ {
return auth()->user()->can('create', Credit::class); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('create', Credit::class);
} }
/** /**
@ -55,10 +58,13 @@ class StoreCreditRequest extends Request
$rules['file'] = $this->file_validation; $rules['file'] = $this->file_validation;
} }
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id; /** @var \App\Models\User $user */
$user = auth()->user();
$rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
// $rules['number'] = new UniqueCreditNumberRule($this->all()); // $rules['number'] = new UniqueCreditNumberRule($this->all());
$rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', auth()->user()->company()->id)]; $rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', $user->company()->id)];
$rules['discount'] = 'sometimes|numeric'; $rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean']; $rules['is_amount_discount'] = ['boolean'];
$rules['tax_rate1'] = 'bail|sometimes|numeric'; $rules['tax_rate1'] = 'bail|sometimes|numeric';
@ -67,7 +73,7 @@ class StoreCreditRequest extends Request
$rules['tax_name1'] = 'bail|sometimes|string|nullable'; $rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
if ($this->invoice_id) { if ($this->invoice_id) {
$rules['invoice_id'] = new ValidInvoiceCreditRule(); $rules['invoice_id'] = new ValidInvoiceCreditRule();

View File

@ -30,7 +30,10 @@ class UpdateCreditRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->can('edit', $this->credit); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('edit', $this->credit);
} }
/** /**
@ -40,6 +43,9 @@ class UpdateCreditRequest extends Request
*/ */
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = []; $rules = [];
if ($this->file('documents') && is_array($this->file('documents'))) { if ($this->file('documents') && is_array($this->file('documents'))) {
@ -55,7 +61,7 @@ class UpdateCreditRequest extends Request
} }
if ($this->number) { if ($this->number) {
$rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id); $rules['number'] = Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id);
} }
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';
@ -67,7 +73,7 @@ class UpdateCreditRequest extends Request
$rules['tax_name1'] = 'bail|sometimes|string|nullable'; $rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }

View File

@ -72,7 +72,7 @@ class StoreInvoiceRequest extends Request
$rules['tax_name1'] = 'bail|sometimes|string|nullable'; $rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }

View File

@ -40,6 +40,9 @@ class UpdateInvoiceRequest extends Request
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = []; $rules = [];
if ($this->file('documents') && is_array($this->file('documents'))) { if ($this->file('documents') && is_array($this->file('documents'))) {
@ -57,7 +60,7 @@ class UpdateInvoiceRequest extends Request
$rules['id'] = new LockedInvoiceRule($this->invoice); $rules['id'] = new LockedInvoiceRule($this->invoice);
if ($this->number) { if ($this->number) {
$rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id); $rules['number'] = Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id);
} }
$rules['is_amount_discount'] = ['boolean']; $rules['is_amount_discount'] = ['boolean'];
@ -72,10 +75,7 @@ class UpdateInvoiceRequest extends Request
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['status_id'] = 'bail|sometimes|not_in:5'; //do not allow cancelled invoices to be modfified. $rules['status_id'] = 'bail|sometimes|not_in:5'; //do not allow cancelled invoices to be modfified.
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
// not needed.
// $rules['partial_due_date'] = 'bail|sometimes|required_unless:partial,0,null';
return $rules; return $rules;
} }

View File

@ -29,7 +29,10 @@ class StorePurchaseOrderRequest extends Request
*/ */
public function authorize() public function authorize()
{ {
return auth()->user()->can('create', PurchaseOrder::class); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('create', PurchaseOrder::class);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
@ -38,11 +41,14 @@ class StorePurchaseOrderRequest extends Request
*/ */
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = []; $rules = [];
$rules['vendor_id'] = 'bail|required|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['vendor_id'] = 'bail|required|exists:vendors,id,company_id,'.$user->company()->id.',is_deleted,0';
$rules['number'] = ['nullable', Rule::unique('purchase_orders')->where('company_id', auth()->user()->company()->id)]; $rules['number'] = ['nullable', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)];
$rules['discount'] = 'sometimes|numeric'; $rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean']; $rules['is_amount_discount'] = ['boolean'];
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';
@ -60,7 +66,7 @@ class StorePurchaseOrderRequest extends Request
} }
$rules['status_id'] = 'nullable|integer|in:1,2,3,4,5'; $rules['status_id'] = 'nullable|integer|in:1,2,3,4,5';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }

View File

@ -63,7 +63,7 @@ class UpdatePurchaseOrderRequest extends Request
} }
$rules['status_id'] = 'sometimes|integer|in:1,2,3,4,5'; $rules['status_id'] = 'sometimes|integer|in:1,2,3,4,5';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }

View File

@ -30,14 +30,20 @@ class StoreQuoteRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->can('create', Quote::class); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('create', Quote::class);
} }
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = []; $rules = [];
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id; $rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
if ($this->file('documents') && is_array($this->file('documents'))) { if ($this->file('documents') && is_array($this->file('documents'))) {
$rules['documents.*'] = $this->file_validation; $rules['documents.*'] = $this->file_validation;
@ -51,11 +57,11 @@ class StoreQuoteRequest extends Request
$rules['file'] = $this->file_validation; $rules['file'] = $this->file_validation;
} }
$rules['number'] = ['nullable', Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)]; $rules['number'] = ['nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)];
$rules['discount'] = 'sometimes|numeric'; $rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean']; $rules['is_amount_discount'] = ['boolean'];
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
// $rules['number'] = new UniqueQuoteNumberRule($this->all()); // $rules['number'] = new UniqueQuoteNumberRule($this->all());
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';

View File

@ -57,7 +57,7 @@ class UpdateQuoteRequest extends Request
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric'; $rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean']; $rules['is_amount_discount'] = ['boolean'];
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }

View File

@ -40,6 +40,9 @@ class StoreRecurringInvoiceRequest extends Request
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = []; $rules = [];
if ($this->file('documents') && is_array($this->file('documents'))) { if ($this->file('documents') && is_array($this->file('documents'))) {
@ -54,7 +57,7 @@ class StoreRecurringInvoiceRequest extends Request
$rules['file'] = $this->file_validation; $rules['file'] = $this->file_validation;
} }
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id; $rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
$rules['invitations.*.client_contact_id'] = 'distinct'; $rules['invitations.*.client_contact_id'] = 'distinct';
@ -71,7 +74,7 @@ class StoreRecurringInvoiceRequest extends Request
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['due_date_days'] = 'bail|sometimes|string'; $rules['due_date_days'] = 'bail|sometimes|string';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }

View File

@ -67,7 +67,7 @@ class UpdateRecurringInvoiceRequest extends Request
$rules['tax_name1'] = 'bail|sometimes|string|nullable'; $rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|gt:0'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
return $rules; return $rules;
} }
@ -132,7 +132,7 @@ class UpdateRecurringInvoiceRequest extends Request
unset($input['documents']); unset($input['documents']);
} }
if (array_key_exists('exchange_rate', $input) && (is_null($input['exchange_rate']) || $input['exchange_rate'] == 0)) { if (array_key_exists('exchange_rate', $input) && (is_null($input['exchange_rate']) || $input['exchange_rate'] == 0) || !isset($input['exchange_rate'])) {
$input['exchange_rate'] = 1; $input['exchange_rate'] = 1;
} }
@ -148,7 +148,7 @@ class UpdateRecurringInvoiceRequest extends Request
* *
* @return bool * @return bool
*/ */
private function setAutoBillFlag($auto_bill) private function setAutoBillFlag($auto_bill): bool
{ {
if ($auto_bill == 'always' || $auto_bill == 'optout') { if ($auto_bill == 'always' || $auto_bill == 'optout') {
return true; return true;

View File

@ -288,7 +288,7 @@ class MatchBankTransactions implements ShouldQueue
$this->available_balance = $amount; $this->available_balance = $amount;
\DB::connection(config('database.default'))->transaction(function () use ($invoices) { \DB::connection(config('database.default'))->transaction(function () use ($invoices) {
$invoices->each(function ($invoice) use ($invoices) { $invoices->each(function ($invoice) {
$this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first(); $this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first();
$_amount = false; $_amount = false;
@ -400,7 +400,7 @@ class MatchBankTransactions implements ShouldQueue
$category = $this->categories->firstWhere('highLevelCategoryId', $this->bt->category_id); $category = $this->categories->firstWhere('highLevelCategoryId', $this->bt->category_id);
$ec = ExpenseCategory::where('company_id', $this->bt->company_id)->where('bank_category_id', $this->bt->category_id)->first(); $ec = ExpenseCategory::query()->where('company_id', $this->bt->company_id)->where('bank_category_id', $this->bt->category_id)->first();
if ($ec) { if ($ec) {
return $ec->id; return $ec->id;

View File

@ -280,7 +280,7 @@ class CompanyImport implements ShouldQueue
'errors' => [] 'errors' => []
]; ];
$_company = Company::find($this->company->id); $_company = Company::query()->find($this->company->id);
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new ImportCompleted($_company, $data); $nmo->mailable = new ImportCompleted($_company, $data);

View File

@ -122,11 +122,11 @@ class CreateRawPdf implements ShouldQueue
$entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey($this->entity->client->getSetting($entity_design_id)); $entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey($this->entity->client->getSetting($entity_design_id));
$design = Design::withTrashed()->find($entity_design_id); $design = Design::query()->withTrashed()->find($entity_design_id);
/* Catch all in case migration doesn't pass back a valid design */ /* Catch all in case migration doesn't pass back a valid design */
if (! $design) { if (! $design) {
$design = Design::find(2); $design = Design::query()->find(2);
} }
$html = new HtmlEngine($this->invitation); $html = new HtmlEngine($this->invitation);

View File

@ -131,7 +131,7 @@ class AdjustProductInventory implements ShouldQueue
collect($this->old_invoice)->filter(function ($item) { collect($this->old_invoice)->filter(function ($item) {
return $item->type_id == '1'; return $item->type_id == '1';
})->each(function ($i) { })->each(function ($i) {
$p = Product::where('product_key', $i->product_key)->where('company_id', $this->company->id)->first(); $p = Product::query()->where('product_key', $i->product_key)->where('company_id', $this->company->id)->first();
if ($p) { if ($p) {
$p->in_stock_quantity += $i->quantity; $p->in_stock_quantity += $i->quantity;

View File

@ -12,37 +12,17 @@ use Illuminate\Foundation\Bus\Dispatchable;
class InjectSignature implements ShouldQueue class InjectSignature implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* @var \App\Models\Invoice | \App\Models\Quote | \App\Models\Credit | \App\Models\PurchaseOrder
*/
public $entity;
/**
* @var string
*/
public $signature;
public $contact_id;
public $ip;
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @param $entity * @param \App\Models\Invoice | \App\Models\Quote | \App\Models\Credit | \App\Models\PurchaseOrder $entity
* @param int $contact_id
* @param string $signature * @param string $signature
* @param string $ip
*/ */
public function __construct($entity, $contact_id, string $signature, ?string $ip) public function __construct(public \App\Models\Invoice | \App\Models\Quote | \App\Models\Credit | \App\Models\PurchaseOrder $entity, private int $contact_id, private string $signature, private ?string $ip)
{ {
$this->entity = $entity;
$this->contact_id = $contact_id;
$this->signature = $signature;
$this->ip = $ip;
} }
/** /**

View File

@ -43,16 +43,16 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
CompanyLedger::where('balance', 0)->where('client_id', $this->client->id)->orderBy('updated_at', 'ASC')->cursor()->each(function ($company_ledger) { CompanyLedger::query()->where('balance', 0)->where('client_id', $this->client->id)->orderBy('updated_at', 'ASC')->cursor()->each(function ($company_ledger) {
if ($company_ledger->balance == 0) { if ($company_ledger->balance == 0) {
$last_record = CompanyLedger::where('client_id', $company_ledger->client_id) $last_record = CompanyLedger::query()->where('client_id', $company_ledger->client_id)
->where('company_id', $company_ledger->company_id) ->where('company_id', $company_ledger->company_id)
->where('balance', '!=', 0) ->where('balance', '!=', 0)
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
->first(); ->first();
if (! $last_record) { if (! $last_record) {
$last_record = CompanyLedger::where('client_id', $company_ledger->client_id) $last_record = CompanyLedger::query()->where('client_id', $company_ledger->client_id)
->where('company_id', $company_ledger->company_id) ->where('company_id', $company_ledger->company_id)
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
->first(); ->first();

View File

@ -51,7 +51,7 @@ class NinjaMailerJob implements ShouldQueue
public $override; public $override;
/* @var \App\Models\Company $company*/ /** @var \App\Models\Company $company | null **/
public ?Company $company; public ?Company $company;
private $mailer; private $mailer;
@ -83,7 +83,7 @@ class NinjaMailerJob implements ShouldQueue
MultiDB::setDb($this->nmo->company->db); MultiDB::setDb($this->nmo->company->db);
/* Serializing models from other jobs wipes the primary key */ /* Serializing models from other jobs wipes the primary key */
$this->company = Company::where('company_key', $this->nmo->company->company_key)->first(); $this->company = Company::query()->where('company_key', $this->nmo->company->company_key)->first();
/* If any pre conditions fail, we return early here */ /* If any pre conditions fail, we return early here */
if (!$this->company || $this->preFlightChecksFail()) { if (!$this->company || $this->preFlightChecksFail()) {
@ -552,7 +552,7 @@ class NinjaMailerJob implements ShouldQueue
* Logs any errors to the SystemLog * Logs any errors to the SystemLog
* *
* @param string $errors * @param string $errors
* @param App\Models\User | App\Models\Client | null $recipient_object * @param \App\Models\User | \App\Models\Client | null $recipient_object
* @return void * @return void
*/ */
private function logMailError($errors, $recipient_object) :void private function logMailError($errors, $recipient_object) :void

View File

@ -84,7 +84,14 @@ class PaymentFailureMailer implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); unset($methods[$key]);
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->amount, null))->build(); $use_react_link = false;
if(isset($company_user->react_settings->react_notification_link) && $company_user->react_settings->react_notification_link) {
$use_react_link = true;
}
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->amount, null, $use_react_link))->build();
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer($mail_obj); $nmo->mailable = new NinjaMailer($mail_obj);

View File

@ -42,7 +42,7 @@ class UpdateRecurring implements ShouldQueue
$this->user->setCompany($this->company); $this->user->setCompany($this->company);
RecurringInvoice::where('company_id', $this->company->id) RecurringInvoice::query()->where('company_id', $this->company->id)
->whereIn('id', $this->ids) ->whereIn('id', $this->ids)
->chunk(100, function ($recurring_invoices) { ->chunk(100, function ($recurring_invoices) {
foreach ($recurring_invoices as $recurring_invoice) { foreach ($recurring_invoices as $recurring_invoice) {

View File

@ -1654,8 +1654,8 @@ class Import implements ShouldQueue
$modified['company_gateway_id'] = $this->transformId('company_gateways', $resource['company_gateway_id']); $modified['company_gateway_id'] = $this->transformId('company_gateways', $resource['company_gateway_id']);
//$modified['user_id'] = $this->processUserId($resource); //$modified['user_id'] = $this->processUserId($resource);
/** @var \App\Models\ClientGatewayToken $cgt **/
$cgt = ClientGatewayToken::Create($modified); $cgt = ClientGatewayToken::create($modified);
$key = "client_gateway_tokens_{$resource['id']}"; $key = "client_gateway_tokens_{$resource['id']}";
@ -1684,7 +1684,8 @@ class Import implements ShouldQueue
$modified['company_id'] = $this->company->id; $modified['company_id'] = $this->company->id;
$modified['user_id'] = $this->processUserId($resource); $modified['user_id'] = $this->processUserId($resource);
$task_status = TaskStatus::Create($modified); /** @var \App\Models\TaskStatus $task_status **/
$task_status = TaskStatus::create($modified);
$key = "task_statuses_{$resource['id']}"; $key = "task_statuses_{$resource['id']}";
@ -1712,7 +1713,8 @@ class Import implements ShouldQueue
$modified['company_id'] = $this->company->id; $modified['company_id'] = $this->company->id;
$modified['user_id'] = $this->processUserId($resource); $modified['user_id'] = $this->processUserId($resource);
$expense_category = ExpenseCategory::Create($modified); /** @var \App\Models\ExpenseCategory $expense_category **/
$expense_category = ExpenseCategory::create($modified);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
@ -1757,7 +1759,8 @@ class Import implements ShouldQueue
$modified['status_id'] = $this->transformId('task_statuses', $resource['status_id']); $modified['status_id'] = $this->transformId('task_statuses', $resource['status_id']);
} }
$task = Task::Create($modified); /** @var \App\Models\Task $task **/
$task = Task::create($modified);
if (array_key_exists('created_at', $modified)) { if (array_key_exists('created_at', $modified)) {
$task->created_at = Carbon::parse($modified['created_at']); $task->created_at = Carbon::parse($modified['created_at']);
@ -1767,8 +1770,6 @@ class Import implements ShouldQueue
$task->updated_at = Carbon::parse($modified['updated_at']); $task->updated_at = Carbon::parse($modified['updated_at']);
} }
$task->save(['timestamps' => false]); $task->save(['timestamps' => false]);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
@ -1802,7 +1803,8 @@ class Import implements ShouldQueue
$modified['client_id'] = $this->transformId('clients', $resource['client_id']); $modified['client_id'] = $this->transformId('clients', $resource['client_id']);
} }
$project = Project::Create($modified); /** @var \App\Models\Project $project **/
$project = Project::create($modified);
$key = "projects_{$resource['id']}"; $key = "projects_{$resource['id']}";
@ -1871,6 +1873,7 @@ try {
$modified['updated_at'] = $modified['created_at']; $modified['updated_at'] = $modified['created_at'];
/** @var \App\Models\Activity $act **/
$act = Activity::make($modified); $act = Activity::make($modified);
$act->save(['timestamps' => false]); $act->save(['timestamps' => false]);
@ -1921,7 +1924,8 @@ nlog("could not import activity: {$e->getMessage()}");
$modified['vendor_id'] = $this->transformId('vendors', $resource['vendor_id']); $modified['vendor_id'] = $this->transformId('vendors', $resource['vendor_id']);
} }
$expense = Expense::Create($modified); /** @var \App\Models\Expense $expense **/
$expense = Expense::create($modified);
if (array_key_exists('created_at', $modified)) { if (array_key_exists('created_at', $modified)) {
$expense->created_at = Carbon::parse($modified['created_at']); $expense->created_at = Carbon::parse($modified['created_at']);
@ -1931,8 +1935,6 @@ nlog("could not import activity: {$e->getMessage()}");
$expense->updated_at = Carbon::parse($modified['updated_at']); $expense->updated_at = Carbon::parse($modified['updated_at']);
} }
$expense->save(['timestamps' => false]); $expense->save(['timestamps' => false]);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;

View File

@ -44,13 +44,13 @@ class ImportStripeCustomers implements ShouldQueue
/** /**
* Execute the job. * Execute the job.
* *
* @return bool
*/ */
public function handle() public function handle()
{ {
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
$cgs = CompanyGateway::where('company_id', $this->company->id) $cgs = CompanyGateway::query()
->where('company_id', $this->company->id)
->where('is_deleted', 0) ->where('is_deleted', 0)
->whereIn('gateway_key', $this->stripe_keys) ->whereIn('gateway_key', $this->stripe_keys)
->get(); ->get();

View File

@ -159,7 +159,7 @@ class ReminderJob implements ShouldQueue
} }
} }
private function addFeeToNewInvoice(Invoice $over_due_invoice, string $reminder_template, array $fees): void private function addFeeToNewInvoice(Invoice $over_due_invoice, string $reminder_template, array $fees)
{ {
$amount = $fees[0]; $amount = $fees[0];

View File

@ -42,13 +42,13 @@ class StripeUpdatePaymentMethods implements ShouldQueue
/** /**
* Execute the job. * Execute the job.
* *
* @return bool
*/ */
public function handle() public function handle()
{ {
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
$cgs = CompanyGateway::where('company_id', $this->company->id) $cgs = CompanyGateway::query()
->where('company_id', $this->company->id)
->whereIn('gateway_key', $this->stripe_keys) ->whereIn('gateway_key', $this->stripe_keys)
->get(); ->get();

View File

@ -51,6 +51,8 @@ class UploadFile implements ShouldQueue
public $entity; public $entity;
public $disk;
public function __construct($file, $type, $user, $company, $entity, $disk = null, $is_public = false) public function __construct($file, $type, $user, $company, $entity, $disk = null, $is_public = false)
{ {
$this->file = $file; $this->file = $file;

View File

@ -43,7 +43,6 @@ class WebhookHandler implements ShouldQueue
/** /**
* Execute the job. * Execute the job.
* *
* @return bool
*/ */
public function handle() public function handle()
{ {
@ -54,7 +53,8 @@ class WebhookHandler implements ShouldQueue
return true; return true;
} }
Webhook::where('company_id', $this->company->id) Webhook::query()
->where('company_id', $this->company->id)
->where('event_id', $this->event_id) ->where('event_id', $this->event_id)
->cursor() ->cursor()
->each(function ($subscription) { ->each(function ($subscription) {

View File

@ -39,6 +39,10 @@ class OAuth
const SOCIAL_APPLE = 8; const SOCIAL_APPLE = 8;
public $provider_instance;
public $provider_id;
/** /**
* @param \Laravel\Socialite\Facades\Socialite $socialite_user * @param \Laravel\Socialite\Facades\Socialite $socialite_user
* @return bool | \App\Models\User | \App\Models\User | null * @return bool | \App\Models\User | \App\Models\User | null
@ -87,6 +91,8 @@ class OAuth
return 'microsoft'; return 'microsoft';
case self::SOCIAL_APPLE: case self::SOCIAL_APPLE:
return 'apple'; return 'apple';
default:
return 'google';
} }
} }
@ -109,6 +115,8 @@ class OAuth
return self::SOCIAL_MICROSOFT; return self::SOCIAL_MICROSOFT;
case 'apple': case 'apple':
return self::SOCIAL_APPLE; return self::SOCIAL_APPLE;
default:
return self::SOCIAL_GOOGLE;
} }
} }

View File

@ -38,7 +38,7 @@ class MigrationCompleted extends Mailable
public function build() public function build()
{ {
MultiDB::setDb($this->db); MultiDB::setDb($this->db);
$this->company = Company::find($this->company_id); $this->company = Company::query()->find($this->company_id);
App::forgetInstance('translator'); App::forgetInstance('translator');
$t = app('translator'); $t = app('translator');

View File

@ -537,8 +537,8 @@ class Client extends BaseModel implements HasLocalePreference
foreach ($pms as $pm) { foreach ($pms as $pm) {
if ($pm['gateway_type_id'] == GatewayType::CREDIT_CARD) { if ($pm['gateway_type_id'] == GatewayType::CREDIT_CARD) {
/**@var \App\Models\CompanyGateway $cg */
$cg = CompanyGateway::find($pm['company_gateway_id']); $cg = CompanyGateway::query()->find($pm['company_gateway_id']);
if ($cg && ! property_exists($cg->fees_and_limits, strval(GatewayType::CREDIT_CARD))) { if ($cg && ! property_exists($cg->fees_and_limits, strval(GatewayType::CREDIT_CARD))) {
$fees_and_limits = $cg->fees_and_limits; $fees_and_limits = $cg->fees_and_limits;
@ -561,7 +561,7 @@ class Client extends BaseModel implements HasLocalePreference
foreach ($pms as $pm) { foreach ($pms as $pm) {
if ($pm['gateway_type_id'] == GatewayType::BACS) { if ($pm['gateway_type_id'] == GatewayType::BACS) {
$cg = CompanyGateway::find($pm['company_gateway_id']); $cg = CompanyGateway::query()->find($pm['company_gateway_id']);
if ($cg && ! property_exists($cg->fees_and_limits, GatewayType::BACS)) { if ($cg && ! property_exists($cg->fees_and_limits, GatewayType::BACS)) {
$fees_and_limits = $cg->fees_and_limits; $fees_and_limits = $cg->fees_and_limits;
@ -587,7 +587,7 @@ class Client extends BaseModel implements HasLocalePreference
if ($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))) { if ($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))) {
foreach ($pms as $pm) { foreach ($pms as $pm) {
if ($pm['gateway_type_id'] == GatewayType::BANK_TRANSFER) { if ($pm['gateway_type_id'] == GatewayType::BANK_TRANSFER) {
$cg = CompanyGateway::find($pm['company_gateway_id']); $cg = CompanyGateway::query()->find($pm['company_gateway_id']);
if ($cg && ! property_exists($cg->fees_and_limits, GatewayType::BANK_TRANSFER)) { if ($cg && ! property_exists($cg->fees_and_limits, GatewayType::BANK_TRANSFER)) {
$fees_and_limits = $cg->fees_and_limits; $fees_and_limits = $cg->fees_and_limits;
@ -606,7 +606,7 @@ class Client extends BaseModel implements HasLocalePreference
if ($this->currency()->code == 'EUR' && (in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id')) || in_array(GatewayType::SEPA, array_column($pms, 'gateway_type_id')))) { if ($this->currency()->code == 'EUR' && (in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id')) || in_array(GatewayType::SEPA, array_column($pms, 'gateway_type_id')))) {
foreach ($pms as $pm) { foreach ($pms as $pm) {
if ($pm['gateway_type_id'] == GatewayType::SEPA) { if ($pm['gateway_type_id'] == GatewayType::SEPA) {
$cg = CompanyGateway::find($pm['company_gateway_id']); $cg = CompanyGateway::query()->find($pm['company_gateway_id']);
if ($cg && $cg->fees_and_limits->{GatewayType::SEPA}->is_enabled) { if ($cg && $cg->fees_and_limits->{GatewayType::SEPA}->is_enabled) {
return $cg; return $cg;
@ -618,7 +618,7 @@ class Client extends BaseModel implements HasLocalePreference
if (in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) { if (in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) {
foreach ($pms as $pm) { foreach ($pms as $pm) {
if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) { if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) {
$cg = CompanyGateway::find($pm['company_gateway_id']); $cg = CompanyGateway::query()->find($pm['company_gateway_id']);
if ($cg && $cg->fees_and_limits->{GatewayType::DIRECT_DEBIT}->is_enabled) { if ($cg && $cg->fees_and_limits->{GatewayType::DIRECT_DEBIT}->is_enabled) {
return $cg; return $cg;

View File

@ -67,6 +67,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property bool $is_large * @property bool $is_large
* @property int $enable_shop_api * @property int $enable_shop_api
* @property string $default_auto_bill * @property string $default_auto_bill
* @property string $custom_value1
* @property string $custom_value2
* @property string $custom_value3
* @property string $custom_value4
* @property bool $mark_expenses_invoiceable * @property bool $mark_expenses_invoiceable
* @property bool $mark_expenses_paid * @property bool $mark_expenses_paid
* @property bool $invoice_expense_documents * @property bool $invoice_expense_documents

View File

@ -167,17 +167,17 @@ class CompanyGateway extends BaseModel
->take(50); ->take(50);
} }
public function company() public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{ {
return $this->belongsTo(Company::class); return $this->belongsTo(Company::class);
} }
public function client_gateway_tokens() public function client_gateway_tokens(): \Illuminate\Database\Eloquent\Relations\HasMany
{ {
return $this->hasMany(ClientGatewayToken::class); return $this->hasMany(ClientGatewayToken::class);
} }
public function gateway() public function gateway(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{ {
return $this->belongsTo(Gateway::class, 'gateway_key', 'key'); return $this->belongsTo(Gateway::class, 'gateway_key', 'key');
} }
@ -190,8 +190,8 @@ class CompanyGateway extends BaseModel
/* This is the public entry point into the payment superclass */ /* This is the public entry point into the payment superclass */
public function driver(Client $client = null) public function driver(Client $client = null)
{ {
// $class = static::driver_class(); // $class = static::driver_class();
$class = self::driver_class(); $class = self::driver_class();
if (!$class) { if (!$class) {
return false; return false;

View File

@ -18,7 +18,7 @@ use Illuminate\Database\Eloquent\Model;
* *
* @property int $id * @property int $id
* @property string $hash * @property string $hash
* @property string $fee_total * @property float $fee_total
* @property int|null $fee_invoice_id * @property int|null $fee_invoice_id
* @property mixed $data * @property mixed $data
* @property int|null $payment_id * @property int|null $payment_id

View File

@ -50,12 +50,12 @@ use Illuminate\Support\Facades\Storage;
* @property string|null $private_notes * @property string|null $private_notes
* @property string|null $terms * @property string|null $terms
* @property string|null $tax_name1 * @property string|null $tax_name1
* @property string $tax_rate1 * @property float $tax_rate1
* @property string|null $tax_name2 * @property string|null $tax_name2
* @property string $tax_rate2 * @property float $tax_rate2
* @property string|null $tax_name3 * @property string|null $tax_name3
* @property string $tax_rate3 * @property float $tax_rate3
* @property string $total_taxes * @property float $total_taxes
* @property int $uses_inclusive_taxes * @property int $uses_inclusive_taxes
* @property string|null $reminder1_sent * @property string|null $reminder1_sent
* @property string|null $reminder2_sent * @property string|null $reminder2_sent
@ -74,11 +74,11 @@ use Illuminate\Support\Facades\Storage;
* @property int $custom_surcharge_tax2 * @property int $custom_surcharge_tax2
* @property int $custom_surcharge_tax3 * @property int $custom_surcharge_tax3
* @property int $custom_surcharge_tax4 * @property int $custom_surcharge_tax4
* @property string $exchange_rate * @property float $exchange_rate
* @property string $balance * @property float $balance
* @property float|null $partial * @property float|null $partial
* @property string $amount * @property float $amount
* @property string $paid_to_date * @property float $paid_to_date
* @property string|null $partial_due_date * @property string|null $partial_due_date
* @property string|null $last_viewed * @property string|null $last_viewed
* @property int|null $deleted_at * @property int|null $deleted_at

View File

@ -47,17 +47,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $recurring_product_ids * @property string|null $recurring_product_ids
* @property string $name * @property string $name
* @property int|null $group_id * @property int|null $group_id
* @property string $price * @property float $price
* @property string $promo_price * @property float $promo_price
* @property int $registration_required * @property int $registration_required
* @property int $use_inventory_management * @property int $use_inventory_management
* @property string|null $optional_product_ids * @property string|null $optional_product_ids
* @property string|null $optional_recurring_product_ids * @property string|null $optional_recurring_product_ids
* @property \App\Models\Company $company * @property-read \App\Models\Company $company
* @property-read mixed $hashed_id * @property-read mixed $hashed_id
* @property-read \App\Models\GroupSetting|null $group_settings * @property-read \App\Models\GroupSetting|null $group_settings
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Database\Factories\SubscriptionFactory factory($count = null, $state = []) * @method static \Database\Factories\SubscriptionFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|Subscription filter(\App\Filters\QueryFilters $filters) * @method static \Illuminate\Database\Eloquent\Builder|Subscription filter(\App\Filters\QueryFilters $filters)
@ -68,6 +67,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope()
* @method static \Illuminate\Database\Eloquent\Builder|Subscription withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Subscription withTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|Subscription withoutTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Subscription withoutTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|Subscription with($value)
* @mixin \Eloquent * @mixin \Eloquent
*/ */
class Subscription extends BaseModel class Subscription extends BaseModel

View File

@ -31,7 +31,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read \App\Models\Company|null $company * @property-read \App\Models\Company|null $company
* @property-read mixed $hashed_id * @property-read mixed $hashed_id
* @property-read \App\Models\User|null $user * @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Illuminate\Database\Eloquent\Builder|Webhook filter(\App\Filters\QueryFilters $filters) * @method static \Illuminate\Database\Eloquent\Builder|Webhook filter(\App\Filters\QueryFilters $filters)
* @method static \Illuminate\Database\Eloquent\Builder|Webhook newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Webhook newModelQuery()

View File

@ -56,7 +56,11 @@ class BaseDriver extends AbstractPaymentDriver
/* The Invitation */ /* The Invitation */
public $invitation; public $invitation;
/* The client */ /**
* The Client
*
* @var \App\Models\Client|null $client
*/
public $client; public $client;
/* Gateway capabilities */ /* Gateway capabilities */

View File

@ -39,7 +39,7 @@ class PaymentIntentPartiallyFundedWebhook implements ShouldQueue
{ {
MultiDB::findAndSetDbByCompanyKey($this->company_key); MultiDB::findAndSetDbByCompanyKey($this->company_key);
$company = Company::where('company_key', $this->company_key)->first(); $company = Company::query()->where('company_key', $this->company_key)->first();
foreach ($this->stripe_request as $transaction) { foreach ($this->stripe_request as $transaction) {
$payment_intent = false; $payment_intent = false;
@ -65,7 +65,7 @@ class PaymentIntentPartiallyFundedWebhook implements ShouldQueue
nlog("paymentintent found but no payment"); nlog("paymentintent found but no payment");
} }
$company_gateway = CompanyGateway::find($this->company_gateway_id); $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$stripe_driver = $company_gateway->driver()->init(); $stripe_driver = $company_gateway->driver()->init();
$hash = isset($transaction['metadata']['payment_hash']) ? $transaction['metadata']['payment_hash'] : false; $hash = isset($transaction['metadata']['payment_hash']) ? $transaction['metadata']['payment_hash'] : false;

View File

@ -57,17 +57,15 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
{ {
MultiDB::findAndSetDbByCompanyKey($this->company_key); MultiDB::findAndSetDbByCompanyKey($this->company_key);
$company = Company::where('company_key', $this->company_key)->first(); $company = Company::query()->where('company_key', $this->company_key)->first();
foreach ($this->stripe_request as $transaction) { foreach ($this->stripe_request as $transaction) {
if (array_key_exists('payment_intent', $transaction)) { if (array_key_exists('payment_intent', $transaction)) {
/** @var \App\Models\Payment $payment **/
$payment = Payment::query() $payment = Payment::query()
->where('company_id', $company->id) ->where('company_id', $company->id)
->where('transaction_reference', $transaction['payment_intent']) ->where('transaction_reference', $transaction['payment_intent'])
->first(); ->first();
} else { } else {
/** @var \App\Models\Payment $payment **/
$payment = Payment::query() $payment = Payment::query()
->where('company_id', $company->id) ->where('company_id', $company->id)
->where('transaction_reference', $transaction['id']) ->where('transaction_reference', $transaction['id'])
@ -97,8 +95,7 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
if ($this->payment_completed) { if ($this->payment_completed) {
return; return;
} }
/** @var \App\Models\CompanyGateway $company_gateway **/ $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$company_gateway = CompanyGateway::find($this->company_gateway_id);
$stripe_driver = $company_gateway->driver()->init(); $stripe_driver = $company_gateway->driver()->init();
$charge_id = false; $charge_id = false;
@ -126,10 +123,8 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
return; return;
} }
/** @var \App\Models\Company $company **/ $company = Company::query()->where('company_key', $this->company_key)->first();
$company = Company::where('company_key', $this->company_key)->first();
/** @var \App\Models\Payment $payment **/
$payment = Payment::query() $payment = Payment::query()
->where('company_id', $company->id) ->where('company_id', $company->id)
->where('transaction_reference', $charge['id']) ->where('transaction_reference', $charge['id'])
@ -189,7 +184,7 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
private function updateAchPayment($payment_hash, $client, $meta) private function updateAchPayment($payment_hash, $client, $meta)
{ {
$company_gateway = CompanyGateway::find($this->company_gateway_id); $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$payment_method_type = $meta['gateway_type_id']; $payment_method_type = $meta['gateway_type_id'];
$driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type); $driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);

View File

@ -56,7 +56,7 @@ class PaymentIntentWebhook implements ShouldQueue
{ {
MultiDB::findAndSetDbByCompanyKey($this->company_key); MultiDB::findAndSetDbByCompanyKey($this->company_key);
$company = Company::where('company_key', $this->company_key)->first(); $company = Company::query()->where('company_key', $this->company_key)->first();
foreach ($this->stripe_request as $transaction) { foreach ($this->stripe_request as $transaction) {
if (array_key_exists('payment_intent', $transaction)) { if (array_key_exists('payment_intent', $transaction)) {
@ -84,7 +84,7 @@ class PaymentIntentWebhook implements ShouldQueue
return; return;
} }
$company_gateway = CompanyGateway::find($this->company_gateway_id); $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$stripe_driver = $company_gateway->driver()->init(); $stripe_driver = $company_gateway->driver()->init();
$charge_id = false; $charge_id = false;
@ -196,7 +196,7 @@ class PaymentIntentWebhook implements ShouldQueue
private function updateAchPayment($payment_hash, $client, $meta) private function updateAchPayment($payment_hash, $client, $meta)
{ {
$company_gateway = CompanyGateway::find($this->company_gateway_id); $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$payment_method_type = $meta['gateway_type_id']; $payment_method_type = $meta['gateway_type_id'];
$driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type); $driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);
@ -267,7 +267,7 @@ class PaymentIntentWebhook implements ShouldQueue
private function updateCreditCardPayment($payment_hash, $client, $meta) private function updateCreditCardPayment($payment_hash, $client, $meta)
{ {
$company_gateway = CompanyGateway::find($this->company_gateway_id); $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$payment_method_type = $meta['gateway_type_id']; $payment_method_type = $meta['gateway_type_id'];
$driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type); $driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);

View File

@ -90,7 +90,7 @@ class UpdatePaymentMethods
); );
foreach ($bank_methods->data as $method) { foreach ($bank_methods->data as $method) {
$token = ClientGatewayToken::where([ $token = ClientGatewayToken::query()->where([
'gateway_customer_reference' => $customer->id, 'gateway_customer_reference' => $customer->id,
'token' => $method->id, 'token' => $method->id,
'client_id' => $client->id, 'client_id' => $client->id,
@ -140,7 +140,7 @@ class UpdatePaymentMethods
} }
foreach ($sources->data as $method) { foreach ($sources->data as $method) {
$token_exists = ClientGatewayToken::where([ $token_exists = ClientGatewayToken::query()->where([
'gateway_customer_reference' => $customer->id, 'gateway_customer_reference' => $customer->id,
'token' => $method->id, 'token' => $method->id,
'client_id' => $client->id, 'client_id' => $client->id,
@ -176,7 +176,7 @@ class UpdatePaymentMethods
private function addOrUpdateCard(PaymentMethod $method, $customer_reference, Client $client, $type_id) private function addOrUpdateCard(PaymentMethod $method, $customer_reference, Client $client, $type_id)
{ {
$token_exists = ClientGatewayToken::where([ $token_exists = ClientGatewayToken::query()->where([
'gateway_customer_reference' => $customer_reference, 'gateway_customer_reference' => $customer_reference,
'token' => $method->id, 'token' => $method->id,
'client_id' => $client->id, 'client_id' => $client->id,

View File

@ -501,7 +501,8 @@ class StripePaymentDriver extends BaseDriver
$this->init(); $this->init();
$client_gateway_token = ClientGatewayToken::whereClientId($this->client->id) $client_gateway_token = ClientGatewayToken::query()
->whereClientId($this->client->id)
->whereCompanyGatewayId($this->company_gateway->id) ->whereCompanyGatewayId($this->company_gateway->id)
->first(); ->first();
@ -890,9 +891,11 @@ class StripePaymentDriver extends BaseDriver
return Account::all(); return Account::all();
} }
public function setClientFromCustomer($customer) public function setClientFromCustomer($customer): self
{ {
$this->client = ClientGatewayToken::where('gateway_customer_reference', $customer)->client; $this->client = ClientGatewayToken::query()->where('gateway_customer_reference', $customer)->first()->client;
return $this;
} }
/** /**

View File

@ -201,7 +201,7 @@ class ACH
public function paymentResponse($request) public function paymentResponse($request)
{ {
$token = ClientGatewayToken::find($this->decodePrimaryKey($request->input('source'))); $token = ClientGatewayToken::query()->find($this->decodePrimaryKey($request->input('source')));
$token_meta = $token->meta; $token_meta = $token->meta;
if (! property_exists($token_meta, 'state') || $token_meta->state != 'authorized') { if (! property_exists($token_meta, 'state') || $token_meta->state != 'authorized') {

View File

@ -136,7 +136,7 @@ class InvoiceMigrationRepository extends BaseRepository
$state['finished_amount'] = $model->amount; $state['finished_amount'] = $model->amount;
$model = $model->service()->applyNumber()->save(); $model = $model->service()->applyNumber()->setReminder()->save();
if ($class->name == Invoice::class || $class->name == RecurringInvoice::class) { if ($class->name == Invoice::class || $class->name == RecurringInvoice::class) {
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) { if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {

View File

@ -137,7 +137,7 @@ class UserRepository extends BaseRepository
$cu->forceDelete(); $cu->forceDelete();
} }
event(new UserWasDeleted($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new UserWasDeleted($user, auth()->user(), auth()->user()->company(), Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$user->delete(); $user->delete();

View File

@ -33,6 +33,9 @@ class ProcessBankRules extends AbstractService
protected $invoices; protected $invoices;
/**
* @param \App\Models\BankTransaction $bank_transaction
*/
public function __construct(public BankTransaction $bank_transaction) public function __construct(public BankTransaction $bank_transaction)
{ {
} }
@ -48,14 +51,12 @@ class ProcessBankRules extends AbstractService
private function matchCredit() private function matchCredit()
{ {
/** @var \Illuminate\Database\Eloquent\Collection<Invoice> $this->invoices */
$this->invoices = Invoice::query()->where('company_id', $this->bank_transaction->company_id) $this->invoices = Invoice::query()->where('company_id', $this->bank_transaction->company_id)
->whereIn('status_id', [1,2,3]) ->whereIn('status_id', [1,2,3])
->where('is_deleted', 0) ->where('is_deleted', 0)
->get(); ->get();
$invoice = $this->invoices->first(function ($value, $key) { $invoice = $this->invoices->first(function ($value, $key) {
/** @var \App\Models\Invoice $value */
return str_contains($this->bank_transaction->description, $value->number); return str_contains($this->bank_transaction->description, $value->number);
}); });

View File

@ -166,7 +166,6 @@ class ClientService
* *
* @param mixed $pdf The pdf blob * @param mixed $pdf The pdf blob
* @param array $options The statement options array * @param array $options The statement options array
* @return void
*/ */
private function emailStatement($pdf, array $options): void private function emailStatement($pdf, array $options): void
{ {

View File

@ -25,6 +25,7 @@ class PaymentMethod
private $amount; private $amount;
/** @var \Illuminate\Support\Collection<CompanyGateway> $gateways **/
private $gateways; private $gateways;
private $payment_methods; private $payment_methods;
@ -78,7 +79,8 @@ class PaymentMethod
return array_search($model->id, $transformed_ids); // this closure sorts for us return array_search($model->id, $transformed_ids); // this closure sorts for us
}); });
} else { } else {
$this->gateways = CompanyGateway::with('gateway') $this->gateways = CompanyGateway::query()
->with('gateway')
->where('company_id', $this->client->company_id) ->where('company_id', $this->client->company_id)
->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa') ->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa')
->whereNull('deleted_at') ->whereNull('deleted_at')
@ -112,7 +114,8 @@ class PaymentMethod
return array_search($model->id, $transformed_ids); // this closure sorts for us return array_search($model->id, $transformed_ids); // this closure sorts for us
}); });
} else { } else {
$this->gateways = CompanyGateway::with('gateway') $this->gateways = CompanyGateway::query()
->with('gateway')
->where('company_id', $this->client->company_id) ->where('company_id', $this->client->company_id)
->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa') ->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa')
->whereNull('deleted_at') ->whereNull('deleted_at')
@ -171,7 +174,7 @@ class PaymentMethod
{ {
foreach ($this->payment_methods as $key => $child_array) { foreach ($this->payment_methods as $key => $child_array) {
foreach ($child_array as $gateway_id => $gateway_type_id) { foreach ($child_array as $gateway_id => $gateway_type_id) {
$gateway = CompanyGateway::find($gateway_id); $gateway = CompanyGateway::query()->find($gateway_id);
$fee_label = $gateway->calcGatewayFeeLabel($this->amount, $this->client, $gateway_type_id); $fee_label = $gateway->calcGatewayFeeLabel($this->amount, $this->client, $gateway_type_id);

View File

@ -52,7 +52,7 @@ class InstantPayment
$is_credit_payment = true; $is_credit_payment = true;
} }
$gateway = CompanyGateway::find($this->request->input('company_gateway_id')); $gateway = CompanyGateway::query()->find($this->request->input('company_gateway_id'));
/** /**
* find invoices * find invoices
@ -100,7 +100,7 @@ class InstantPayment
* Determine the payable amount and the max payable. ie either partial or invoice balance * Determine the payable amount and the max payable. ie either partial or invoice balance
*/ */
$payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount'], $client->currency()->precision)); $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), $client->currency()->precision);
$invoice_balance = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), $client->currency()->precision); $invoice_balance = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), $client->currency()->precision);
@ -155,7 +155,7 @@ class InstantPayment
return $payable_invoice['invoice_id'] == $inv->hashed_id; return $payable_invoice['invoice_id'] == $inv->hashed_id;
}); });
$payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount'], $client->currency()->precision)); $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), $client->currency()->precision);
$invoice_balance = Number::roundValue($invoice->balance, $client->currency()->precision); $invoice_balance = Number::roundValue($invoice->balance, $client->currency()->precision);
$payable_invoice['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format()); $payable_invoice['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format());
@ -273,7 +273,7 @@ class InstantPayment
'is_recurring' => $this->request->is_recurring, 'is_recurring' => $this->request->is_recurring,
]; ];
if ($is_credit_payment || $totals <= 0) { if ($is_credit_payment) {
return $this->processCreditPayment($this->request, $data); return $this->processCreditPayment($this->request, $data);
} }

View File

@ -33,7 +33,6 @@ class SendEmail
/** /**
* Builds the correct template to send. * Builds the correct template to send.
* @return void
*/ */
public function run() public function run()
{ {

View File

@ -194,7 +194,7 @@ class AutoBillInvoice extends AbstractService
$current_credit = false; $current_credit = false;
foreach ($this->used_credit as $credit) { foreach ($this->used_credit as $credit) {
$current_credit = Credit::find($credit['credit_id']); $current_credit = Credit::query()->find($credit['credit_id']);
$payment->credits() $payment->credits()
->attach($current_credit->id, ['amount' => $credit['amount']]); ->attach($current_credit->id, ['amount' => $credit['amount']]);

View File

@ -302,6 +302,7 @@ class FacturaEInvoice extends AbstractService
private function resolvePaymentMethod(\App\Models\Payment $payment): array private function resolvePaymentMethod(\App\Models\Payment $payment): array
{ {
$data = []; $data = [];
$method = FacturaePayment::TYPE_CARD;
match($payment->type_id){ match($payment->type_id){
PaymentType::BANK_TRANSFER => $method = FacturaePayment::TYPE_TRANSFER , PaymentType::BANK_TRANSFER => $method = FacturaePayment::TYPE_TRANSFER ,

View File

@ -238,7 +238,8 @@ class RefundPayment
private function adjustInvoices() private function adjustInvoices()
{ {
if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) {
foreach ($this->refund_data['invoices'] as $refunded_invoice) { foreach ($this->refund_data['invoices'] as $refunded_invoice)
{
$invoice = Invoice::withTrashed()->find($refunded_invoice['invoice_id']); $invoice = Invoice::withTrashed()->find($refunded_invoice['invoice_id']);
if ($invoice->trashed()) { if ($invoice->trashed()) {
@ -265,10 +266,10 @@ class RefundPayment
$invoice->saveQuietly(); $invoice->saveQuietly();
//06-09-2022 //08-08-2023
$client = $invoice->client $client = $invoice->client
->service() ->service()
->updateBalance($refunded_invoice['amount']) ->updateBalanceAndPaidToDate($refunded_invoice['amount'], -1 * $refunded_invoice['amount'])
->save(); ->save();
if ($invoice->is_deleted) { if ($invoice->is_deleted) {
@ -276,13 +277,6 @@ class RefundPayment
} }
} }
$client = $this->payment->client->fresh();
if ($client->trashed()) {
$client->restore();
}
$client->service()->updatePaidToDate(-1 * $refunded_invoice['amount'])->save();
} else { } else {
//if we are refunding and no payments have been tagged, then we need to decrement the client->paid_to_date by the total refund amount. //if we are refunding and no payments have been tagged, then we need to decrement the client->paid_to_date by the total refund amount.

View File

@ -42,7 +42,8 @@ class StubBuilder
public $entity_type; public $entity_type;
public \App\Models\Client | \App\Models\Vendor $recipient; /** @var Client | Vendor $recipient **/
public Client | Vendor $recipient;
public mixed $contact; public mixed $contact;
@ -178,7 +179,7 @@ class StubBuilder
$design_string = "{$this->entity_type}_design_id"; $design_string = "{$this->entity_type}_design_id";
$design = DesignModel::withTrashed()->find($this->decodePrimaryKey($html->settings->{$design_string})); $design = DesignModel::query()->withTrashed()->find($this->decodePrimaryKey($html->settings->{$design_string}));
$template = new PdfMakerDesign(strtolower($design->name)); $template = new PdfMakerDesign(strtolower($design->name));

View File

@ -293,7 +293,7 @@ class ProfitLoss
foreach ($payment->paymentables as $pivot) { foreach ($payment->paymentables as $pivot) {
if ($pivot->paymentable_type == 'invoices') { if ($pivot->paymentable_type == 'invoices') {
$invoice = Invoice::withTrashed()->find($pivot->paymentable_id); $invoice = Invoice::query()->withTrashed()->find($pivot->paymentable_id);
$amount_payment_paid += $pivot->amount - $pivot->refunded; $amount_payment_paid += $pivot->amount - $pivot->refunded;
$amount_payment_paid_converted += $amount_payment_paid / ($payment->exchange_rate ?: 1); $amount_payment_paid_converted += $amount_payment_paid / ($payment->exchange_rate ?: 1);

View File

@ -213,7 +213,7 @@ class SubscriptionService
$email_object->body = ctrans('texts.white_label_body', ['license_key' => $license_key]); $email_object->body = ctrans('texts.white_label_body', ['license_key' => $license_key]);
$email_object->client_id = $invoice->client_id; $email_object->client_id = $invoice->client_id;
$email_object->client_contact_id = $contact->id; $email_object->client_contact_id = $contact->id;
$email_object->invitation_key = $invitation->invitation_key; $email_object->invitation_key = $invitation->key;
$email_object->invitation_id = $invitation->id; $email_object->invitation_id = $invitation->id;
$email_object->entity_id = $invoice->id; $email_object->entity_id = $invoice->id;
$email_object->entity_class = Invoice::class; $email_object->entity_class = Invoice::class;
@ -676,7 +676,7 @@ class SubscriptionService
$pro_rata_refund_amount = $this->calculateProRataRefund($last_invoice, $old_subscription); $pro_rata_refund_amount = $this->calculateProRataRefund($last_invoice, $old_subscription);
} elseif ($last_invoice->balance > 0) { } elseif ($last_invoice->balance > 0) {
$pro_rata_charge_amount = $this->calculateProRataCharge($last_invoice, $old_subscription); $pro_rata_charge_amount = $this->calculateProRataCharge($last_invoice);
nlog("pro rata charge = {$pro_rata_charge_amount}"); nlog("pro rata charge = {$pro_rata_charge_amount}");
} else { } else {
$pro_rata_refund_amount = $this->calculateProRataRefund($last_invoice, $old_subscription) * -1; $pro_rata_refund_amount = $this->calculateProRataRefund($last_invoice, $old_subscription) * -1;
@ -740,7 +740,7 @@ class SubscriptionService
} }
if ($last_invoice->balance > 0) { if ($last_invoice->balance > 0) {
$pro_rata_charge_amount = $this->calculateProRataCharge($last_invoice, $old_subscription); $pro_rata_charge_amount = $this->calculateProRataCharge($last_invoice);
nlog("pro rata charge = {$pro_rata_charge_amount}"); nlog("pro rata charge = {$pro_rata_charge_amount}");
} else { } else {
$pro_rata_refund_amount = $this->calculateProRataRefund($last_invoice, $old_subscription) * -1; $pro_rata_refund_amount = $this->calculateProRataRefund($last_invoice, $old_subscription) * -1;
@ -807,7 +807,7 @@ class SubscriptionService
{ {
nlog("handle plan change"); nlog("handle plan change");
$old_recurring_invoice = RecurringInvoice::find($this->decodePrimaryKey($payment_hash->data->billing_context->recurring_invoice)); $old_recurring_invoice = RecurringInvoice::query()->find($this->decodePrimaryKey($payment_hash->data->billing_context->recurring_invoice));
if (!$old_recurring_invoice) { if (!$old_recurring_invoice) {
return $this->handleRedirect('/client/recurring_invoices/'); return $this->handleRedirect('/client/recurring_invoices/');
@ -816,7 +816,7 @@ class SubscriptionService
$recurring_invoice = $this->createNewRecurringInvoice($old_recurring_invoice); $recurring_invoice = $this->createNewRecurringInvoice($old_recurring_invoice);
//update the invoice and attach to the recurring invoice!!!!! //update the invoice and attach to the recurring invoice!!!!!
$invoice = Invoice::find($payment_hash->fee_invoice_id); $invoice = Invoice::query()->find($payment_hash->fee_invoice_id);
$invoice->recurring_id = $recurring_invoice->id; $invoice->recurring_id = $recurring_invoice->id;
$invoice->is_proforma = false; $invoice->is_proforma = false;
$invoice->save(); $invoice->save();
@ -1243,7 +1243,7 @@ class SubscriptionService
/** /**
* Get available upgrades & downgrades for the plan. * Get available upgrades & downgrades for the plan.
* *
* @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
public function getPlans() public function getPlans()
{ {

View File

@ -11,16 +11,24 @@
namespace App\Transformers; namespace App\Transformers;
use App\Models\Activity;
use App\Models\Backup;
use App\Models\ClientContact;
use App\Models\Invoice;
use App\Models\PurchaseOrder;
use App\Models\Task; use App\Models\Task;
use App\Models\User; use App\Models\User;
use App\Models\Quote;
use App\Models\Backup;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Vendor; use App\Models\Vendor;
use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Activity;
use App\Models\ClientContact;
use App\Models\PurchaseOrder;
use App\Models\VendorContact; use App\Models\VendorContact;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Models\RecurringInvoice;
use App\Transformers\EntityTransformer;
use App\Transformers\InvoiceHistoryTransformer;
class ActivityTransformer extends EntityTransformer class ActivityTransformer extends EntityTransformer
{ {

View File

@ -13,7 +13,9 @@ namespace App\Transformers;
use App\Models\Account; use App\Models\Account;
use App\Models\BankIntegration; use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Transformers\EntityTransformer;
/** /**
* Class BankIntegrationTransformer. * Class BankIntegrationTransformer.

View File

@ -43,7 +43,7 @@ class BankTransactionRuleTransformer extends EntityTransformer
]; ];
/** /**
* @param BankTransaction $bank_integration * @param BankTransactionRule $bank_transaction_rule
* @return array * @return array
*/ */
public function transform(BankTransactionRule $bank_transaction_rule) public function transform(BankTransactionRule $bank_transaction_rule)

View File

@ -11,9 +11,11 @@
namespace App\Transformers; namespace App\Transformers;
use App\Models\BankTransaction; use App\Models\Vendor;
use App\Models\Company; use App\Models\Company;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Payment;
use App\Models\BankTransaction;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
/** /**

View File

@ -51,6 +51,7 @@ class DocumentTransformer extends EntityTransformer
'archived_at' => (int) $document->deleted_at, 'archived_at' => (int) $document->deleted_at,
'created_at' => (int) $document->created_at, 'created_at' => (int) $document->created_at,
'is_deleted' => (bool) false, 'is_deleted' => (bool) false,
'is_public' => (bool) $document->is_public,
]; ];
} }
} }

View File

@ -94,11 +94,14 @@ class UserTransformer extends EntityTransformer
return $this->includeCollection($user->company_users, $transformer, CompanyUser::class); return $this->includeCollection($user->company_users, $transformer, CompanyUser::class);
} }
/**
*
* @param User $user
*/
public function includeCompanyUser(User $user) public function includeCompanyUser(User $user)
{ {
if (! $user->company_id && request()->header('X-API-TOKEN')) { if (! $user->company_id && request()->header('X-API-TOKEN')) {
$company_token = CompanyToken::where('token', request()->header('X-API-TOKEN'))->first(); $company_token = CompanyToken::query()->where('token', request()->header('X-API-TOKEN'))->first();
/** @var \App\Models\User $user */
$user->company_id = $company_token->company_id; $user->company_id = $company_token->company_id;
} }

View File

@ -0,0 +1,60 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('purchase_order_invitations', function (Blueprint $table) {
$table->text('signature_ip')->nullable();
});
$xag = \App\Models\Currency::find(116);
if(!$xag) {
$xag = new \App\Models\Currency();
$xag->id = 115;
$xag->code = 'XAG';
$xag->name = 'Silver Troy Ounce';
$xag->symbol = 'XAG';
$xag->thousand_separator = ',';
$xag->decimal_separator = '.';
$xag->precision = 2;
$xag->save();
}
$xau = \App\Models\Currency::find(117);
if(!$xau) {
$xau = new \App\Models\Currency();
$xau->id = 117;
$xau->code = 'XAU';
$xau->name = 'Gold Troy Ounce';
$xau->symbol = 'XAU';
$xau->thousand_separator = ',';
$xau->decimal_separator = '.';
$xau->precision = 3;
$xau->save();
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
};

View File

@ -1,7 +1,7 @@
<div> <div>
<div class="flex flex-col items-end mb-2" x-data> <div class="flex flex-col items-end mb-2" x-data>
<button wire:loading.attr="disabled" wire:click="downloadPdf" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-2 rounded inline-flex" type="button"> <button wire:loading.attr="disabled" wire:click="downloadPdf" class="bg-primary text-white px-4 py-4 lg:px-2 lg:py-2 rounded" type="button">
<span>{{ ctrans('texts.download_pdf') }}</span> <span class="mr-0">{{ ctrans('texts.download_pdf') }}</span>
<div wire:loading wire:target="downloadPdf"> <div wire:loading wire:target="downloadPdf">
<svg class="animate-spin h-5 w-5 text-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> <svg class="animate-spin h-5 w-5 text-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>