diff --git a/VERSION.txt b/VERSION.txt index a8fd6edcd03b..9b054f6a5cc5 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.0.50 \ No newline at end of file +5.0.51 \ No newline at end of file diff --git a/app/Factory/CloneCreditFactory.php b/app/Factory/CloneCreditFactory.php index 070f8239ee8b..19966346314a 100644 --- a/app/Factory/CloneCreditFactory.php +++ b/app/Factory/CloneCreditFactory.php @@ -25,7 +25,7 @@ class CloneCreditFactory $clone_credit->due_date = null; $clone_credit->partial_due_date = null; $clone_credit->user_id = $user_id; - $clone_credit->balance = $credit->amount; + //$clone_credit->balance = $credit->amount; $clone_credit->line_items = $credit->line_items; return $clone_credit; diff --git a/app/Factory/CloneCreditToQuoteFactory.php b/app/Factory/CloneCreditToQuoteFactory.php index 0013ca28303e..17adc7706d94 100644 --- a/app/Factory/CloneCreditToQuoteFactory.php +++ b/app/Factory/CloneCreditToQuoteFactory.php @@ -39,7 +39,7 @@ class CloneCreditToQuoteFactory $quote->custom_value3 = $credit->custom_value3; $quote->custom_value4 = $credit->custom_value4; $quote->amount = $credit->amount; - $quote->balance = $credit->balance; + //$quote->balance = $credit->balance; $quote->partial = $credit->partial; $quote->partial_due_date = $credit->partial_due_date; $quote->last_viewed = $credit->last_viewed; @@ -49,7 +49,7 @@ class CloneCreditToQuoteFactory $quote->date = null; $quote->due_date = null; $quote->partial_due_date = null; - $quote->balance = $credit->amount; + // $quote->balance = $credit->amount; $quote->line_items = $credit->line_items; return $quote; diff --git a/app/Factory/CloneInvoiceFactory.php b/app/Factory/CloneInvoiceFactory.php index a070564ccf4a..a7423415c70b 100644 --- a/app/Factory/CloneInvoiceFactory.php +++ b/app/Factory/CloneInvoiceFactory.php @@ -24,7 +24,7 @@ class CloneInvoiceFactory $clone_invoice->due_date = null; $clone_invoice->partial_due_date = null; $clone_invoice->user_id = $user_id; - $clone_invoice->balance = $invoice->amount; + //$clone_invoice->balance = $invoice->amount; $clone_invoice->amount = $invoice->amount; $clone_invoice->line_items = $invoice->line_items; diff --git a/app/Factory/CloneInvoiceToQuoteFactory.php b/app/Factory/CloneInvoiceToQuoteFactory.php index 358427b02f8d..6055311db95c 100644 --- a/app/Factory/CloneInvoiceToQuoteFactory.php +++ b/app/Factory/CloneInvoiceToQuoteFactory.php @@ -38,7 +38,7 @@ class CloneInvoiceToQuoteFactory $quote->custom_value3 = $invoice->custom_value3; $quote->custom_value4 = $invoice->custom_value4; $quote->amount = $invoice->amount; - $quote->balance = $invoice->amount; + //$quote->balance = $invoice->amount; $quote->partial = $invoice->partial; $quote->partial_due_date = $invoice->partial_due_date; $quote->last_viewed = $invoice->last_viewed; diff --git a/app/Factory/CloneQuoteFactory.php b/app/Factory/CloneQuoteFactory.php index 142e66ac9506..74617c557cce 100644 --- a/app/Factory/CloneQuoteFactory.php +++ b/app/Factory/CloneQuoteFactory.php @@ -24,7 +24,7 @@ class CloneQuoteFactory $clone_quote->due_date = null; $clone_quote->partial_due_date = null; $clone_quote->user_id = $user_id; - $clone_quote->balance = $quote->amount; + //$clone_quote->balance = $quote->amount; $clone_quote->amount = $quote->amount; $clone_quote->line_items = $quote->line_items; diff --git a/app/Factory/CloneQuoteToInvoiceFactory.php b/app/Factory/CloneQuoteToInvoiceFactory.php index e147cb7a3444..a2626d47dda6 100644 --- a/app/Factory/CloneQuoteToInvoiceFactory.php +++ b/app/Factory/CloneQuoteToInvoiceFactory.php @@ -38,6 +38,7 @@ class CloneQuoteToInvoiceFactory $invoice->partial_due_date = null; $invoice->number = null; $invoice->date = now()->format('Y-m-d'); + $invoice->balance = 0; return $invoice; } } diff --git a/app/Factory/InvoiceToRecurringInvoiceFactory.php b/app/Factory/InvoiceToRecurringInvoiceFactory.php index 04ea7a37b3cd..d63565980586 100644 --- a/app/Factory/InvoiceToRecurringInvoiceFactory.php +++ b/app/Factory/InvoiceToRecurringInvoiceFactory.php @@ -42,7 +42,7 @@ class InvoiceToRecurringInvoiceFactory $recurring_invoice->custom_value3 = $invoice->custom_value3; $recurring_invoice->custom_value4 = $invoice->custom_value4; $recurring_invoice->amount = $invoice->amount; - $recurring_invoice->balance = $invoice->balance; + // $recurring_invoice->balance = $invoice->balance; $recurring_invoice->user_id = $invoice->user_id; $recurring_invoice->client_id = $invoice->client_id; $recurring_invoice->company_id = $invoice->company_id; diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 13d8baeb5160..7ee03112f51e 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -75,7 +75,7 @@ class BaseController extends Controller 'company.credits.invitations.company', 'company.credits.documents', 'company.expenses.documents', - 'company.groups', + 'company.groups.documents', 'company.invoices.invitations.contact', 'company.invoices.invitations.company', 'company.invoices.documents', diff --git a/app/Jobs/Mail/EntityFailedSendMailer.php b/app/Jobs/Mail/EntityFailedSendMailer.php new file mode 100644 index 000000000000..4f4b5f7a2354 --- /dev/null +++ b/app/Jobs/Mail/EntityFailedSendMailer.php @@ -0,0 +1,99 @@ +company = $company; + + $this->user = $user; + + $this->invitation = $invitation; + + $this->entity = $invitation->{$entity_type}; + + $this->entity_type = $entity_type; + + $this->settings = $invitation->contact->client->getMergedSettings(); + + $this->template = $template; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + nlog("entity sent mailer"); + + /*If we are migrating data we don't want to fire these notification*/ + if ($this->company->is_disabled) { + return true; + } + + //Set DB + MultiDB::setDb($this->company->db); + + //if we need to set an email driver do it now + $this->setMailDriver(); + + $mail_obj = (new EntitySentObject($this->invitation, $this->entity_type, $this->template))->build(); + $mail_obj->from = [config('mail.from.address'), config('mail.from.name')]; + + try { + Mail::to($this->user->email) + ->send(new EntityNotificationMailer($mail_obj)); + } catch (\Exception $e) { + $this->failed($e); + $this->logMailError($e->getMessage(), $this->entity->client); + } + } +} diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php index aecd38180d22..b281ccd1fd2a 100644 --- a/app/Jobs/Util/Import.php +++ b/app/Jobs/Util/Import.php @@ -11,7 +11,6 @@ namespace App\Jobs\Util; -use Illuminate\Http\UploadedFile; use App\DataMapper\Analytics\MigrationFailure; use App\DataMapper\CompanySettings; use App\Exceptions\MigrationValidatorFailed; @@ -37,6 +36,7 @@ use App\Libraries\MultiDB; use App\Mail\MigrationCompleted; use App\Models\Activity; use App\Models\Client; +use App\Models\ClientContact; use App\Models\ClientGatewayToken; use App\Models\Company; use App\Models\CompanyGateway; @@ -76,6 +76,7 @@ use Exception; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Http\UploadedFile; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Mail; @@ -470,17 +471,22 @@ class Import implements ShouldQueue $contact_repository->save($saveable_contacts, $client); //link contact ids - $client->fresh(); - $new_contacts = $client->contacts; foreach ($resource['contacts'] as $key => $old_contact) { - $contact_match = $new_contacts->where('contact_key', $old_contact['contact_key'])->first(); + + $contact_match = ClientContact::where('contact_key', $old_contact['contact_key']) + ->where('company_id', $this->company->id) + ->where('client_id', $client->id) + ->withTrashed() + ->first(); if ($contact_match) { + $this->ids['client_contacts']['client_contacts_'.$old_contact['id']] = [ 'old' => $old_contact['id'], 'new' => $contact_match->id, ]; + } } } @@ -875,7 +881,7 @@ class Import implements ShouldQueue PaymentFactory::create($this->company->id, $modified['user_id']) ); - if ($resource['company_gateway_id'] != 'NULL' && $resource['company_gateway_id'] != null) { + if (array_key_exists('company_gateway_id', $resource) && isset($resource['company_gateway_id']) && $resource['company_gateway_id'] != 'NULL') { $payment->company_gateway_id = $this->transformId('company_gateways', $resource['company_gateway_id']); $payment->save(); } diff --git a/app/Listeners/Invoice/InvoiceFailedEmailNotification.php b/app/Listeners/Invoice/InvoiceFailedEmailNotification.php new file mode 100644 index 000000000000..8c55ce5520b8 --- /dev/null +++ b/app/Listeners/Invoice/InvoiceFailedEmailNotification.php @@ -0,0 +1,63 @@ +company->db); + + $first_notification_sent = true; + + $invoice = $event->invitation->invoice; + $invoice->last_sent_date = now(); + $invoice->save(); + + foreach ($event->invitation->company->company_users as $company_user) { + $user = $company_user->user; + + $notification = new EntitySentNotification($event->invitation, 'invoice'); + + $methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent']); + + if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { + unset($methods[$key]); + + EntitySentMailer::dispatch($event->invitation, 'invoice', $user, $event->invitation->company, $event->template); + $first_notification_sent = false; + } + + $notification->method = $methods; + + $user->notify($notification); + } + } +} diff --git a/app/Mail/Admin/EntityFailedSendObject.php b/app/Mail/Admin/EntityFailedSendObject.php new file mode 100644 index 000000000000..7ef6ee23f9b3 --- /dev/null +++ b/app/Mail/Admin/EntityFailedSendObject.php @@ -0,0 +1,140 @@ +invitation = $invitation; + $this->entity_type = $entity_type; + $this->entity = $invitation->{$entity_type}; + $this->contact = $invitation->contact; + $this->company = $invitation->company; + $this->template = $template; + } + + public function build() + { + $this->setTemplate(); + + $mail_obj = new stdClass; + $mail_obj->amount = $this->getAmount(); + $mail_obj->subject = $this->getSubject(); + $mail_obj->data = $this->getData(); + $mail_obj->markdown = 'email.admin.generic'; + $mail_obj->tag = $this->company->company_key; + + return $mail_obj; + } + + private function setTemplate() + { + // nlog($this->template); + + switch ($this->template) { + case 'invoice': + $this->template_subject = "texts.notification_invoice_bounced_subject"; + $this->template_body = "texts.notification_invoice_bounced"; + break; + case 'reminder1': + $this->template_subject = "texts.notification_invoice_reminder1_sent_subject"; + $this->template_body = "texts.notification_invoice_sent"; + break; + case 'reminder2': + $this->template_subject = "texts.notification_invoice_reminder2_sent_subject"; + $this->template_body = "texts.notification_invoice_sent"; + break; + case 'reminder3': + $this->template_subject = "texts.notification_invoice_reminder3_sent_subject"; + $this->template_body = "texts.notification_invoice_sent"; + break; + case 'reminder_endless': + $this->template_subject = "texts.notification_invoice_reminder_endless_sent_subject"; + $this->template_body = "texts.notification_invoice_sent"; + break; + case 'quote': + $this->template_subject = "texts.notification_quote_bounced_subject"; + $this->template_body = "texts.notification_quote_sent"; + break; + case 'credit': + $this->template_subject = "texts.notification_credit_bounced_subject"; + $this->template_body = "texts.notification_credit_bounced"; + break; + default: + $this->template_subject = "texts.notification_invoice_sent_subject"; + $this->template_body = "texts.notification_invoice_sent"; + break; + } + } + + private function getAmount() + { + return Number::formatMoney($this->entity->amount, $this->entity->client); + } + + private function getSubject() + { + return + ctrans( + $this->template_subject, + [ + 'client' => $this->contact->present()->name(), + 'invoice' => $this->entity->number, + ] + ); + } + + private function getData() + { + $settings = $this->entity->client->getMergedSettings(); + + return [ + 'title' => $this->getSubject(), + 'message' => ctrans( + $this->template_body, + [ + 'amount' => $this->getAmount(), + 'client' => $this->contact->present()->name(), + 'invoice' => $this->entity->number, + ] + ), + 'url' => $this->invitation->getAdminLink(), + 'button' => ctrans("texts.view_{$this->entity_type}"), + 'signature' => $settings->email_signature, + 'logo' => $this->company->present()->logo(), + 'settings' => $settings, + 'whitelabel' => $this->company->account->isPaid() ? true : false, + ]; + } +} diff --git a/app/Mail/Admin/EntitySentObject.php b/app/Mail/Admin/EntitySentObject.php index 3e0f9c8f76e2..ad7da9877e60 100644 --- a/app/Mail/Admin/EntitySentObject.php +++ b/app/Mail/Admin/EntitySentObject.php @@ -14,7 +14,7 @@ namespace App\Mail\Admin; use App\Utils\Number; use stdClass; -class EntitySentObject +class EntityFailedSendObject { public $invitation; @@ -91,7 +91,6 @@ class EntitySentObject $this->template_subject = "texts.notification_credit_sent_subject"; $this->template_body = "texts.notification_credit_sent"; break; - default: $this->template_subject = "texts.notification_invoice_sent_subject"; $this->template_body = "texts.notification_invoice_sent"; diff --git a/app/Models/Client.php b/app/Models/Client.php index 09afcc4a86b5..a94d23514c52 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -73,6 +73,7 @@ class Client extends BaseModel implements HasLocalePreference 'id_number', 'group_settings_id', 'public_notes', + 'phone' ]; protected $with = [ diff --git a/app/Repositories/ActivityRepository.php b/app/Repositories/ActivityRepository.php index 1e81e79acc26..d32c9de6a2b4 100644 --- a/app/Repositories/ActivityRepository.php +++ b/app/Repositories/ActivityRepository.php @@ -120,6 +120,12 @@ class ActivityRepository extends BaseRepository $entity_design_id = $entity->design_id ? $entity->design_id : $this->decodePrimaryKey($entity->client->getSetting($entity_design_id)); $design = Design::find($entity_design_id); + + if(!$entity->invitations()->exists()){ + nlog("No invitations for entity {$entity->id} - {$entity->number}"); + return; + } + $html = new HtmlEngine($entity->invitations->first()); if ($design->is_custom) { diff --git a/app/Services/Quote/ConvertQuote.php b/app/Services/Quote/ConvertQuote.php index 0a6b226e3f2f..b3b947ee40de 100644 --- a/app/Services/Quote/ConvertQuote.php +++ b/app/Services/Quote/ConvertQuote.php @@ -40,8 +40,8 @@ class ConvertQuote $invoice->fresh(); $invoice->service() - ->markSent() - ->createInvitations() + // ->markSent() + // ->createInvitations() ->save(); $quote->invoice_id = $invoice->id; diff --git a/app/Services/Quote/QuoteService.php b/app/Services/Quote/QuoteService.php index 4feefda7b944..fbf53e623a37 100644 --- a/app/Services/Quote/QuoteService.php +++ b/app/Services/Quote/QuoteService.php @@ -38,17 +38,20 @@ class QuoteService return $this; } - public function markApproved() - { - $mark_approved = new MarkApproved($this->quote->client); - $this->quote = $mark_approved->run($this->quote); + // public function markApproved() + // { + // $mark_approved = new MarkApproved($this->quote->client); + // $this->quote = $mark_approved->run($this->quote); - if ($this->quote->client->getSetting('auto_convert_quote') === true) { - $this->convert(); - } + // if ($this->quote->client->getSetting('auto_convert_quote') == true) { + // $this->convert(); + // } - return $this; - } + // $this->markSent() + // ->createInvitations(); + + // return $this; + // } public function convert() :self { @@ -116,12 +119,18 @@ class QuoteService event(new QuoteWasApproved($contact, $this->quote, $this->quote->company, Ninja::eventVars())); - $invoice = null; - if ($this->quote->client->getSetting('auto_convert_quote')) { $this->convert(); + + $this->invoice + ->service() + ->markSent() + ->createInvitations() + ->save(); + } + if ($this->quote->client->getSetting('auto_archive_quote')) { $quote_repo = new QuoteRepository(); $quote_repo->archive($this->quote); @@ -134,11 +143,13 @@ class QuoteService { //to prevent circular references we need to explicit call this here. - $mark_approved = new MarkApproved($this->quote->client); - $this->quote = $mark_approved->run($this->quote); + // $mark_approved = new MarkApproved($this->quote->client); + // $this->quote = $mark_approved->run($this->quote); $this->convert(); + $this->invoice->service()->createInvitations(); + return $this->invoice; } diff --git a/app/Transformers/GroupSettingTransformer.php b/app/Transformers/GroupSettingTransformer.php index 9696e956ee09..761b19d2f112 100644 --- a/app/Transformers/GroupSettingTransformer.php +++ b/app/Transformers/GroupSettingTransformer.php @@ -11,7 +11,9 @@ namespace App\Transformers; +use App\Models\Document; use App\Models\GroupSetting; +use App\Transformers\DocumentTransformer; use App\Utils\Traits\MakesHash; use stdClass; @@ -23,6 +25,7 @@ class GroupSettingTransformer extends EntityTransformer use MakesHash; protected $defaultIncludes = [ + 'documents' ]; /** @@ -47,4 +50,12 @@ class GroupSettingTransformer extends EntityTransformer 'is_deleted' => (bool) $group_setting->is_deleted, ]; } + + public function includeDocuments(GroupSetting $group_setting) + { + $transformer = new DocumentTransformer($this->serializer); + + return $this->includeCollection($group_setting->documents, $transformer, Document::class); + } + } diff --git a/config/ninja.php b/config/ninja.php index fad1a3744acf..80bb34276d78 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -13,7 +13,7 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', ''), - 'app_version' => '5.0.50', + 'app_version' => '5.0.51', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f89dd1350aa9..1dda79726715 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3371,4 +3371,6 @@ return [ 'required_payment_information_more' => 'To complete a payment we need more details about you.', 'required_client_info_save_label' => 'We will save this, so you don\'t have to enter it next time.', + 'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact.', + 'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice', ];