Fixes for mailable logging

This commit is contained in:
David Bomba 2025-01-15 18:15:34 +11:00
parent cd7866ed8a
commit dc033ae7e9
14 changed files with 211 additions and 20 deletions

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Events\General;
use App\Models\Company;
use Illuminate\Queue\SerializesModels;
/**
* Class EntityWasEmailed.
*/
class EntityWasEmailed
{
use SerializesModels;
public $invitation;
public $company;
public $event_vars;
public $template;
public function __construct($invitation, Company $company, array $event_vars, string $template)
{
$this->invitation = $invitation;
$this->company = $company;
$this->event_vars = $event_vars;
$this->template = $template;
}
}

View File

@ -32,6 +32,7 @@ use App\Events\Credit\CreditWasCreated;
use App\Events\Credit\CreditWasUpdated; use App\Events\Credit\CreditWasUpdated;
use App\Transformers\CreditTransformer; use App\Transformers\CreditTransformer;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use App\Events\General\EntityWasEmailed;
use App\Services\Template\TemplateAction; use App\Services\Template\TemplateAction;
use App\Http\Requests\Credit\BulkCreditRequest; use App\Http\Requests\Credit\BulkCreditRequest;
use App\Http\Requests\Credit\EditCreditRequest; use App\Http\Requests\Credit\EditCreditRequest;
@ -640,10 +641,28 @@ class CreditController extends BaseController
case 'email': case 'email':
case 'send_email': case 'send_email':
$credit->service()->markSent()->save();
$credit->invitations->load('contact.client.country', 'credit.client.country', 'credit.company')->each(function ($invitation) use ($credit) { $credit->invitations->load('contact.client.country', 'credit.client.country', 'credit.company')->each(function ($invitation) use ($credit) {
EmailEntity::dispatch($invitation->withoutRelations(), $credit->company->db, 'credit');
$mo = new \App\Services\Email\EmailObject();
$mo->entity_id = $credit->id;
$mo->entity_class = \App\Models\Credit::class;
$mo->invitation_id = $invitation->id;
$mo->client_id = $invitation->contact->client_id ?? null;
$mo->vendor_id = $invitation->contact->vendor_id ?? null;
$mo->settings = $invitation->contact->client->getMergedSettings();
$mo->email_template_body = 'email_template_credit';
$mo->email_template_subject = 'email_subject_credit';
\App\Services\Email\Email::dispatch($mo, $invitation->company);
$credit->entityEmailEvent($invitation, 'credit', 'credit');
}); });
event(new EntityWasEmailed($credit->invitations->first(), $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'credit'));
if (! $bulk) { if (! $bulk) {
return response()->json(['message' => 'email sent'], 200); return response()->json(['message' => 'email sent'], 200);
} }

View File

