From 5ddbc0f50734383b8e993c611ea63c9ce325b78b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 2 Oct 2021 11:10:12 +1000 Subject: [PATCH 1/5] Fixes for recurring crons --- app/Console/Commands/PostUpdate.php | 2 +- app/Jobs/RecurringInvoice/SendRecurring.php | 5 +++++ app/Mail/SupportMessageSent.php | 2 +- app/Observers/InvoiceObserver.php | 5 +++++ app/Observers/QuoteObserver.php | 5 +++++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/PostUpdate.php b/app/Console/Commands/PostUpdate.php index b8d57ff26400..1e5025dac200 100644 --- a/app/Console/Commands/PostUpdate.php +++ b/app/Console/Commands/PostUpdate.php @@ -50,7 +50,7 @@ class PostUpdate extends Command info("I wasn't able to migrate the data."); } - nlog("finished migrating"); + info("finished migrating"); $output = []; diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index 9ee37c268b14..df7d0dcc40bb 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -72,6 +72,11 @@ class SendRecurring implements ShouldQueue // Generate Standard Invoice $invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client); + if($this->recurring_invoice->auto_bill == "always") + $invoice->auto_bill_enabled = true; + elseif($this->recurring_invoice->auto_bill == "off") + $invoice->auto_bill_enabled = false; + $invoice->date = now()->format('Y-m-d'); $invoice->due_date = $this->recurring_invoice->calculateDueDate(now()->format('Y-m-d')); $invoice->recurring_id = $this->recurring_invoice->id; diff --git a/app/Mail/SupportMessageSent.php b/app/Mail/SupportMessageSent.php index b326ba45086d..7904eb9d916a 100644 --- a/app/Mail/SupportMessageSent.php +++ b/app/Mail/SupportMessageSent.php @@ -64,7 +64,7 @@ class SupportMessageSent extends Mailable $db = str_replace("db-ninja-", "", $company->db); $is_large = $company->is_large ? "L" : "S"; $platform = array_key_exists('platform', $this->data) ? $this->data['platform'] : "U"; - $migrated = strlen($company->company_key) == 32 ? "M" : ""; + $migrated = strlen($company->company_key) == 32 ? "M" : "T"; if(Ninja::isHosted()) $subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated} :: {$plan} :: ".date('M jS, g:ia'); diff --git a/app/Observers/InvoiceObserver.php b/app/Observers/InvoiceObserver.php index 609d1e912291..c7da824f246d 100644 --- a/app/Observers/InvoiceObserver.php +++ b/app/Observers/InvoiceObserver.php @@ -34,6 +34,8 @@ class InvoiceObserver ->where('event_id', Webhook::EVENT_CREATE_INVOICE) ->exists(); + $invoice->load('client'); + if ($subscriptions) { WebhookHandler::dispatch(Webhook::EVENT_CREATE_INVOICE, $invoice, $invoice->company); } @@ -51,6 +53,9 @@ class InvoiceObserver ->where('event_id', Webhook::EVENT_UPDATE_INVOICE) ->exists(); + $invoice->load('client'); + + if ($subscriptions) { WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company); } diff --git a/app/Observers/QuoteObserver.php b/app/Observers/QuoteObserver.php index 7f54296ef8e9..89c64a603e2b 100644 --- a/app/Observers/QuoteObserver.php +++ b/app/Observers/QuoteObserver.php @@ -30,6 +30,8 @@ class QuoteObserver ->where('event_id', Webhook::EVENT_CREATE_QUOTE) ->exists(); + $quote->load('client'); + if ($subscriptions) { WebhookHandler::dispatch(Webhook::EVENT_CREATE_QUOTE, $quote, $quote->company); } @@ -47,6 +49,9 @@ class QuoteObserver ->where('event_id', Webhook::EVENT_UPDATE_QUOTE) ->exists(); + $quote->load('client'); + + if ($subscriptions) { WebhookHandler::dispatch(Webhook::EVENT_UPDATE_QUOTE, $quote, $quote->company); } From 63e6b1c26d076d1963871afad9859cdbe24bb7b8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 2 Oct 2021 12:49:18 +1000 Subject: [PATCH 2/5] Improve queries for recurring objects to exclusive disabled companies --- app/Jobs/Cron/AutoBillCron.php | 12 ++++++++++ app/Jobs/Cron/RecurringExpensesCron.php | 3 +++ app/Jobs/Cron/RecurringInvoicesCron.php | 29 +++++++++++++++---------- app/Jobs/Util/ReminderJob.php | 3 +++ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/app/Jobs/Cron/AutoBillCron.php b/app/Jobs/Cron/AutoBillCron.php index 0b852cb4806b..b84203681fa3 100644 --- a/app/Jobs/Cron/AutoBillCron.php +++ b/app/Jobs/Cron/AutoBillCron.php @@ -52,6 +52,9 @@ class AutoBillCron ->where('auto_bill_enabled', true) ->where('balance', '>', 0) ->where('is_deleted', false) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company'); nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill"); @@ -65,6 +68,9 @@ class AutoBillCron ->where('auto_bill_enabled', true) ->where('balance', '>', 0) ->where('is_deleted', false) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company'); nlog($auto_bill_invoices->count(). " full invoices to auto bill"); @@ -85,6 +91,9 @@ class AutoBillCron ->where('auto_bill_enabled', true) ->where('balance', '>', 0) ->where('is_deleted', false) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company'); nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill db = {$db}"); @@ -98,6 +107,9 @@ class AutoBillCron ->where('auto_bill_enabled', true) ->where('balance', '>', 0) ->where('is_deleted', false) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company'); nlog($auto_bill_invoices->count(). " full invoices to auto bill db = {$db}"); diff --git a/app/Jobs/Cron/RecurringExpensesCron.php b/app/Jobs/Cron/RecurringExpensesCron.php index 78a59aec6bff..8af8f9aef352 100644 --- a/app/Jobs/Cron/RecurringExpensesCron.php +++ b/app/Jobs/Cron/RecurringExpensesCron.php @@ -66,6 +66,9 @@ class RecurringExpensesCron ->whereNull('deleted_at') ->where('status_id', RecurringInvoice::STATUS_ACTIVE) ->where('remaining_cycles', '!=', '0') + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company') ->cursor(); diff --git a/app/Jobs/Cron/RecurringInvoicesCron.php b/app/Jobs/Cron/RecurringInvoicesCron.php index 14e9c1cfa56f..bff131da8838 100644 --- a/app/Jobs/Cron/RecurringInvoicesCron.php +++ b/app/Jobs/Cron/RecurringInvoicesCron.php @@ -53,23 +53,27 @@ class RecurringInvoicesCron $query->where('is_deleted',0) ->where('deleted_at', NULL); }) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company') ->cursor(); nlog(now()->format('Y-m-d') . ' Sending Recurring Invoices. Count = '.$recurring_invoices->count()); $recurring_invoices->each(function ($recurring_invoice, $key) { + nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date); - if (!$recurring_invoice->company->is_disabled) { - - try{ - SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db); - } - catch(\Exception $e){ - nlog("Unable to sending recurring invoice {$recurring_invoice->id}"); - } + nlog("Trying to send {$recurring_invoice->number}"); + + try{ + SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db); } + catch(\Exception $e){ + nlog("Unable to sending recurring invoice {$recurring_invoice->id}"); + } + }); } else { //multiDB environment, need to @@ -86,6 +90,9 @@ class RecurringInvoicesCron $query->where('is_deleted',0) ->where('deleted_at', NULL); }) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('company') ->cursor(); @@ -94,15 +101,15 @@ class RecurringInvoicesCron $recurring_invoices->each(function ($recurring_invoice, $key) { nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date ." Recurring #id = ". $recurring_invoice->id); - if (!$recurring_invoice->company->is_disabled) { - + nlog("Trying to send {$recurring_invoice->number}"); + try{ SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db); } catch(\Exception $e){ nlog("Unable to sending recurring invoice {$recurring_invoice->id}"); } - } + }); } } diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index b468fb097c48..55b0ae690703 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -70,6 +70,9 @@ class ReminderJob implements ShouldQueue $query->where('is_deleted',0) ->where('deleted_at', NULL); }) + ->whereHas('company', function ($query) { + $query->where('is_disabled',0); + }) ->with('invitations')->cursor()->each(function ($invoice) { if ($invoice->isPayable()) { From a679ec1f3cc3e0e8ba528b2a36d4ea7cf0f1c71f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 2 Oct 2021 18:46:03 +1000 Subject: [PATCH 3/5] Reschedule recurring expenses just after +0 UTC --- app/Console/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 7e32ab6de533..881c9eacc618 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -63,7 +63,7 @@ class Kernel extends ConsoleKernel $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping(); - $schedule->job(new RecurringExpensesCron)->dailyAt('23:45')->withoutOverlapping(); + $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping(); $schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping(); From 773fc3f29bace7a5620f4ad9a18984ec8dac286e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 3 Oct 2021 08:31:21 +1100 Subject: [PATCH 4/5] Improve efficiency of MultiDB queries --- app/Libraries/MultiDB.php | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/app/Libraries/MultiDB.php b/app/Libraries/MultiDB.php index c3f3dc5c4abd..48d95d781d61 100644 --- a/app/Libraries/MultiDB.php +++ b/app/Libraries/MultiDB.php @@ -11,6 +11,7 @@ namespace App\Libraries; +use App\Models\Account; use App\Models\Client; use App\Models\ClientContact; use App\Models\Company; @@ -178,7 +179,7 @@ class MultiDB $current_db = config('database.default'); foreach (self::$dbs as $db) { - if ($ct = ClientContact::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) { + if (ClientContact::on($db)->whereRaw('BINARY `token`= ?', [$token])->exists()) { self::setDb($db); return true; } @@ -230,8 +231,8 @@ class MultiDB $current_db = config('database.default'); foreach (self::$dbs as $db) { - if ($ct = CompanyToken::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) { - self::setDb($ct->company->db); + if (CompanyToken::on($db)->whereRaw('BINARY `token`= ?', [$token])->exists()) { + self::setDb($db); return true; } } @@ -246,8 +247,24 @@ class MultiDB $current_db = config('database.default'); foreach (self::$dbs as $db) { - if ($company = Company::on($db)->where('company_key', $company_key)->first()) { - self::setDb($company->db); + if (Company::on($db)->where('company_key', $company_key)->exists()) { + self::setDb($db); + return true; + } + } + + self::setDB($current_db); + + return false; + } + + public static function findAndSetDbByAccountKey($account_key) :bool + { + $current_db = config('database.default'); + + foreach (self::$dbs as $db) { + if (Account::on($db)->where('key', $account_key)->exists()) { + self::setDb($db); return true; } } @@ -262,8 +279,8 @@ class MultiDB $current_db = config('database.default'); foreach (self::$dbs as $db) { - if ($client_contact = ClientContact::on($db)->where('contact_key', $contact_key)->first()) { - self::setDb($client_contact->company->db); + if (ClientContact::on($db)->where('contact_key', $contact_key)->exists()) { + self::setDb($db); return true; } } @@ -278,8 +295,8 @@ class MultiDB $current_db = config('database.default'); foreach (self::$dbs as $db) { - if ($client = Client::on($db)->where('client_hash', $client_hash)->first()) { - self::setDb($client->company->db); + if (Client::on($db)->where('client_hash', $client_hash)->exists()) { + self::setDb($db); return true; } } @@ -299,7 +316,7 @@ class MultiDB foreach (self::$dbs as $db) { if ($company = Company::on($db)->where($query_array)->first()) { - self::setDb($company->db); + self::setDb($db); return $company; } } @@ -315,7 +332,7 @@ class MultiDB $current_db = config('database.default'); foreach (self::$dbs as $db) { - if ($invite = $class::on($db)->whereRaw('BINARY `key`= ?', [$invitation_key])->first()) { + if ($invite = $class::on($db)->whereRaw('BINARY `key`= ?', [$invitation_key])->exists()) { self::setDb($db); return true; } From 6c1ba6d40b588618f8dd0131f7af48643cd39bf1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 3 Oct 2021 13:36:43 +1100 Subject: [PATCH 5/5] Minor fixes for Square --- .../ClientPortal/NinjaPlanController.php | 16 +++-- app/PaymentDrivers/Square/CreditCard.php | 4 ++ app/PaymentDrivers/WePay/ACH.php | 59 +++++++++++-------- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/NinjaPlanController.php b/app/Http/Controllers/ClientPortal/NinjaPlanController.php index 507f874790db..8080145d7504 100644 --- a/app/Http/Controllers/ClientPortal/NinjaPlanController.php +++ b/app/Http/Controllers/ClientPortal/NinjaPlanController.php @@ -15,6 +15,7 @@ namespace App\Http\Controllers\ClientPortal; use App\Http\Controllers\Controller; use App\Http\Requests\ClientPortal\Uploads\StoreUploadRequest; use App\Libraries\MultiDB; +use App\Models\Account; use App\Models\ClientContact; use App\Models\Company; use App\Utils\Ninja; @@ -26,14 +27,21 @@ use Illuminate\Support\Facades\Auth; class NinjaPlanController extends Controller { - public function index(string $contact_key, string $company_key) + public function index(string $contact_key, string $account_or_company_key) { - MultiDB::findAndSetDbByCompanyKey($company_key); - $company = Company::where('company_key', $company_key)->first(); + MultiDB::findAndSetDbByCompanyKey($account_or_company_key); + $company = Company::where('company_key', $account_or_company_key)->first(); + + if(!$company){ + MultiDB::findAndSetDbByAccountKey($account_or_company_key); + $account = Account::where('key', $account_or_company_key)->first(); + } + else + $account = $company->account; + nlog("Ninja Plan Controller Company key found {$company->company_key}"); - $account = $company->account; if (MultiDB::findAndSetDbByContactKey($contact_key) && $client_contact = ClientContact::where('contact_key', $contact_key)->first()) { diff --git a/app/PaymentDrivers/Square/CreditCard.php b/app/PaymentDrivers/Square/CreditCard.php index b5bf6eec93ef..63a581472133 100644 --- a/app/PaymentDrivers/Square/CreditCard.php +++ b/app/PaymentDrivers/Square/CreditCard.php @@ -270,6 +270,10 @@ class CreditCard if ($api_response->isSuccess()) { $customers = $api_response->getBody(); $customers = json_decode($customers); + + if(count(array($api_response->getBody(),1)) == 0) + $customers = false; + } else { $errors = $api_response->getErrors(); } diff --git a/app/PaymentDrivers/WePay/ACH.php b/app/PaymentDrivers/WePay/ACH.php index 6de6498c2ea0..3a8a14b5ad8e 100644 --- a/app/PaymentDrivers/WePay/ACH.php +++ b/app/PaymentDrivers/WePay/ACH.php @@ -60,12 +60,16 @@ class ACH 'method' => '1', */ - $response = $this->wepay_payment_driver->wepay->request('payment_bank/persist', [ - 'client_id' => config('ninja.wepay.client_id'), - 'client_secret' => config('ninja.wepay.client_secret'), - 'payment_bank_id' => (int)$data['bank_account_id'], - ]); - + try{ + $response = $this->wepay_payment_driver->wepay->request('payment_bank/persist', [ + 'client_id' => config('ninja.wepay.client_id'), + 'client_secret' => config('ninja.wepay.client_secret'), + 'payment_bank_id' => (int)$data['bank_account_id'], + ]); + } + catch(\Exception $e){ + throw new PaymentFailed($e->getMessage(), 400); + } // display the response // nlog($response); @@ -202,26 +206,31 @@ class ACH $app_fee = (config('ninja.wepay.fee_ach_multiplier') * $this->wepay_payment_driver->payment_hash->data->amount_with_fee) + config('ninja.wepay.fee_fixed'); - $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( - // 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), - 'unique_id' => Str::random(40), - 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), - 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, - 'currency' => $this->wepay_payment_driver->client->getCurrencyCode(), - 'short_description' => 'Goods and Services', - 'type' => 'goods', - 'fee' => [ - 'fee_payer' => config('ninja.wepay.fee_payer'), - 'app_fee' => $app_fee, - ], - 'payment_method' => array( - 'type' => 'payment_bank', - 'payment_bank' => array( - 'id' => $token->token + try{ + $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( + // 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), + 'unique_id' => Str::random(40), + 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), + 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, + 'currency' => $this->wepay_payment_driver->client->getCurrencyCode(), + 'short_description' => 'Goods and Services', + 'type' => 'goods', + 'fee' => [ + 'fee_payer' => config('ninja.wepay.fee_payer'), + 'app_fee' => $app_fee, + ], + 'payment_method' => array( + 'type' => 'payment_bank', + 'payment_bank' => array( + 'id' => $token->token + ) ) - ) - )); - + )); + } + catch(\Exception $e){ + throw new PaymentFailed($e->getMessage(), 500); + } + /* Merge all data and store in the payment hash*/ $state = [ 'server_response' => $response,