Merge pull request #9032 from turbo124/v5-develop

v5.7.60
This commit is contained in:
David Bomba 2023-12-11 22:44:40 +11:00 committed by GitHub
commit fe34ab41d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 239 additions and 133 deletions

View File

@ -1 +1 @@
5.7.59 5.7.60

View File

@ -12,7 +12,8 @@
namespace App\DataProviders; namespace App\DataProviders;
class Domains { class Domains
{
private static array $verify_domains = [ private static array $verify_domains = [
'0-00.usa.cc', '0-00.usa.cc',

View File

@ -146,16 +146,26 @@ class InvoiceFilters extends QueryFilters
*/ */
public function upcoming(): Builder public function upcoming(): Builder
{ {
return $this->builder->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
->whereNull('due_date') return $this->builder->where(function ($query) {
$query->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
->where('is_deleted', 0)
->where('balance', '>', 0)
->where(function ($query) {
$query->whereNull('due_date')
->orWhere(function ($q) { ->orWhere(function ($q) {
$q->where('due_date', '>=', now()->startOfDay()->subSecond())->where('partial', 0); $q->where('due_date', '>=', now()->startOfDay()->subSecond())->where('partial', 0);
}) })
->orWhere(function ($q) { ->orWhere(function ($q) {
$q->where('partial_due_date', '>=', now()->startOfDay()->subSecond())->where('partial', '>', 0); $q->where('partial_due_date', '>=', now()->startOfDay()->subSecond())->where('partial', '>', 0);
}) });
->orderByRaw('ISNULL(due_date), due_date '. 'desc')
->orderByRaw('ISNULL(partial_due_date), partial_due_date '. 'desc'); })
->orderByRaw('ISNULL(due_date), due_date ' . 'desc')
->orderByRaw('ISNULL(partial_due_date), partial_due_date ' . 'desc');
});
} }
/** /**
@ -165,13 +175,18 @@ class InvoiceFilters extends QueryFilters
*/ */
public function overdue(): Builder public function overdue(): Builder
{ {
return $this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) return $this->builder->where(function ($query) {
$query->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('is_deleted', 0) ->where('is_deleted', 0)
->where('balance', '>', 0)
->where(function ($query) { ->where(function ($query) {
$query->where('due_date', '<', now()) $query->where('due_date', '<', now())
->orWhere('partial_due_date', '<', now()); ->orWhere('partial_due_date', '<', now());
}) })
->orderBy('due_date', 'ASC'); ->orderBy('due_date', 'ASC');
});
} }
/** /**

View File

@ -155,6 +155,7 @@ class PaymentController extends BaseController
$user = auth()->user(); $user = auth()->user();
$payment = PaymentFactory::create($user->company()->id, $user->id); $payment = PaymentFactory::create($user->company()->id, $user->id);
$payment->date = now()->addSeconds($user->company()->utc_offset())->format('Y-m-d');
return $this->itemResponse($payment); return $this->itemResponse($payment);
} }

View File

@ -30,6 +30,12 @@ class SubdomainController extends BaseController
return response()->json(['message' => ctrans('texts.subdomain_is_not_available')], 401); return response()->json(['message' => ctrans('texts.subdomain_is_not_available')], 401);
} }
if (!preg_match('/^[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?$/', request()->input('subdomain'))) {
return response()->json(['message' => ctrans('texts.subdomain_is_not_available')], 401);
}
return response()->json(['message' => 'Domain available'], 200); return response()->json(['message' => 'Domain available'], 200);
} }
} }

View File

@ -21,6 +21,12 @@ use Twilio\Rest\Client;
class TwilioController extends BaseController class TwilioController extends BaseController
{ {
private array $invalid_codes = [
'+21',
'+17152567760',
];
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -38,6 +44,10 @@ class TwilioController extends BaseController
$account = $user->company()->account; $account = $user->company()->account;
if(!$this->checkPhoneValidity($request->phone)) {
return response()->json(['message' => 'This phone number is not supported'], 400);
}
if (MultiDB::hasPhoneNumber($request->phone)) { if (MultiDB::hasPhoneNumber($request->phone)) {
return response()->json(['message' => 'This phone number has already been verified with another account'], 400); return response()->json(['message' => 'This phone number has already been verified with another account'], 400);
} }
@ -65,6 +75,19 @@ class TwilioController extends BaseController
return response()->json(['message' => 'Code sent.'], 200); return response()->json(['message' => 'Code sent.'], 200);
} }
private function checkPhoneValidity($phone)
{
foreach($this->invalid_codes as $code){
if(stripos($phone, $code) !== false) {
return false;
}
return true;
}
}
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
* *

View File

@ -60,7 +60,7 @@ class UpdateCreditRequest extends Request
$rules['file'] = $this->file_validation; $rules['file'] = $this->file_validation;
} }
$rules['number'] = ['bail', 'sometimes', Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id)]; $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id)];
$rules['client_id'] = ['bail', 'sometimes',Rule::in([$this->credit->client_id])]; $rules['client_id'] = ['bail', 'sometimes',Rule::in([$this->credit->client_id])];

View File

@ -37,7 +37,7 @@ class ImportRequest extends Request
'column_map' => 'required_with:hash|array', 'column_map' => 'required_with:hash|array',
'skip_header' => 'required_with:hash|boolean', 'skip_header' => 'required_with:hash|boolean',
'files.*' => 'file|mimes:csv,txt', 'files.*' => 'file|mimes:csv,txt',
'bank_integration_id' => 'bail|required_if:column_map,bank_transaction|min:2' 'bank_integration_id' => 'bail|required_with:column_map.bank_transaction|min:2'
]; ];
} }
} }

View File

@ -59,9 +59,8 @@ class UpdateInvoiceRequest extends Request
$rules['id'] = new LockedInvoiceRule($this->invoice); $rules['id'] = new LockedInvoiceRule($this->invoice);
$rules['number'] = ['bail', 'sometimes', Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id)]; $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id)];
$rules['is_amount_discount'] = ['boolean']; $rules['is_amount_discount'] = ['boolean'];
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->invoice->client_id])]; $rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->invoice->client_id])];
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';

View File

@ -48,7 +48,7 @@ class UpdatePurchaseOrderRequest extends Request
$rules = []; $rules = [];
$rules['number'] = ['bail', 'sometimes', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)->ignore($this->purchase_order->id)]; $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)->ignore($this->purchase_order->id)];
$rules['vendor_id'] = ['bail', 'sometimes', Rule::in([$this->purchase_order->vendor_id])]; $rules['vendor_id'] = ['bail', 'sometimes', Rule::in([$this->purchase_order->vendor_id])];
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';

View File

@ -55,7 +55,7 @@ class UpdateQuoteRequest extends Request
} }
$rules['number'] = ['bail', 'sometimes', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)]; $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)];
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])]; $rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])];
@ -73,6 +73,8 @@ class UpdateQuoteRequest extends Request
$input = $this->decodePrimaryKeys($input); $input = $this->decodePrimaryKeys($input);
$input['id'] = $this->quote->id;
if (isset($input['line_items'])) { if (isset($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
} }
@ -85,7 +87,6 @@ class UpdateQuoteRequest extends Request
$input['exchange_rate'] = 1; $input['exchange_rate'] = 1;
} }
$input['id'] = $this->quote->id;
$this->replace($input); $this->replace($input);
} }

View File

@ -42,12 +42,12 @@ class StoreSchedulerRequest extends Request
'template' => 'bail|required|string', 'template' => 'bail|required|string',
'parameters' => 'bail|array', 'parameters' => 'bail|array',
'parameters.clients' => ['bail','sometimes', 'array', new ValidClientIds()], 'parameters.clients' => ['bail','sometimes', 'array', new ValidClientIds()],
'parameters.date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom', 'parameters.date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom,all',
'parameters.start_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom'], 'parameters.start_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom'],
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'], 'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'], 'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
'parameters.entity_id' => ['bail', 'sometimes', 'string'], 'parameters.entity_id' => ['bail', 'sometimes', 'string'],
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,clients,client_contacts,credits,documents,expenses,invoices,invoice_items,quotes,quote_items,recurring_invoices,payments,products,tasks'], 'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,client,contact,client_contact,credit,document,expense,invoice,invoice_item,quote,quote_item,recurring_invoice,payment,product,task'],
'parameters.date_key' => ['bail','sometimes', 'string'], 'parameters.date_key' => ['bail','sometimes', 'string'],
'parameters.status' => ['bail','sometimes', 'string', 'in:all,draft,paid,unpaid,overdue'], 'parameters.status' => ['bail','sometimes', 'string', 'in:all,draft,paid,unpaid,overdue'],
]; ];
@ -67,6 +67,10 @@ class StoreSchedulerRequest extends Request
$input['frequency_id'] = 0; $input['frequency_id'] = 0;
} }
if(isset($input['parameters']) && isset($input['parameters']['clients'])) {
$input['parameters']['clients'] = [];
}
$this->replace($input); $this->replace($input);
} }
} }

View File

@ -39,12 +39,12 @@ class UpdateSchedulerRequest extends Request
'template' => 'bail|required|string', 'template' => 'bail|required|string',
'parameters' => 'bail|array', 'parameters' => 'bail|array',
'parameters.clients' => ['bail','sometimes', 'array', new ValidClientIds()], 'parameters.clients' => ['bail','sometimes', 'array', new ValidClientIds()],
'parameters.date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom', 'parameters.date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom,all',
'parameters.start_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom'], 'parameters.start_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom'],
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'], 'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'], 'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
'parameters.entity_id' => ['bail', 'sometimes', 'string'], 'parameters.entity_id' => ['bail', 'sometimes', 'string'],
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report', 'in:ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,clients,client_contacts,credits,documents,expenses,invoices,invoice_items,quotes,quote_items,recurring_invoices,payments,products,tasks'], 'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report', 'in:ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,client,client_contact,credit,document,expense,invoice,invoice_item,quote,quote_item,recurring_invoice,payment,product,task'],
'parameters.date_key' => ['bail','sometimes', 'string'], 'parameters.date_key' => ['bail','sometimes', 'string'],
]; ];
@ -63,6 +63,10 @@ class UpdateSchedulerRequest extends Request
$input['frequency_id'] = 0; $input['frequency_id'] = 0;
} }
if(isset($input['parameters']) && isset($input['parameters']['clients'])) {
$input['parameters']['clients'] = [];
}
$this->replace($input); $this->replace($input);

View File

@ -387,19 +387,19 @@ class CompanyExport implements ShouldQueue
})->all(); })->all();
$this->export_data['bank_integrations'] = $this->company->bank_integrations()->orderBy('id', 'ASC')->cursor()->map(function ($bank_integration) { $this->export_data['bank_integrations'] = $this->company->bank_integrations()->withTrashed()->orderBy('id', 'ASC')->cursor()->map(function ($bank_integration) {
$bank_integration = $this->transformArrayOfKeys($bank_integration, ['account_id','company_id', 'user_id']); $bank_integration = $this->transformArrayOfKeys($bank_integration, ['account_id','company_id', 'user_id']);
return $bank_integration->makeVisible(['id','user_id','company_id','account_id']); return $bank_integration->makeVisible(['id','user_id','company_id','account_id','hashed_id']);
})->all(); })->all();
$this->export_data['bank_transactions'] = $this->company->bank_transactions()->orderBy('id', 'ASC')->cursor()->map(function ($bank_transaction) { $this->export_data['bank_transactions'] = $this->company->bank_transactions()->withTrashed()->orderBy('id', 'ASC')->cursor()->map(function ($bank_transaction) {
$bank_transaction = $this->transformArrayOfKeys($bank_transaction, ['company_id', 'user_id','bank_integration_id','expense_id','category_id','ninja_category_id','vendor_id']); $bank_transaction = $this->transformArrayOfKeys($bank_transaction, ['company_id', 'user_id','bank_integration_id','expense_id','ninja_category_id','vendor_id']);
return $bank_transaction->makeVisible(['id','user_id','company_id']); return $bank_transaction->makeVisible(['id','user_id','company_id']);
})->all(); })->all();
$this->export_data['schedulers'] = $this->company->schedulers()->orderBy('id', 'ASC')->cursor()->map(function ($scheduler) { $this->export_data['schedulers'] = $this->company->schedulers()->withTrashed()->orderBy('id', 'ASC')->cursor()->map(function ($scheduler) {
$scheduler = $this->transformArrayOfKeys($scheduler, ['company_id', 'user_id']); $scheduler = $this->transformArrayOfKeys($scheduler, ['company_id', 'user_id']);
return $scheduler->makeVisible(['id','user_id','company_id']); return $scheduler->makeVisible(['id','user_id','company_id']);

View File

@ -63,14 +63,12 @@ use App\Utils\Ninja;
use App\Utils\TempFile; use App\Utils\TempFile;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use function GuzzleHttp\json_encode;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use JsonMachine\JsonDecoder\ExtJsonDecoder; use JsonMachine\JsonDecoder\ExtJsonDecoder;
@ -142,7 +140,6 @@ class CompanyImport implements ShouldQueue
'recurring_expenses', 'recurring_expenses',
'expenses', 'expenses',
'tasks', 'tasks',
'payments',
'company_ledger', 'company_ledger',
'designs', 'designs',
'documents', 'documents',
@ -569,7 +566,7 @@ class CompanyImport implements ShouldQueue
['expenses' => 'expense_id'], ['expenses' => 'expense_id'],
['vendors' => 'vendor_id'], ['vendors' => 'vendor_id'],
['expense_categories' => 'ninja_category_id'], ['expense_categories' => 'ninja_category_id'],
['expense_categories' => 'category_id'], // ['expense_categories' => 'category_id'],
['bank_integrations' => 'bank_integration_id'] ['bank_integrations' => 'bank_integration_id']
], ],
'bank_transactions', 'bank_transactions',
@ -1143,7 +1140,34 @@ class CompanyImport implements ShouldQueue
continue; continue;
} }
$storage_url = (object)$this->getObject('storage_url', true);
if (!Storage::exists($document->url) && is_string($storage_url)) {
$url = $storage_url . $document->url;
$file = @file_get_contents($url);
if ($file) {
try {
Storage::disk(config('filesystems.default'))->put($document->url, $file);
} catch(\Exception $e) {
nlog($e->getMessage());
nlog("I could not upload {$document->url}");
}
}
else
continue;
}
else
continue;
$new_document = new Document(); $new_document = new Document();
$new_document->disk = config('filesystems.default');
$new_document->user_id = $this->transformId('users', $document->user_id); $new_document->user_id = $this->transformId('users', $document->user_id);
$new_document->assigned_user_id = $this->transformId('users', $document->assigned_user_id); $new_document->assigned_user_id = $this->transformId('users', $document->assigned_user_id);
$new_document->company_id = $this->company->id; $new_document->company_id = $this->company->id;
@ -1169,26 +1193,6 @@ class CompanyImport implements ShouldQueue
$new_document->save(['timestamps' => false]); $new_document->save(['timestamps' => false]);
$storage_url = (object)$this->getObject('storage_url', true);
if (!Storage::exists($new_document->url) && is_string($storage_url)) {
$url = $storage_url . $new_document->url;
$file = @file_get_contents($url);
if ($file) {
try {
Storage::disk(config('filesystems.default'))->put($new_document->url, $file);
$new_document->disk = config('filesystems.default');
$new_document->save();
} catch(\Exception $e) {
nlog($e->getMessage());
nlog("I could not upload {$new_document->url}");
$new_document->forceDelete();
}
}
}
} }
return $this; return $this;
@ -1727,7 +1731,9 @@ class CompanyImport implements ShouldQueue
*/ */
private function transformId(string $resource, ?string $old): ?int private function transformId(string $resource, ?string $old): ?int
{ {
if (empty($old)) {
// WjnegYbwZ1 == 0 return null;
if (empty($old) || $old == 'WjnegYbwZ1') {
return null; return null;
} }
@ -1736,6 +1742,7 @@ class CompanyImport implements ShouldQueue
} }
if (! array_key_exists($resource, $this->ids)) { if (! array_key_exists($resource, $this->ids)) {
$this->sendImportMail("The Import failed due to missing data in the import file. Resource {$resource} not available."); $this->sendImportMail("The Import failed due to missing data in the import file. Resource {$resource} not available.");
throw new \Exception("Resource {$resource} not available."); throw new \Exception("Resource {$resource} not available.");
@ -1744,16 +1751,12 @@ class CompanyImport implements ShouldQueue
if (! array_key_exists("{$old}", $this->ids[$resource])) { if (! array_key_exists("{$old}", $this->ids[$resource])) {
// nlog($this->ids[$resource]); // nlog($this->ids[$resource]);
nlog("searching for {$old} in {$resource}"); nlog("searching for {$old} in {$resource}");
nlog("If we are missing a user - default to the company owner");
if ($resource == 'users') { if ($resource == 'users') {
return $this->company_owner->id; return $this->company_owner->id;
} }
$this->sendImportMail("The Import failed due to missing data in the import file. Resource {$resource} not available."); $this->sendImportMail("The Import failed due to missing data in the import file. Key {$old} not found in {$resource}.");
nlog($this->ids[$resource]);
throw new \Exception("Missing {$resource} key: {$old}"); throw new \Exception("Missing {$resource} key: {$old}");
} }

View File

@ -105,7 +105,7 @@ class PaymentFailedMailer implements ShouldQueue
}); });
//add client payment failures here. //add client payment failures here.
//
if ($this->client->contacts()->whereNotNull('email')->exists() && $this->payment_hash) { if ($this->client->contacts()->whereNotNull('email')->exists() && $this->payment_hash) {
$contact = $this->client->contacts()->whereNotNull('email')->first(); $contact = $this->client->contacts()->whereNotNull('email')->first();

View File

@ -305,14 +305,20 @@ class ProcessPostmarkWebhook implements ShouldQueue
if($sl) { if($sl) {
$this->updateSystemLog($sl, $data); $this->updateSystemLog($sl, $data);
if (config('ninja.notification.slack')) {
$this->invitation->company->notification(new EmailSpamNotification($this->invitation->company->account))->ninja();
}
return; return;
} }
(new SystemLogger($data, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle(); (new SystemLogger($data, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle();
if (config('ninja.notification.slack')) { if (config('ninja.notification.slack')) {
$this->invitation->company->notification(new EmailSpamNotification($this->invitation->company->account))->ninja(); $this->invitation->company->notification(new EmailSpamNotification($this->invitation->company->account))->ninja();
} }
} }
private function discoverInvitation($message_id) private function discoverInvitation($message_id)

View File

@ -307,8 +307,6 @@ class ReminderJob implements ShouldQueue
/**Refresh Invoice values*/ /**Refresh Invoice values*/
$invoice = $invoice->calc()->getInvoice(); $invoice = $invoice->calc()->getInvoice();
// nlog('adjusting client balance and invoice balance by #'.$invoice->number.' '.($invoice->balance - $temp_invoice_balance));
// $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance);
$invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}"); $invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
$invoice->client->service()->calculateBalance(); $invoice->client->service()->calculateBalance();

View File

@ -113,6 +113,10 @@ class InstantBankPay implements MethodInterface
return $this->processSuccessfulPayment($payment); return $this->processSuccessfulPayment($payment);
} }
if ($billing_request->status === 'submitted') {
return $this->processPendingPayment($payment);
}
return $this->processUnsuccessfulPayment($payment); return $this->processUnsuccessfulPayment($payment);
} catch (\Exception $exception) { } catch (\Exception $exception) {
throw new PaymentFailed( throw new PaymentFailed(
@ -125,7 +129,40 @@ class InstantBankPay implements MethodInterface
/** /**
* Handle pending payments for Instant Bank Transfer. * Handle pending payments for Instant Bank Transfer.
* *
* @param ResourcesPayment $payment * @param \GoCardlessPro\Resources\Payment $payment
* @param array $data
* @return RedirectResponse
*/
public function processPendingPayment(\GoCardlessPro\Resources\Payment $payment, array $data = [])
{
$data = [
'payment_method' => $payment->links->mandate,
'payment_type' => PaymentType::INSTANT_BANK_PAY,
'amount' => $this->go_cardless->payment_hash->data->amount_with_fee,
'transaction_reference' => $payment->id,
'gateway_type_id' => GatewayType::INSTANT_BANK_PAY,
];
$payment = $this->go_cardless->createPayment($data, Payment::STATUS_PENDING);
SystemLogger::dispatch(
['response' => $payment, 'data' => $data],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_GOCARDLESS,
$this->go_cardless->client,
$this->go_cardless->client->company,
);
return redirect()->route('client.payments.show', ['payment' => $this->go_cardless->encodePrimaryKey($payment->id)]);
}
/**
* Handle pending payments for Instant Bank Transfer.
*
* @param \GoCardlessPro\Resources\Payment $payment
* @param array $data * @param array $data
* @return RedirectResponse * @return RedirectResponse
*/ */
@ -163,12 +200,12 @@ class InstantBankPay implements MethodInterface
{ {
PaymentFailureMailer::dispatch($this->go_cardless->client, $payment->status, $this->go_cardless->client->company, $this->go_cardless->payment_hash->data->amount_with_fee); PaymentFailureMailer::dispatch($this->go_cardless->client, $payment->status, $this->go_cardless->client->company, $this->go_cardless->payment_hash->data->amount_with_fee);
PaymentFailureMailer::dispatch( // PaymentFailureMailer::dispatch(
$this->go_cardless->client, // $this->go_cardless->client,
$payment, // $payment,
$this->go_cardless->client->company, // $this->go_cardless->client->company,
$payment->amount // $payment->amount
); // );
$message = [ $message = [
'server_response' => $payment, 'server_response' => $payment,

View File

@ -165,7 +165,7 @@ class GoCardlessPaymentDriver extends BaseDriver
], ],
]); ]);
if ($payment->status === 'pending_submission') { if (in_array($payment->status, ['submitted', 'pending_submission'])) {
$this->confirmGatewayFee(); $this->confirmGatewayFee();
$data = [ $data = [

View File

@ -70,7 +70,7 @@ class EmailReport
'report_keys' => [] 'report_keys' => []
]; ];
if (count($this->scheduler->parameters['clients']) >= 1) { if (isset($this->scheduler->parameters['clients']) && count($this->scheduler->parameters['clients']) >= 1) {
$data['clients'] = $this->transformKeys($this->scheduler->parameters['clients']); $data['clients'] = $this->transformKeys($this->scheduler->parameters['clients']);
} }
@ -84,20 +84,20 @@ class EmailReport
'client_balance' => $export = (new ClientBalanceReport($this->scheduler->company, $data)), 'client_balance' => $export = (new ClientBalanceReport($this->scheduler->company, $data)),
'client_sales' => $export = (new ClientSalesReport($this->scheduler->company, $data)), 'client_sales' => $export = (new ClientSalesReport($this->scheduler->company, $data)),
'user_sales' => $export = (new UserSalesReport($this->scheduler->company, $data)), 'user_sales' => $export = (new UserSalesReport($this->scheduler->company, $data)),
'clients' => $export = (new ClientExport($this->scheduler->company, $data)),
'client_contacts' => $export = (new ContactExport($this->scheduler->company, $data)),
'credits' => $export = (new CreditExport($this->scheduler->company, $data)),
'documents' => $export = (new DocumentExport($this->scheduler->company, $data)),
'expenses' => $export = (new ExpenseExport($this->scheduler->company, $data)),
'invoices' => $export = (new InvoiceExport($this->scheduler->company, $data)),
'invoice_items' => $export = (new InvoiceItemExport($this->scheduler->company, $data)),
'quotes' => $export = (new QuoteExport($this->scheduler->company, $data)),
'quote_items' => $export = (new QuoteItemExport($this->scheduler->company, $data)),
'recurring_invoices' => $export = (new RecurringInvoiceExport($this->scheduler->company, $data)),
'payments' => $export = (new PaymentExport($this->scheduler->company, $data)),
'products' => $export = (new ProductExport($this->scheduler->company, $data)),
'tasks' => $export = (new TaskExport($this->scheduler->company, $data)),
'profitloss' => $export = (new ProfitLoss($this->scheduler->company, $data)), 'profitloss' => $export = (new ProfitLoss($this->scheduler->company, $data)),
'client' => $export = (new ClientExport($this->scheduler->company, $data)),
'client_contact' => $export = (new ContactExport($this->scheduler->company, $data)),
'credit' => $export = (new CreditExport($this->scheduler->company, $data)),
'document' => $export = (new DocumentExport($this->scheduler->company, $data)),
'expense' => $export = (new ExpenseExport($this->scheduler->company, $data)),
'invoice' => $export = (new InvoiceExport($this->scheduler->company, $data)),
'invoice_item' => $export = (new InvoiceItemExport($this->scheduler->company, $data)),
'quote' => $export = (new QuoteExport($this->scheduler->company, $data)),
'quote_item' => $export = (new QuoteItemExport($this->scheduler->company, $data)),
'recurring_invoice' => $export = (new RecurringInvoiceExport($this->scheduler->company, $data)),
'payment' => $export = (new PaymentExport($this->scheduler->company, $data)),
'product' => $export = (new ProductExport($this->scheduler->company, $data)),
'task' => $export = (new TaskExport($this->scheduler->company, $data)),
default => $export = false, default => $export = false,
}; };

86
composer.lock generated
View File

@ -485,16 +485,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.293.5", "version": "3.293.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "f2002e52b382b45231da3f9552033f769acfebd8" "reference": "3bf86ba8b9bbea2b298f89e6f5edc58de276690b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/f2002e52b382b45231da3f9552033f769acfebd8", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3bf86ba8b9bbea2b298f89e6f5edc58de276690b",
"reference": "f2002e52b382b45231da3f9552033f769acfebd8", "reference": "3bf86ba8b9bbea2b298f89e6f5edc58de276690b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -574,9 +574,9 @@
"support": { "support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.293.5" "source": "https://github.com/aws/aws-sdk-php/tree/3.293.7"
}, },
"time": "2023-12-06T19:09:15+00:00" "time": "2023-12-08T19:11:21+00:00"
}, },
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -6576,16 +6576,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "2.72.0", "version": "2.72.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/briannesbitt/Carbon.git", "url": "https://github.com/briannesbitt/Carbon.git",
"reference": "a6885fcbad2ec4360b0e200ee0da7d9b7c90786b" "reference": "2b3b3db0a2d0556a177392ff1a3bf5608fa09f78"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/a6885fcbad2ec4360b0e200ee0da7d9b7c90786b", "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2b3b3db0a2d0556a177392ff1a3bf5608fa09f78",
"reference": "a6885fcbad2ec4360b0e200ee0da7d9b7c90786b", "reference": "2b3b3db0a2d0556a177392ff1a3bf5608fa09f78",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6679,7 +6679,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-11-28T10:13:25+00:00" "time": "2023-12-08T23:47:49+00:00"
}, },
{ {
"name": "nette/schema", "name": "nette/schema",
@ -6831,16 +6831,16 @@
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.17.1", "version": "v4.18.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6881,9 +6881,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0"
}, },
"time": "2023-08-13T19:53:39+00:00" "time": "2023-12-10T21:03:43+00:00"
}, },
{ {
"name": "nunomaduro/termwind", "name": "nunomaduro/termwind",
@ -13841,23 +13841,23 @@
}, },
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
"version": "2.2.6", "version": "v2.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
"reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb",
"reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-dom": "*", "ext-dom": "*",
"ext-libxml": "*", "ext-libxml": "*",
"php": "^5.5 || ^7.0 || ^8.0", "php": "^5.5 || ^7.0 || ^8.0",
"symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10"
@ -13888,9 +13888,9 @@
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
"support": { "support": {
"issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
"source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7"
}, },
"time": "2023-01-03T09:29:04+00:00" "time": "2023-12-08T13:03:43+00:00"
}, },
{ {
"name": "turbo124/beacon", "name": "turbo124/beacon",
@ -15263,16 +15263,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.40.2", "version": "v3.41.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4344562a516b76afe8f2d64b2e52214c30d64ed8" "reference": "8b6ae8dcbaf23f09680643ab832a4a3a260265f6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4344562a516b76afe8f2d64b2e52214c30d64ed8", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/8b6ae8dcbaf23f09680643ab832a4a3a260265f6",
"reference": "4344562a516b76afe8f2d64b2e52214c30d64ed8", "reference": "8b6ae8dcbaf23f09680643ab832a4a3a260265f6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -15302,8 +15302,6 @@
"php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4",
"phpspec/prophecy": "^1.17",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.6", "phpunit/phpunit": "^9.6",
"symfony/phpunit-bridge": "^6.3.8 || ^7.0", "symfony/phpunit-bridge": "^6.3.8 || ^7.0",
"symfony/yaml": "^5.4 || ^6.0 || ^7.0" "symfony/yaml": "^5.4 || ^6.0 || ^7.0"
@ -15344,7 +15342,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.40.2" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.41.1"
}, },
"funding": [ "funding": [
{ {
@ -15352,7 +15350,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-12-03T09:21:33+00:00" "time": "2023-12-10T19:59:27+00:00"
}, },
{ {
"name": "hamcrest/hamcrest-php", "name": "hamcrest/hamcrest-php",
@ -15633,16 +15631,16 @@
}, },
{ {
"name": "mockery/mockery", "name": "mockery/mockery",
"version": "1.6.6", "version": "1.6.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mockery/mockery.git", "url": "https://github.com/mockery/mockery.git",
"reference": "b8e0bb7d8c604046539c1115994632c74dcb361e" "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/b8e0bb7d8c604046539c1115994632c74dcb361e", "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"reference": "b8e0bb7d8c604046539c1115994632c74dcb361e", "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -15655,9 +15653,7 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^8.5 || ^9.6.10", "phpunit/phpunit": "^8.5 || ^9.6.10",
"psalm/plugin-phpunit": "^0.18.4", "symplify/easy-coding-standard": "^12.0.8"
"symplify/easy-coding-standard": "^11.5.0",
"vimeo/psalm": "^4.30"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -15714,7 +15710,7 @@
"security": "https://github.com/mockery/mockery/security/advisories", "security": "https://github.com/mockery/mockery/security/advisories",
"source": "https://github.com/mockery/mockery" "source": "https://github.com/mockery/mockery"
}, },
"time": "2023-08-09T00:03:52+00:00" "time": "2023-12-10T02:24:34+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@ -16071,16 +16067,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.10.47", "version": "1.10.48",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "84dbb33b520ea28b6cf5676a3941f4bae1c1ff39" "reference": "087ed4b5f4a7a6e8f3bbdfbfe98ce5c181380bc6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/84dbb33b520ea28b6cf5676a3941f4bae1c1ff39", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/087ed4b5f4a7a6e8f3bbdfbfe98ce5c181380bc6",
"reference": "84dbb33b520ea28b6cf5676a3941f4bae1c1ff39", "reference": "087ed4b5f4a7a6e8f3bbdfbfe98ce5c181380bc6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -16129,7 +16125,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-12-01T15:19:17+00:00" "time": "2023-12-08T14:34:28+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@ -17,8 +17,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => env('APP_VERSION', '5.7.59'), 'app_version' => env('APP_VERSION', '5.7.60'),
'app_tag' => env('APP_TAG', '5.7.59'), 'app_tag' => env('APP_TAG', '5.7.60'),
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false), 'api_secret' => env('API_SECRET', false),

View File

@ -28,8 +28,20 @@ class DomainCheckTest extends TestCase
public function testDomainCheck() public function testDomainCheck()
{ {
$this->assertTrue(in_array('yopmail.com', Domains::getDomains())); $this->assertTrue(in_array('yopmail.com', \App\DataProviders\Domains::getDomains()));
$this->assertFalse(in_array('invoiceninja.com', Domains::getDomains())); $this->assertFalse(in_array('invoiceninja.com', \App\DataProviders\Domains::getDomains()));
} }
public function testSubdomainValidation()
{
$this->assertFalse($this->checker('invoiceninja'));
$this->assertFalse($this->checker('hello'));
$this->assertTrue($this->checker('nasty.pasty'));
}
public function checker($subdomain)
{
return (!preg_match('/^[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?$/', $subdomain));
}
} }