@ -11,7 +11,6 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\DataMapper\InvoiceSync;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Models\Quote; use App\Models\Quote;
use App\Models\Credit; use App\Models\Credit;
@ -19,6 +18,7 @@ use App\Models\Invoice;
use App\Models\Webhook; use App\Models\Webhook;
use App\Models\PurchaseOrder; use App\Models\PurchaseOrder;
use App\Services\Email\Email; use App\Services\Email\Email;
use App\DataMapper\InvoiceSync;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Services\Email\EmailObject; use App\Services\Email\EmailObject;
@ -27,6 +27,7 @@ use App\Transformers\QuoteTransformer;
use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Address;
use App\Events\Credit\CreditWasEmailed; use App\Events\Credit\CreditWasEmailed;
use App\Transformers\CreditTransformer; use App\Transformers\CreditTransformer;
use App\Events\General\EntityWasEmailed;
use App\Transformers\InvoiceTransformer; use App\Transformers\InvoiceTransformer;
use App\Http\Requests\Email\SendEmailRequest; use App\Http\Requests\Email\SendEmailRequest;
use App\Jobs\PurchaseOrder\PurchaseOrderEmail; use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
@ -80,7 +81,7 @@ class EmailController extends BaseController
} }
$entity_obj->invitations->each(function ($invitation) use ($entity_obj, $mo) { $entity_obj->invitations->each(function ($invitation) use ($entity_obj, $mo, $template) {
if (! $invitation->contact->trashed() && $invitation->contact->email) { if (! $invitation->contact->trashed() && $invitation->contact->email) {
$entity_obj->service()->markSent()->save(); $entity_obj->service()->markSent()->save();
@ -89,6 +90,8 @@ class EmailController extends BaseController
$mo->vendor_id = $invitation->contact->vendor_id ?? null; $mo->vendor_id = $invitation->contact->vendor_id ?? null;
Email::dispatch($mo, $invitation->company); Email::dispatch($mo, $invitation->company);
$entity_obj->entityEmailEvent($invitation, $template, $template);
} }
}); });
@ -102,7 +105,7 @@ class EmailController extends BaseController
$this->entity_transformer = InvoiceTransformer::class; $this->entity_transformer = InvoiceTransformer::class;
if ($entity_obj->invitations->count() >= 1) { if ($entity_obj->invitations->count() >= 1) {
$entity_obj->entityEmailEvent($entity_obj->invitations->first(), $template, $template); event(new EntityWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'invoice'));
$entity_obj->sendEvent(Webhook::EVENT_SENT_INVOICE, "client"); $entity_obj->sendEvent(Webhook::EVENT_SENT_INVOICE, "client");
} }
} }
@ -112,7 +115,7 @@ class EmailController extends BaseController
$this->entity_transformer = QuoteTransformer::class; $this->entity_transformer = QuoteTransformer::class;
if ($entity_obj->invitations->count() >= 1) { if ($entity_obj->invitations->count() >= 1) {
$entity_obj->entityEmailEvent($entity_obj->invitations->first(), $template); event(new EntityWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'invoice'));
$entity_obj->sendEvent(Webhook::EVENT_SENT_QUOTE, "client"); $entity_obj->sendEvent(Webhook::EVENT_SENT_QUOTE, "client");
} }
} }
@ -122,7 +125,7 @@ class EmailController extends BaseController
$this->entity_transformer = CreditTransformer::class; $this->entity_transformer = CreditTransformer::class;
if ($entity_obj->invitations->count() >= 1) { if ($entity_obj->invitations->count() >= 1) {
event(new CreditWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'credit')); event(new EntityWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'invoice'));
$entity_obj->sendEvent(Webhook::EVENT_SENT_CREDIT, "client"); $entity_obj->sendEvent(Webhook::EVENT_SENT_CREDIT, "client");
} }
} }

View File

@ -49,7 +49,6 @@ class NinjaMailer extends Mailable
$ninja_mailable->text($this->mail_obj->text_view, $this->mail_obj->data); $ninja_mailable->text($this->mail_obj->text_view, $this->mail_obj->data);
} }
nlog($this->mail_obj);
if (property_exists($this->mail_obj, 'bcc')) { if (property_exists($this->mail_obj, 'bcc')) {
$ninja_mailable->bcc($this->mail_obj->bcc); $ninja_mailable->bcc($this->mail_obj->bcc);
} }

View File

@ -535,7 +535,7 @@ class NinjaMailerJob implements ShouldQueue
if ($sending_user == "0") { if ($sending_user == "0") {
$user = $this->company->owner(); $user = $this->company->owner();
} else { } else {
$user = User::find($this->decodePrimaryKey($sending_user)); $user = User::withTrashed()->find($this->decodePrimaryKey($sending_user));
} }
return $user; return $user;

View File

@ -0,0 +1,96 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Listeners\General;
use App\Libraries\MultiDB;
use App\Jobs\Mail\NinjaMailer;
use App\Models\QuoteInvitation;
use App\Models\CreditInvitation;
use App\Jobs\Mail\NinjaMailerJob;
use App\Models\InvoiceInvitation;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Admin\EntitySentObject;
use App\Models\PurchaseOrderInvitation;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Utils\Traits\Notifications\UserNotifies;
class EntityEmailedNotification implements ShouldQueue
{
use UserNotifies;
public $delay = 10;
private $entity_string;
public function __construct()
{
}
private function resolveEntityString($invitation): self
{
$this->entity_string = match(get_class($invitation)) {
InvoiceInvitation::class => 'invoice',
CreditInvitation::class => 'credit',
QuoteInvitation::class => 'quote',
PurchaseOrderInvitation::class => 'purchase_order',
};
return $this;
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
MultiDB::setDb($event->company->db);
$first_notification_sent = true;
$this->resolveEntityString($event->invitation);
$entity = $event->invitation->{$this->entity_string}->fresh();
$entity->last_sent_date = now();
$entity->saveQuietly();
/* We loop through each user and determine whether they need to be notified */
foreach ($event->invitation->company->company_users as $company_user) {
/* The User */
$user = $company_user->user;
/* Returns an array of notification methods */
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, $this->entity_string, ['all_notifications', "{$this->entity_string}_sent", "{$this->entity_string}_sent_all", "{$this->entity_string}_sent_user"]);
/* If one of the methods is email then we fire the EntitySentMailer */
if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]);
$nmo = new NinjaMailerObject();
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, $this->entity_string, $event->template, $company_user->portalType()))->build());
$nmo->company = $event->invitation->company;
$nmo->settings = $event->invitation->company->settings;
$nmo->to_user = $user;
(new NinjaMailerJob($nmo))->handle();
$nmo = null;
/* This prevents more than one notification being sent */
$first_notification_sent = false;
}
}
}
}

