From 37aac0db0318aa85d160d94c4dc2a2a9a8ebcdd7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 18 Oct 2021 13:31:21 +1100 Subject: [PATCH 1/7] Fixes for converting quotes to invoices - invitations --- app/Jobs/RecurringInvoice/SendRecurring.php | 43 ++++++++++++++------- app/Repositories/BaseRepository.php | 18 ++++----- app/Services/Quote/ConvertQuote.php | 36 +++++++++++++++-- app/Services/Quote/QuoteService.php | 2 +- app/Services/Recurring/RecurringService.php | 2 - 5 files changed, 73 insertions(+), 28 deletions(-) diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index 441c4f2b5769..cd150fdb9662 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -13,12 +13,14 @@ namespace App\Jobs\RecurringInvoice; use App\DataMapper\Analytics\SendRecurringFailure; use App\Events\Invoice\InvoiceWasEmailed; +use App\Factory\InvoiceInvitationFactory; use App\Factory\RecurringInvoiceToInvoiceFactory; use App\Jobs\Entity\EmailEntity; use App\Models\Invoice; use App\Models\RecurringInvoice; use App\Utils\Ninja; use App\Utils\Traits\GeneratesCounter; +use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesInvoiceValues; use Carbon\Carbon; use Illuminate\Bus\Queueable; @@ -32,7 +34,8 @@ class SendRecurring implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use GeneratesCounter; - + use MakesHash; + public $recurring_invoice; protected $db; @@ -58,16 +61,6 @@ class SendRecurring implements ShouldQueue */ public function handle() : void { - //reset all contacts here - // $this->recurring_invoice->client->contacts()->update(['send_email' => false]); - - // $this->recurring_invoice->invitations->each(function ($invitation){ - - // $contact = $invitation->contact; - // $contact->send_email = true; - // $contact->save(); - - // }); // Generate Standard Invoice $invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client); @@ -86,10 +79,12 @@ class SendRecurring implements ShouldQueue $invoice = $invoice->service() ->markSent() ->applyNumber() - ->createInvitations() //need to only link invitations to those in the recurring invoice + // ->createInvitations() //need to only link invitations to those in the recurring invoice ->fillDefaults() ->save(); - + + $invoice = $this->createRecurringInvitations($invoice); + } else{ @@ -154,6 +149,28 @@ class SendRecurring implements ShouldQueue } + /** + * Only create the invitations that are defined on the recurring invoice. + * @param Invoice $invoice + * @return Invoice $invoice + */ + private function createRecurringInvitations($invoice) :Invoice + { + + $this->recurring_invoice->invitations->each(function ($recurring_invitation) use($invoice){ + + $ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); + $ii->key = $this->createDbHash(config('database.default')); + $ii->invoice_id = $invoice->id; + $ii->client_contact_id = $recurring_invitation->client_contact_id; + $ii->save(); + + }); + + + return $invoice->fresh(); + } + public function failed($exception = null) { nlog('the job failed'); diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index d75e03f7e6f6..e122fa3110c7 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -222,15 +222,15 @@ class BaseRepository $this->saveDocuments($data['documents'], $model); /* Marks whether the client contact should receive emails based on the send_email property */ - if (isset($data['client_contacts'])) { - foreach ($data['client_contacts'] as $contact) { - if ($contact['send_email'] == 1 && is_string($contact['id'])) { - $client_contact = ClientContact::find($this->decodePrimaryKey($contact['id'])); - $client_contact->send_email = true; - $client_contact->save(); - } - } - } + // if (isset($data['client_contacts'])) { + // foreach ($data['client_contacts'] as $contact) { + // if ($contact['send_email'] == 1 && is_string($contact['id'])) { + // $client_contact = ClientContact::find($this->decodePrimaryKey($contact['id'])); + // $client_contact->send_email = true; + // $client_contact->save(); + // } + // } + // } /* If invitations are present we need to filter existing invitations with the new ones */ if (isset($data['invitations'])) { diff --git a/app/Services/Quote/ConvertQuote.php b/app/Services/Quote/ConvertQuote.php index 23631b5baa01..12915ce54f24 100644 --- a/app/Services/Quote/ConvertQuote.php +++ b/app/Services/Quote/ConvertQuote.php @@ -13,6 +13,8 @@ namespace App\Services\Quote; use App\Factory\CloneQuoteToInvoiceFactory; +use App\Factory\InvoiceInvitationFactory; +use App\Models\Invoice; use App\Models\Quote; use App\Repositories\InvoiceRepository; use App\Utils\Traits\MakesHash; @@ -39,14 +41,20 @@ class ConvertQuote { $invoice = CloneQuoteToInvoiceFactory::create($quote, $quote->user_id); $invoice->design_id = $this->decodePrimaryKey($this->client->getSetting('invoice_design_id')); - $invoice = $this->invoice_repo->save($invoice->toArray(), $invoice); + + //create invitations here before the repo save() + //we need to do this here otherwise the repo_save will create + //invitations for ALL contacts + $invites = $this->createConversionInvitations($invoice, $quote); + $invoice_array = $invoice->toArray(); + $invoice_array['invitations'] = $invites; + + $invoice = $this->invoice_repo->save($invoice_array, $invoice); $invoice->fresh(); $invoice->service() ->fillDefaults() - // ->markSent() - // ->createInvitations() ->save(); $quote->invoice_id = $invoice->id; @@ -56,4 +64,26 @@ class ConvertQuote // maybe should return invoice here return $invoice; } + + /** + * Only create the invitations that are defined on the quote. + * + * @return Invoice $invoice + */ + private function createConversionInvitations($invoice, $quote) + { + $invites = []; + + foreach($quote->invitations as $quote_invitation){ + + $ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); + $ii->key = $this->createDbHash(config('database.default')); + $ii->client_contact_id = $quote_invitation->client_contact_id; + + $invites[] = $ii; + } + + return $invites; + } + } diff --git a/app/Services/Quote/QuoteService.php b/app/Services/Quote/QuoteService.php index b8865b8d2abf..a16210bf5be1 100644 --- a/app/Services/Quote/QuoteService.php +++ b/app/Services/Quote/QuoteService.php @@ -12,6 +12,7 @@ namespace App\Services\Quote; use App\Events\Quote\QuoteWasApproved; +use App\Factory\InvoiceInvitationFactory; use App\Jobs\Util\UnlinkFile; use App\Models\Invoice; use App\Models\Quote; @@ -117,7 +118,6 @@ class QuoteService $this->invoice ->service() ->markSent() - ->createInvitations() ->deletePdf() ->save(); diff --git a/app/Services/Recurring/RecurringService.php b/app/Services/Recurring/RecurringService.php index 0f7d8b892e2c..380cf8a66610 100644 --- a/app/Services/Recurring/RecurringService.php +++ b/app/Services/Recurring/RecurringService.php @@ -54,8 +54,6 @@ class RecurringService return $this; } - // $this->createInvitations()->setStatus(RecurringInvoice::STATUS_ACTIVE); - $this->setStatus(RecurringInvoice::STATUS_ACTIVE); return $this; From 5ce87d00d945af76e1dc1977eef868abcbef3e57 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 18 Oct 2021 15:23:34 +1100 Subject: [PATCH 2/7] Minor refactor --- app/Repositories/BaseRepository.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index e122fa3110c7..2f62dbc6ccf3 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -285,10 +285,8 @@ class BaseRepository } } - $model->load('invitations'); - /* If no invitations have been created, this is our fail safe to maintain state*/ - if ($model->invitations->count() == 0) + if ($model->invitations()->count() == 0) $model->service()->createInvitations(); /* Recalculate invoice amounts */ From 5f8b03cc58d3dbda8777488f336c3a0937f11cef Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 18 Oct 2021 21:46:26 +1100 Subject: [PATCH 3/7] minor fixes --- app/Http/Controllers/BaseController.php | 4 +++- app/Mail/SupportMessageSent.php | 2 +- tests/MockAccountData.php | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index d5df45487874..e1e4f7409bbe 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -781,7 +781,9 @@ class BaseController extends Controller case 'next': return 'main.next.dart.js'; case 'profile': - return 'main.profile.dart.js'; + return 'main.profile.dart.js'; + case 'html': + return 'main.html.dart.js'; default: return 'main.foss.dart.js'; diff --git a/app/Mail/SupportMessageSent.php b/app/Mail/SupportMessageSent.php index d0cc8e5de96b..888fcf1c235b 100644 --- a/app/Mail/SupportMessageSent.php +++ b/app/Mail/SupportMessageSent.php @@ -69,7 +69,7 @@ class SupportMessageSent extends Mailable if(Ninja::isHosted()) $subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated} :: {$plan} :: ".date('M jS, g:ia'); else - $subject = "{$priority}Self Hosted :: {$plan} :: {$platform} :: ".date('M jS, g:ia'); + $subject = "{$priority}Self Hosted :: {$plan} :: {$is_large}{$platform}{$migrated} :: ".date('M jS, g:ia'); return $this->from(config('mail.from.address'), $user->present()->name()) ->replyTo($user->email, $user->present()->name()) diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 4582ab1f2dba..cd9c0f4b1335 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -11,6 +11,7 @@ namespace Tests; +use App\DataMapper\ClientRegistrationFields; use App\DataMapper\ClientSettings; use App\DataMapper\CompanySettings; use App\Factory\CompanyUserFactory; @@ -173,6 +174,8 @@ trait MockAccountData 'account_id' => $this->account->id, ]); + $this->company->client_registration_fields = ClientRegistrationFields::generate(); + Storage::makeDirectory($this->company->company_key.'/documents', 0755, true); Storage::makeDirectory($this->company->company_key.'/images', 0755, true); From 9717c238f50c2825b1d2b2de90b684d5cd4e0c0a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Oct 2021 06:52:11 +1100 Subject: [PATCH 4/7] Add missing keys --- resources/lang/en/texts.php | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 00030eba5201..173b103d3de3 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4328,6 +4328,7 @@ $LANG = array( 'giropay_law' => 'By entering your Customer information (such as name, sort code and account number) you (the Customer) agree that this information is given voluntarily.', 'eps' => 'EPS', 'you_need_to_accept_the_terms_before_proceeding' => 'You need to accept the terms before proceeding.', + 'clone_to_expense' => 'Clone to expense', ); return $LANG; From 587f912a2217e8d31b04e121992b30c9599a7400 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Oct 2021 12:10:34 +1100 Subject: [PATCH 5/7] Increase rate limit for postmark --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index 96e445e4a169..552f3d18a9bc 100644 --- a/routes/api.php +++ b/routes/api.php @@ -215,7 +215,7 @@ Route::match(['get', 'post'], 'payment_notification_webhook/{company_key}/{compa ->middleware(['guest']) ->name('payment_notification_webhook'); -Route::post('api/v1/postmark_webhook', 'PostMarkController@webhook'); +Route::post('api/v1/postmark_webhook', 'PostMarkController@webhook')->middleware(['throttle:5000,1']); Route::get('token_hash_router', 'OneTimeTokenController@router'); Route::get('webcron', 'WebCronController@index'); Route::post('api/v1/get_migration_account', 'HostedMigrationController@getAccount')->middleware('guest'); From 51d6efb5ca11a812771219bbc1cf3f0d2b932cec Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Oct 2021 15:27:06 +1100 Subject: [PATCH 6/7] Fix language of subscription purchase --- resources/lang/en/texts.php | 2 ++ .../components/livewire/billing-portal-purchase.blade.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 173b103d3de3..0c6451c1ec9a 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4329,6 +4329,8 @@ $LANG = array( 'eps' => 'EPS', 'you_need_to_accept_the_terms_before_proceeding' => 'You need to accept the terms before proceeding.', 'clone_to_expense' => 'Clone to expense', + 'checkout' => 'Checkout', + ); return $LANG; diff --git a/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php b/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php index c02cc44ed202..6c67fbd9e3e2 100644 --- a/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php @@ -90,7 +90,7 @@
-

{{ $heading_text ?? ctrans('texts.login') }}

+

{{ $heading_text ?? ctrans('texts.checkout') }}

@if (session()->has('message')) @component('portal.ninja2020.components.message') {{ session('message') }} From a09666fb96b3accbe9e20b7b39a93968f6686e03 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Oct 2021 20:04:19 +1100 Subject: [PATCH 7/7] Minor fixes for marking an invoice as sent --- app/PaymentDrivers/BaseDriver.php | 2 +- app/Services/Invoice/InvoiceService.php | 2 +- app/Services/Invoice/MarkPaid.php | 4 ++-- app/Services/Invoice/MarkSent.php | 22 +++++++++++++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 57a288223532..e1b0e65fee77 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -308,7 +308,7 @@ class BaseDriver extends AbstractPaymentDriver $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->get(); $invoices->each(function ($invoice) { - $invoice->service()->removeUnpaidGatewayFees(); + $invoice->service()->removeUnpaidGatewayFees()->save(); }); } diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index dd0977a55bc6..d13743586651 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -494,6 +494,6 @@ class InvoiceService { $this->invoice->saveQuietly(); - return $this->invoice; + return $this->invoice->fresh(); } } diff --git a/app/Services/Invoice/MarkPaid.php b/app/Services/Invoice/MarkPaid.php index a78ba98b2c80..6f6831e0b696 100644 --- a/app/Services/Invoice/MarkPaid.php +++ b/app/Services/Invoice/MarkPaid.php @@ -86,8 +86,8 @@ class MarkPaid extends AbstractService ->deletePdf() ->save(); - if ($this->invoice->client->getSetting('client_manual_payment_notification')) - $payment->service()->sendEmail(); + // if ($this->invoice->client->getSetting('client_manual_payment_notification')) + // $payment->service()->sendEmail(); /* Update Invoice balance */ event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); diff --git a/app/Services/Invoice/MarkSent.php b/app/Services/Invoice/MarkSent.php index 198f7dbbd5ca..fbf874f9558b 100644 --- a/app/Services/Invoice/MarkSent.php +++ b/app/Services/Invoice/MarkSent.php @@ -33,15 +33,18 @@ class MarkSent extends AbstractService { /* Return immediately if status is not draft */ - if ($this->invoice->status_id != Invoice::STATUS_DRAFT) { + if ($this->invoice->fresh()->status_id != Invoice::STATUS_DRAFT) { return $this->invoice; } - $this->invoice->markInvitationsSent(); - + /*Set status*/ $this->invoice ->service() ->setStatus(Invoice::STATUS_SENT) + ->save(); + + $this->invoice + ->service() ->applyNumber() ->setDueDate() ->updateBalance($this->invoice->amount) @@ -49,9 +52,18 @@ class MarkSent extends AbstractService ->setReminder() ->save(); - $this->client->service()->updateBalance($this->invoice->balance)->save(); + $this->invoice->markInvitationsSent(); - $this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance, "Invoice {$this->invoice->number} marked as sent."); + /*Adjust client balance*/ + $this->client + ->service() + ->updateBalance($this->invoice->balance) + ->save(); + + /*Update ledger*/ + $this->invoice + ->ledger() + ->updateInvoiceBalance($this->invoice->balance, "Invoice {$this->invoice->number} marked as sent."); event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));