View File

@ -37,7 +37,7 @@ class InvoiceEmailedNotification implements ShouldQueue
*/ */
public function handle($event) public function handle($event)
{ {
nlog($event->template); // nlog($event->template);
MultiDB::setDb($event->company->db); MultiDB::setDb($event->company->db);

View File

@ -277,6 +277,8 @@ class Activity extends StaticModel
public const E_EXPENSE_CREATED = 148; public const E_EXPENSE_CREATED = 148;
public const EMAIL_CREDIT = 149;
protected $casts = [ protected $casts = [
'is_system' => 'boolean', 'is_system' => 'boolean',
'updated_at' => 'timestamp', 'updated_at' => 'timestamp',

View File

@ -11,6 +11,7 @@
namespace App\Models; namespace App\Models;
use App\Utils\Ninja;
use App\Utils\Number; use App\Utils\Number;
use Laravel\Scout\Searchable; use Laravel\Scout\Searchable;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
@ -20,6 +21,7 @@ use Illuminate\Support\Facades\App;
use App\Utils\Traits\MakesReminders; use App\Utils\Traits\MakesReminders;
use App\Services\Credit\CreditService; use App\Services\Credit\CreditService;
use App\Services\Ledger\LedgerService; use App\Services\Ledger\LedgerService;
use App\Events\Credit\CreditWasEmailed;
use App\Utils\Traits\MakesInvoiceValues; use App\Utils\Traits\MakesInvoiceValues;
use Laracasts\Presenter\PresentableTrait; use Laracasts\Presenter\PresentableTrait;
use App\Models\Presenters\CreditPresenter; use App\Models\Presenters\CreditPresenter;
@ -430,4 +432,24 @@ class Credit extends BaseModel
return ctrans('texts.sent'); return ctrans('texts.sent');
} }
} }
/**
* entityEmailEvent
*
* Translates the email type into an activity + notification
* that matches.
*/
public function entityEmailEvent($invitation, $reminder_template)
{
switch ($reminder_template) {
case 'credit':
event(new CreditWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
break;
default:
// code...
break;
}
}
} }

View File

@ -89,6 +89,7 @@ use App\Events\Credit\CreditWasArchived;
use App\Events\Credit\CreditWasRestored; use App\Events\Credit\CreditWasRestored;
use App\Events\Design\DesignWasArchived; use App\Events\Design\DesignWasArchived;
use App\Events\Design\DesignWasRestored; use App\Events\Design\DesignWasRestored;
use App\Events\General\EntityWasEmailed;
use App\Events\Invoice\InvoiceWasViewed; use App\Events\Invoice\InvoiceWasViewed;
use App\Events\Misc\InvitationWasViewed; use App\Events\Misc\InvitationWasViewed;
use App\Events\Payment\PaymentWasVoided; use App\Events\Payment\PaymentWasVoided;
@ -137,10 +138,13 @@ use App\Events\Vendor\VendorContactLoggedIn;
use App\Listeners\Quote\QuoteViewedActivity; use App\Listeners\Quote\QuoteViewedActivity;
use App\Listeners\User\ArchivedUserActivity; use App\Listeners\User\ArchivedUserActivity;
use App\Listeners\User\RestoredUserActivity; use App\Listeners\User\RestoredUserActivity;
use App\Events\Invoice\InvoiceAutoBillFailed;
use App\Events\Quote\QuoteReminderWasEmailed; use App\Events\Quote\QuoteReminderWasEmailed;
use App\Events\Statement\StatementWasEmailed; use App\Events\Statement\StatementWasEmailed;
use App\Listeners\Credit\CreditEmailActivity;
use App\Listeners\Quote\QuoteApprovedWebhook; use App\Listeners\Quote\QuoteApprovedWebhook;
use App\Listeners\Quote\QuoteDeletedActivity; use App\Listeners\Quote\QuoteDeletedActivity;
use App\Events\Invoice\InvoiceAutoBillSuccess;
use App\Listeners\Credit\CreditViewedActivity; use App\Listeners\Credit\CreditViewedActivity;
use App\Listeners\Invoice\InvoicePaidActivity; use App\Listeners\Invoice\InvoicePaidActivity;
use App\Listeners\Payment\PaymentNotification; use App\Listeners\Payment\PaymentNotification;
@ -155,8 +159,6 @@ use App\Listeners\Activity\TaskUpdatedActivity;
use App\Listeners\Invoice\InvoiceEmailActivity; use App\Listeners\Invoice\InvoiceEmailActivity;
use App\Listeners\SendVerificationNotification; use App\Listeners\SendVerificationNotification;
use App\Events\Credit\CreditWasEmailedAndFailed; use App\Events\Credit\CreditWasEmailedAndFailed;
use App\Events\Invoice\InvoiceAutoBillFailed;
use App\Events\Invoice\InvoiceAutoBillSuccess;
use App\Listeners\Activity\CreatedQuoteActivity; use App\Listeners\Activity\CreatedQuoteActivity;
use App\Listeners\Activity\DeleteClientActivity; use App\Listeners\Activity\DeleteClientActivity;
use App\Listeners\Activity\DeleteCreditActivity; use App\Listeners\Activity\DeleteCreditActivity;
@ -216,6 +218,7 @@ use App\Listeners\Quote\QuoteReminderEmailActivity;
use App\Events\PurchaseOrder\PurchaseOrderWasViewed; use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
use App\Events\Subscription\SubscriptionWasArchived; use App\Events\Subscription\SubscriptionWasArchived;
use App\Events\Subscription\SubscriptionWasRestored; use App\Events\Subscription\SubscriptionWasRestored;
use App\Listeners\General\EntityEmailedNotification;
use App\Events\PurchaseOrder\PurchaseOrderWasCreated; use App\Events\PurchaseOrder\PurchaseOrderWasCreated;
use App\Events\PurchaseOrder\PurchaseOrderWasDeleted; use App\Events\PurchaseOrder\PurchaseOrderWasDeleted;
use App\Events\PurchaseOrder\PurchaseOrderWasEmailed; use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
@ -240,6 +243,8 @@ use App\Events\RecurringQuote\RecurringQuoteWasArchived;
use App\Events\RecurringQuote\RecurringQuoteWasRestored; use App\Events\RecurringQuote\RecurringQuoteWasRestored;
use App\Listeners\Activity\SubscriptionArchivedActivity; use App\Listeners\Activity\SubscriptionArchivedActivity;
use App\Listeners\Activity\SubscriptionRestoredActivity; use App\Listeners\Activity\SubscriptionRestoredActivity;
use App\Listeners\Invoice\InvoiceAutoBillFailedActivity;
use App\Listeners\Invoice\InvoiceAutoBillSuccessActivity;
use App\Listeners\Invoice\InvoiceFailedEmailNotification; use App\Listeners\Invoice\InvoiceFailedEmailNotification;
use App\Events\RecurringExpense\RecurringExpenseWasCreated; use App\Events\RecurringExpense\RecurringExpenseWasCreated;
use App\Events\RecurringExpense\RecurringExpenseWasDeleted; use App\Events\RecurringExpense\RecurringExpenseWasDeleted;
@ -252,8 +257,6 @@ use App\Events\RecurringExpense\RecurringExpenseWasArchived;
use App\Events\RecurringExpense\RecurringExpenseWasRestored; use App\Events\RecurringExpense\RecurringExpenseWasRestored;
use App\Events\RecurringInvoice\RecurringInvoiceWasArchived; use App\Events\RecurringInvoice\RecurringInvoiceWasArchived;
use App\Events\RecurringInvoice\RecurringInvoiceWasRestored; use App\Events\RecurringInvoice\RecurringInvoiceWasRestored;
use App\Listeners\Invoice\InvoiceAutoBillFailedActivity;
use App\Listeners\Invoice\InvoiceAutoBillSuccessActivity;
use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity; use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity;
use App\Listeners\PurchaseOrder\PurchaseOrderViewedActivity; use App\Listeners\PurchaseOrder\PurchaseOrderViewedActivity;
use App\Listeners\PurchaseOrder\UpdatePurchaseOrderActivity; use App\Listeners\PurchaseOrder\UpdatePurchaseOrderActivity;
@ -392,7 +395,8 @@ class EventServiceProvider extends ServiceProvider
CreditWasEmailedAndFailed::class => [ CreditWasEmailedAndFailed::class => [
], ],
CreditWasEmailed::class => [ CreditWasEmailed::class => [
CreditEmailedNotification::class, CreditEmailActivity::class,
// CreditEmailedNotification::class,
], ],
CreditWasMarkedSent::class => [ CreditWasMarkedSent::class => [
], ],
@ -414,6 +418,9 @@ class EventServiceProvider extends ServiceProvider
], ],
DesignWasRestored::class => [ DesignWasRestored::class => [
], ],
EntityWasEmailed::class => [
EntityEmailedNotification::class,
],
ExpenseWasCreated::class => [ ExpenseWasCreated::class => [
CreatedExpenseActivity::class, CreatedExpenseActivity::class,
], ],
@ -453,7 +460,7 @@ class EventServiceProvider extends ServiceProvider
], ],
InvoiceWasEmailed::class => [ InvoiceWasEmailed::class => [
InvoiceEmailActivity::class, InvoiceEmailActivity::class,
InvoiceEmailedNotification::class, // InvoiceEmailedNotification::class,
], ],
InvoiceWasEmailedAndFailed::class => [ InvoiceWasEmailedAndFailed::class => [
InvoiceEmailFailedActivity::class, InvoiceEmailFailedActivity::class,
@ -461,7 +468,7 @@ class EventServiceProvider extends ServiceProvider
], ],
InvoiceReminderWasEmailed::class => [ InvoiceReminderWasEmailed::class => [
InvoiceReminderEmailActivity::class, InvoiceReminderEmailActivity::class,
InvoiceEmailedNotification::class, // InvoiceEmailedNotification::class,
], ],
InvoiceWasDeleted::class => [ InvoiceWasDeleted::class => [
InvoiceDeletedActivity::class, InvoiceDeletedActivity::class,
@ -499,7 +506,7 @@ class EventServiceProvider extends ServiceProvider
], ],
PurchaseOrderWasEmailed::class => [ PurchaseOrderWasEmailed::class => [
PurchaseOrderEmailActivity::class, PurchaseOrderEmailActivity::class,
PurchaseOrderEmailedNotification::class, // PurchaseOrderEmailedNotification::class,
], ],
PurchaseOrderWasRestored::class => [ PurchaseOrderWasRestored::class => [
PurchaseOrderRestoredActivity::class, PurchaseOrderRestoredActivity::class,

View File

@ -66,8 +66,10 @@ class ActivityRepository extends BaseRepository
$activity->save(); $activity->save();
//rate limiter //rate limiter
if(!in_array($fields->activity_type_id, [Activity::EMAIL_INVOICE, Activity::EMAIL_CREDIT, Activity::EMAIL_QUOTE, Activity::EMAIL_PURCHASE_ORDER])){
$this->createBackup($entity, $activity); $this->createBackup($entity, $activity);
} }
}
/** /**
* Creates a backup. * Creates a backup.

View File

@ -244,7 +244,6 @@ class Purify
$html = str_replace('%24', '$', $html); $html = str_replace('%24', '$', $html);
libxml_use_internal_errors(true); libxml_use_internal_errors(true);
libxml_disable_entity_loader(true); libxml_disable_entity_loader(true);
// nlog("pre purify => {$html}");
$document = new \DOMDocument(); $document = new \DOMDocument();
@$document->loadHTML(htmlspecialchars_decode(htmlspecialchars($html, ENT_QUOTES, 'UTF-8'))); @$document->loadHTML(htmlspecialchars_decode(htmlspecialchars($html, ENT_QUOTES, 'UTF-8')));

View File

@ -5487,6 +5487,8 @@ $lang = array(
'enable_public_notifications_help' => 'Enable real-time notifications from Invoice Ninja.', 'enable_public_notifications_help' => 'Enable real-time notifications from Invoice Ninja.',
'navigate' => 'Navigate', 'navigate' => 'Navigate',
'calculate_taxes_warning' => 'This action will enable line item taxes and disable total taxes. Any open invoices may be recalculated with the new settings!', 'calculate_taxes_warning' => 'This action will enable line item taxes and disable total taxes. Any open invoices may be recalculated with the new settings!',
'activity_149' => ':user emailed credit :credit for :client to :contact',
); );
return $lang; return $lang;