From 2d405bbda434e27f54087ef73918a2559e28e0f0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 22 Aug 2023 18:00:54 +1000 Subject: [PATCH 01/10] Updates for translations --- lang/en/texts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/en/texts.php b/lang/en/texts.php index a96969812c96..a00b5f151fc1 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -1150,7 +1150,7 @@ $LANG = array( 'plan_status' => 'Plan Status', 'plan_upgrade' => 'Upgrade', - 'plan_change' => 'Change Plan', + 'plan_change' => 'Manage Plan', 'pending_change_to' => 'Changes To', 'plan_changes_to' => ':plan on :date', 'plan_term_changes_to' => ':plan (:term) on :date', @@ -4330,7 +4330,7 @@ $LANG = array( 'include_drafts' => 'Include Drafts', 'include_drafts_help' => 'Include draft records in reports', 'is_invoiced' => 'Is Invoiced', - 'change_plan' => 'Change Plan', + 'change_plan' => 'Manage Plan', 'persist_data' => 'Persist Data', 'customer_count' => 'Customer Count', 'verify_customers' => 'Verify Customers', From 6f23fc31331e099399a6d6f4a8391dd69b489cd8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 22 Aug 2023 20:42:01 +1000 Subject: [PATCH 02/10] Working on postmark email histoyr --- app/Jobs/PostMark/ProcessPostmarkWebhook.php | 84 ++++++++++++----- app/Services/Client/EmailHistory.php | 95 ++++++++++++++++++++ app/Services/Email/Email.php | 2 +- composer.json | 3 +- composer.lock | 40 ++++++++- lang/en/texts.php | 3 +- 6 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 app/Services/Client/EmailHistory.php diff --git a/app/Jobs/PostMark/ProcessPostmarkWebhook.php b/app/Jobs/PostMark/ProcessPostmarkWebhook.php index d99455ab322e..1a04673afc40 100644 --- a/app/Jobs/PostMark/ProcessPostmarkWebhook.php +++ b/app/Jobs/PostMark/ProcessPostmarkWebhook.php @@ -11,27 +11,24 @@ namespace App\Jobs\PostMark; -use App\DataMapper\Analytics\Mail\EmailBounce; -use App\DataMapper\Analytics\Mail\EmailSpam; -use App\Jobs\Util\SystemLogger; +use App\Models\SystemLog; use App\Libraries\MultiDB; -use App\Models\Company; +use Postmark\PostmarkClient; +use Illuminate\Bus\Queueable; +use App\Jobs\Util\SystemLogger; +use App\Models\QuoteInvitation; use App\Models\CreditInvitation; use App\Models\InvoiceInvitation; -use App\Models\Payment; -use App\Models\PurchaseOrderInvitation; -use App\Models\QuoteInvitation; -use App\Models\RecurringInvoiceInvitation; -use App\Models\SystemLog; -use App\Notifications\Ninja\EmailBounceNotification; -use App\Notifications\Ninja\EmailSpamNotification; -use App\Utils\Ninja; -use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Turbo124\Beacon\Facades\LightLogs; +use App\Models\PurchaseOrderInvitation; +use Illuminate\Queue\InteractsWithQueue; +use App\Models\RecurringInvoiceInvitation; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use App\DataMapper\Analytics\Mail\EmailSpam; +use App\DataMapper\Analytics\Mail\EmailBounce; +use App\Notifications\Ninja\EmailSpamNotification; class ProcessPostmarkWebhook implements ShouldQueue { @@ -41,6 +38,16 @@ class ProcessPostmarkWebhook implements ShouldQueue public $invitation; + private array $default_response = [ + 'subject' => 'Message not found.', + 'status' => '', + 'recipient' => '', + 'type' => '', + 'delivery_message' => '', + 'server' => '', + 'server_ip' => '', + ]; + /** * Create a new job instance. * @@ -126,8 +133,10 @@ class ProcessPostmarkWebhook implements ShouldQueue $this->invitation->opened_date = now(); $this->invitation->save(); + $data = array_merge($this->request, ['history' => $this->fetchMessage()]); + (new SystemLogger( - $this->request, + $data, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_OPENED, SystemLog::TYPE_WEBHOOK_RESPONSE, @@ -155,8 +164,10 @@ class ProcessPostmarkWebhook implements ShouldQueue $this->invitation->email_status = 'delivered'; $this->invitation->save(); + $data = array_merge($this->request, ['history' => $this->fetchMessage()]); + (new SystemLogger( - $this->request, + $data, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_DELIVERY, SystemLog::TYPE_WEBHOOK_RESPONSE, @@ -204,7 +215,9 @@ class ProcessPostmarkWebhook implements ShouldQueue LightLogs::create($bounce)->send(); - (new SystemLogger($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle(); + $data = array_merge($this->request, ['history' => $this->fetchMessage()]); + + (new SystemLogger($data, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle(); // if(config('ninja.notification.slack')) // $this->invitation->company->notification(new EmailBounceNotification($this->invitation->company->account))->ninja(); @@ -248,7 +261,9 @@ class ProcessPostmarkWebhook implements ShouldQueue LightLogs::create($spam)->send(); - (new SystemLogger($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle(); + $data = array_merge($this->request, ['history' => $this->fetchMessage()]); + + (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')) { $this->invitation->company->notification(new EmailSpamNotification($this->invitation->company->account))->ninja(); @@ -273,4 +288,33 @@ class ProcessPostmarkWebhook implements ShouldQueue return $invitation; } } + + private function fetchMessage(): array + { + if(strlen($this->request['MessageID']) < 1){ + return $this->default_response; + } + + try { + + $postmark = new PostmarkClient(config('services.postmark.secret')); + $messageDetail = $postmark->getOutboundMessageDetails($this->request['MessageID']); + + return [ + 'subject' => $messageDetail->subject ?? '', + 'status' => $messageDetail->status ?? '', + 'recipient' => $messageDetail->messageevents[0]['Recipient'] ?? '', + 'type' => $messageDetail->messageevents[0]->Type ?? '', + 'subject' => $messageDetail->messageevents[0]->Details->DeliveryMessage ?? '', + 'server' => $messageDetail->messageevents[0]->Details->DestinationServer ?? '', + 'server_ip' => $messageDetail->messageevents[0]->Details->DestinationIP ?? '', + ]; + + } + catch (\Exception $e) { + + return $this->default_response; + + } + } } diff --git a/app/Services/Client/EmailHistory.php b/app/Services/Client/EmailHistory.php new file mode 100644 index 000000000000..b279c6c5f2c6 --- /dev/null +++ b/app/Services/Client/EmailHistory.php @@ -0,0 +1,95 @@ + 'Message not found.', + 'status' => '', + 'recipient' => '', + 'type' => '', + 'subject' => '', + 'server' => '', + 'server_ip' => '', + ]; + + public function __construct(public Client $client) + { + } + + public function run(): array + { + // $settings = $this->client->getMergedSettings(); + + // if($settings->email_sending_method == 'default'){ + // $this->postmark_token = config('services.postmark.token'); + // } + // elseif($settings->email_sending_method == 'client_postmark'){ + // $this->postmark_token = $settings->postmark_secret; + // } + // else{ + // return []; + // } + + // $this->postmark = new PostmarkClient($this->postmark_token); + + return SystemLog::query() + ->where('client_id', $this->client->id) + ->where('category_id', SystemLog::CATEGORY_MAIL) + ->orderBy('id','DESC') + ->cursor() + ->map(function ($system_log) { + + if($system_log->log['history'] ?? false){ + return json_decode($system_log->log['history'],true); + } + })->toArray(); + } + + private function fetchMessage(string $message_id): array + { + if(strlen($message_id) < 1){ + return $this->default_response; + } + + try { + + $messageDetail = $this->postmark->getOutboundMessageDetails($message_id); + + return [ + 'subject' => $messageDetail->subject ?? '', + 'status' => $messageDetail->status ?? '', + 'recipient' => $messageDetail->messageevents[0]['Recipient'] ?? '', + 'type' => $messageDetail->messageevents[0]->Type ?? '', + 'delivery_message' => $messageDetail->messageevents[0]->Details->DeliveryMessage ?? '', + 'server' => $messageDetail->messageevents[0]->Details->DestinationServer ?? '', + 'server_ip' => $messageDetail->messageevents[0]->Details->DestinationIP ?? '', + ]; + + } + catch (\Exception $e) { + + return $this->default_response; + + } + } +} \ No newline at end of file diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index 4c114d586d31..cebb8b7b6f79 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -240,7 +240,6 @@ class Email implements ShouldQueue } if ($this->client_mailgun_secret) { - $mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint); } @@ -254,6 +253,7 @@ class Email implements ShouldQueue LightLogs::create(new EmailSuccess($this->company->company_key)) ->send(); + } catch(\Symfony\Component\Mime\Exception\RfcComplianceException $e) { nlog("Mailer failed with a Logic Exception {$e->getMessage()}"); $this->fail(); diff --git a/composer.json b/composer.json index d9bb6348f79c..3204247f8398 100644 --- a/composer.json +++ b/composer.json @@ -76,6 +76,7 @@ "omnipay/paypal": "^3.0", "payfast/payfast-php-sdk": "^1.1", "pragmarx/google2fa": "^8.0", + "psr/http-message": "^1.0", "pusher/pusher-php-server": "^7.2", "razorpay/razorpay": "2.*", "sentry/sentry-laravel": "^3", @@ -96,7 +97,7 @@ "twilio/sdk": "^6.40", "webpatser/laravel-countries": "dev-master#75992ad", "wepay/php-sdk": "^0.3", - "psr/http-message": "^1.0" + "wildbit/postmark-php": "^4.0" }, "require-dev": { "php": "^8.1", diff --git a/composer.lock b/composer.lock index 31584f8b7590..accbb507064e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "673ca66ddfdb05c3ea29012594a196d3", + "content-hash": "70ade0ea4925946765213166010b0f32", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -14230,6 +14230,44 @@ "source": "https://github.com/wepay/PHP-SDK/tree/master" }, "time": "2017-01-21T07:03:26+00:00" + }, + { + "name": "wildbit/postmark-php", + "version": "v4.0.5", + "source": { + "type": "git", + "url": "https://github.com/ActiveCampaign/postmark-php.git", + "reference": "b71efba061de7cf7e1f853d211b1c5edce4e3c5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ActiveCampaign/postmark-php/zipball/b71efba061de7cf7e1f853d211b1c5edce4e3c5b", + "reference": "b71efba061de7cf7e1f853d211b1c5edce4e3c5b", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Postmark\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The officially supported client for Postmark (http://postmarkapp.com)", + "support": { + "issues": "https://github.com/ActiveCampaign/postmark-php/issues", + "source": "https://github.com/ActiveCampaign/postmark-php/tree/v4.0.5" + }, + "time": "2023-02-03T15:00:17+00:00" } ], "packages-dev": [ diff --git a/lang/en/texts.php b/lang/en/texts.php index a00b5f151fc1..183cbf5c8c04 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5158,8 +5158,7 @@ $LANG = array( 'unlinked_transaction' => 'Successfully unlinked transaction', 'view_dashboard_permission' => 'Allow user to access the dashboard, data is limited to available permissions', 'marked_sent_credits' => 'Successfully marked credits sent', - -); +); return $LANG; From e1e99cbd9e18591b1c566d2a9f74ec1c8d7d3a3c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 14:36:55 +1000 Subject: [PATCH 03/10] Working on email history for PostMark --- app/Jobs/PostMark/ProcessPostmarkWebhook.php | 57 +++++++++++++------- app/Services/Client/EmailHistory.php | 3 +- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/app/Jobs/PostMark/ProcessPostmarkWebhook.php b/app/Jobs/PostMark/ProcessPostmarkWebhook.php index 1a04673afc40..18dde45e430c 100644 --- a/app/Jobs/PostMark/ProcessPostmarkWebhook.php +++ b/app/Jobs/PostMark/ProcessPostmarkWebhook.php @@ -38,15 +38,15 @@ class ProcessPostmarkWebhook implements ShouldQueue public $invitation; - private array $default_response = [ - 'subject' => 'Message not found.', - 'status' => '', - 'recipient' => '', - 'type' => '', - 'delivery_message' => '', - 'server' => '', - 'server_ip' => '', - ]; + private $entity; + + private array $default_response = [ + 'recipients' => '', + 'subject' => 'Message not found.', + 'entity' => '', + 'entity_id' => '', + 'events' => [], + ]; /** * Create a new job instance. @@ -275,14 +275,19 @@ class ProcessPostmarkWebhook implements ShouldQueue $invitation = false; if ($invitation = InvoiceInvitation::where('message_id', $message_id)->first()) { + $this->entity = 'invoice'; return $invitation; } elseif ($invitation = QuoteInvitation::where('message_id', $message_id)->first()) { + $this->entity = 'quote'; return $invitation; } elseif ($invitation = RecurringInvoiceInvitation::where('message_id', $message_id)->first()) { + $this->entity = 'recurring_invoice'; return $invitation; } elseif ($invitation = CreditInvitation::where('message_id', $message_id)->first()) { + $this->entity = 'credit'; return $invitation; } elseif ($invitation = PurchaseOrderInvitation::where('message_id', $message_id)->first()) { + $this->entity = 'purchase_order'; return $invitation; } else { return $invitation; @@ -297,19 +302,33 @@ class ProcessPostmarkWebhook implements ShouldQueue try { - $postmark = new PostmarkClient(config('services.postmark.secret')); + $postmark = new PostmarkClient(config('services.postmark.token')); $messageDetail = $postmark->getOutboundMessageDetails($this->request['MessageID']); - + + $recipients = collect($messageDetail['recipients'])->flatten()->implode(','); + $subject = $messageDetail->subject ?? ''; + + $events = collect($messageDetail->messageevents)->map(function ($event) { + + return [ + 'recipient' => $event->Recipient ?? '', + 'status' => $event->Type ?? '', + 'delivery_message' => $event->Details->DeliveryMessage ?? $event->Details->Summary ?? '', + 'server' => $event->Details->DestinationServer ?? '', + 'server_ip' => $event->Details->DestinationIP ?? '', + 'date' => \Carbon\Carbon::parse($event->ReceivedAt)->format('Y-m-d H:m:s') ?? '', + ]; + + })->toArray(); + return [ - 'subject' => $messageDetail->subject ?? '', - 'status' => $messageDetail->status ?? '', - 'recipient' => $messageDetail->messageevents[0]['Recipient'] ?? '', - 'type' => $messageDetail->messageevents[0]->Type ?? '', - 'subject' => $messageDetail->messageevents[0]->Details->DeliveryMessage ?? '', - 'server' => $messageDetail->messageevents[0]->Details->DestinationServer ?? '', - 'server_ip' => $messageDetail->messageevents[0]->Details->DestinationIP ?? '', + 'recipients' => $recipients, + 'subject' => $subject, + 'entity' => $this->entity ?? '', + 'entity_id' => $this->invitation->{$this->entity}->hashed_id ?? '', + 'events' => $events, ]; - + } catch (\Exception $e) { diff --git a/app/Services/Client/EmailHistory.php b/app/Services/Client/EmailHistory.php index b279c6c5f2c6..f0715c49b3e3 100644 --- a/app/Services/Client/EmailHistory.php +++ b/app/Services/Client/EmailHistory.php @@ -16,6 +16,7 @@ use App\Models\SystemLog; use Postmark\PostmarkClient; use App\Services\AbstractService; +/** @deprecated */ class EmailHistory extends AbstractService { private string $postmark_token; @@ -27,7 +28,7 @@ class EmailHistory extends AbstractService 'status' => '', 'recipient' => '', 'type' => '', - 'subject' => '', + 'delivery_message' => '', 'server' => '', 'server_ip' => '', ]; From 88423df8cb9ebeb05c83a8392f486ee155143355 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 15:01:37 +1000 Subject: [PATCH 04/10] Add email history tracking --- .../Controllers/EmailHistoryController.php | 65 +++++++++++++++++++ .../Email/ClientEmailHistoryRequest.php | 55 ++++++++++++++++ .../Email/EntityEmailHistoryRequest.php | 62 ++++++++++++++++++ routes/api.php | 3 + 4 files changed, 185 insertions(+) create mode 100644 app/Http/Controllers/EmailHistoryController.php create mode 100644 app/Http/Requests/Email/ClientEmailHistoryRequest.php create mode 100644 app/Http/Requests/Email/EntityEmailHistoryRequest.php diff --git a/app/Http/Controllers/EmailHistoryController.php b/app/Http/Controllers/EmailHistoryController.php new file mode 100644 index 000000000000..70d52b28d4d1 --- /dev/null +++ b/app/Http/Controllers/EmailHistoryController.php @@ -0,0 +1,65 @@ +id) + ->where('category_id', SystemLog::CATEGORY_MAIL) + ->orderBy('id','DESC') + ->map(function ($system_log){ + if($system_log->log['history'] ?? false) { + return json_decode($system_log->log['history'], true); + } + }); + + return response()->json($data, 200); + + } + + public function entityHistory(EntityEmailHistoryRequest $request) + { + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $data = SystemLog::where('company_id', $user->company()->id) + ->where('category_id', SystemLog::CATEGORY_MAIL) + ->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id)) + ->orderBy('id', 'DESC') + ->map(function ($system_log) { + if($system_log->log['history'] ?? false) { + return json_decode($system_log->log['history'], true); + } + }); + + return response()->json($data, 200); + + } +} \ No newline at end of file diff --git a/app/Http/Requests/Email/ClientEmailHistoryRequest.php b/app/Http/Requests/Email/ClientEmailHistoryRequest.php new file mode 100644 index 000000000000..45d60b458b99 --- /dev/null +++ b/app/Http/Requests/Email/ClientEmailHistoryRequest.php @@ -0,0 +1,55 @@ +user(); + + return $user->can('view', $this->client); + + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + ]; + } + + public function prepareForValidation() + { + $input = $this->all(); + + $this->replace($input); + } + +} diff --git a/app/Http/Requests/Email/EntityEmailHistoryRequest.php b/app/Http/Requests/Email/EntityEmailHistoryRequest.php new file mode 100644 index 000000000000..2c506e653dd6 --- /dev/null +++ b/app/Http/Requests/Email/EntityEmailHistoryRequest.php @@ -0,0 +1,62 @@ +user(); + + return [ + 'entity' => 'bail|required|string|in:invoice,quote,credit,recurring_invoice,purchase_order', + 'entity_id' => ['bail','required',Rule::exists($this->entity_plural, 'id')->where('company_id', $user->company()->id)], + ]; + } + + public function prepareForValidation() + { + $input = $this->all(); + + $this->entity_plural = Str::plural($input['entity']) ?? ''; + $input['entity_id'] = $this->decodePrimaryKey($input['entity_id']); + + $this->replace($input); + } + +} diff --git a/routes/api.php b/routes/api.php index 34e6c57c308b..aa4e108a5273 100644 --- a/routes/api.php +++ b/routes/api.php @@ -58,6 +58,7 @@ use App\Http\Controllers\TaskStatusController; use App\Http\Controllers\Bank\YodleeController; use App\Http\Controllers\CompanyUserController; use App\Http\Controllers\PaymentTermController; +use App\Http\Controllers\EmailHistoryController; use App\Http\Controllers\GroupSettingController; use App\Http\Controllers\OneTimeTokenController; use App\Http\Controllers\SubscriptionController; @@ -202,6 +203,8 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('documents/bulk', [DocumentController::class, 'bulk'])->name('documents.bulk'); Route::post('emails', [EmailController::class, 'send'])->name('email.send')->middleware('user_verified'); + Route::post('emails/clientHistory/{client}', [EmailHistoryController::class, 'clientHistory'])->name('email.clientHistory'); + Route::post('emails/entityHistory', [EmailHistoryController::class, 'entityHistory'])->name('email.entityHistory'); Route::resource('expenses', ExpenseController::class); // name = (expenses. index / create / show / update / destroy / edit Route::put('expenses/{expense}/upload', [ExpenseController::class, 'upload']); From 17872a3158757ac5baebb8f376db1eb38353d1cd Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 15:02:32 +1000 Subject: [PATCH 05/10] Update docs --- .../Controllers/EmailHistoryController.php | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/EmailHistoryController.php b/app/Http/Controllers/EmailHistoryController.php index 70d52b28d4d1..863ac93d6a3b 100644 --- a/app/Http/Controllers/EmailHistoryController.php +++ b/app/Http/Controllers/EmailHistoryController.php @@ -11,15 +11,11 @@ namespace App\Http\Controllers; -use App\Models\Quote; -use App\Models\Client; -use App\Models\Invoice; -use App\Models\SystemLog; -use App\Models\PurchaseOrder; -use App\Utils\Traits\MakesHash; -use App\Models\RecurringInvoice; use App\Http\Requests\Email\ClientEmailHistoryRequest; use App\Http\Requests\Email\EntityEmailHistoryRequest; +use App\Models\Client; +use App\Models\SystemLog; +use App\Utils\Traits\MakesHash; class EmailHistoryController extends BaseController { @@ -33,17 +29,22 @@ class EmailHistoryController extends BaseController { $data = SystemLog::where('client_id', $client->id) ->where('category_id', SystemLog::CATEGORY_MAIL) - ->orderBy('id','DESC') - ->map(function ($system_log){ - if($system_log->log['history'] ?? false) { - return json_decode($system_log->log['history'], true); - } + ->orderBy('id', 'DESC') + ->map(function ($system_log) { + if($system_log->log['history'] ?? false) { + return json_decode($system_log->log['history'], true); + } }); return response()->json($data, 200); } - + + /** + * May need to expand on this using + * just the message-id and search for the + * entity in the invitations + */ public function entityHistory(EntityEmailHistoryRequest $request) { /** @var \App\Models\User $user */ @@ -62,4 +63,4 @@ class EmailHistoryController extends BaseController return response()->json($data, 200); } -} \ No newline at end of file +} From 2f1968c899d9b75a445977e1141e4cc975662fc6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 15:03:48 +1000 Subject: [PATCH 06/10] Fixes for static analysis --- app/PaymentDrivers/SquarePaymentDriver.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/PaymentDrivers/SquarePaymentDriver.php b/app/PaymentDrivers/SquarePaymentDriver.php index 9a14358be634..5e6e3ecbb738 100644 --- a/app/PaymentDrivers/SquarePaymentDriver.php +++ b/app/PaymentDrivers/SquarePaymentDriver.php @@ -394,8 +394,10 @@ class SquarePaymentDriver extends BaseDriver //getsubscriptionid here $subscription_id = $this->checkWebhooks(); - if(!$subscription_id) - return nlog('No Subscription Found'); + if(!$subscription_id){ + nlog('No Subscription Found'); + return; + } $api_response = $this->square->getWebhookSubscriptionsApi()->testWebhookSubscription($subscription_id, $body); From 21f14694994894a3b59dfc2c315d1d3fd352719e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 17:13:46 +1000 Subject: [PATCH 07/10] Tests for entity history --- .../Controllers/EmailHistoryController.php | 8 +- tests/Feature/InvoiceEmailTest.php | 97 ++++++++++++++++++- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/EmailHistoryController.php b/app/Http/Controllers/EmailHistoryController.php index 863ac93d6a3b..e8f2d4894fda 100644 --- a/app/Http/Controllers/EmailHistoryController.php +++ b/app/Http/Controllers/EmailHistoryController.php @@ -30,9 +30,11 @@ class EmailHistoryController extends BaseController $data = SystemLog::where('client_id', $client->id) ->where('category_id', SystemLog::CATEGORY_MAIL) ->orderBy('id', 'DESC') + ->cursor() ->map(function ($system_log) { if($system_log->log['history'] ?? false) { - return json_decode($system_log->log['history'], true); + return $system_log->log['history']; + // return json_decode($system_log->log['history'], true); } }); @@ -54,9 +56,11 @@ class EmailHistoryController extends BaseController ->where('category_id', SystemLog::CATEGORY_MAIL) ->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id)) ->orderBy('id', 'DESC') + ->cursor() ->map(function ($system_log) { if($system_log->log['history'] ?? false) { - return json_decode($system_log->log['history'], true); + return $system_log->log['history']; + // return json_decode($system_log->log['history'], true); } }); diff --git a/tests/Feature/InvoiceEmailTest.php b/tests/Feature/InvoiceEmailTest.php index 9609840e8ae2..2eb3e2da172f 100644 --- a/tests/Feature/InvoiceEmailTest.php +++ b/tests/Feature/InvoiceEmailTest.php @@ -11,14 +11,15 @@ namespace Tests\Feature; +use Tests\TestCase; +use App\Models\SystemLog; +use Tests\MockAccountData; use App\Jobs\Entity\EmailEntity; use App\Utils\Traits\GeneratesCounter; use Illuminate\Database\Eloquent\Model; -use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; -use Tests\MockAccountData; -use Tests\TestCase; use Illuminate\Validation\ValidationException; +use Illuminate\Foundation\Testing\DatabaseTransactions; /** * @test @@ -46,6 +47,96 @@ class InvoiceEmailTest extends TestCase } + public function testClientEmailHistory() + { + $system_log = new SystemLog(); + $system_log->company_id = $this->company->id; + $system_log->client_id = $this->client->id; + $system_log->category_id = SystemLog::CATEGORY_MAIL; + $system_log->event_id = SystemLog::EVENT_MAIL_SEND; + $system_log->type_id = SystemLog::TYPE_WEBHOOK_RESPONSE; + $system_log->log = [ + 'history' => [ + 'entity_id' => $this->invoice->hashed_id, + 'entity_type' => 'invoice', + 'subject' => 'Invoice #1', + 'events' => [ + [ + 'recipient' => 'bob@gmail.com', + 'status' => 'Delivered', + 'delivery_message' => 'A message that was deliveryed', + 'server' => 'email.mx.com', + 'server_ip' => '127.0.0.1', + 'date' => \Carbon\Carbon::parse('2023-10-10')->format('Y-m-d H:m:s') ?? '', + ], + ], + ] + ]; + + $system_log->save(); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/emails/clientHistory/'.$this->client->hashed_id); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('invoice', $arr[0]['entity_type']); + + } + + public function testEntityEmailHistory() + { + $system_log = new SystemLog(); + $system_log->company_id = $this->company->id; + $system_log->client_id = $this->client->id; + $system_log->category_id = SystemLog::CATEGORY_MAIL; + $system_log->event_id = SystemLog::EVENT_MAIL_SEND; + $system_log->type_id = SystemLog::TYPE_WEBHOOK_RESPONSE; + $system_log->log = [ + 'history' => [ + 'entity_id' => $this->invoice->hashed_id, + 'entity_type' => 'invoice', + 'subject' => 'Invoice #1', + 'events' => [ + [ + 'recipient' => 'bob@gmail.com', + 'status' => 'Delivered', + 'delivery_message' => 'A message that was deliveryed', + 'server' => 'email.mx.com', + 'server_ip' => '127.0.0.1', + 'date' => \Carbon\Carbon::parse('2023-10-10')->format('Y-m-d H:m:s') ?? '', + ], + ], + ] + ]; + + $system_log->save(); + + $data = [ + 'entity' => 'invoice', + 'entity_id' => $this->invoice->hashed_id, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/emails/entityHistory/', $data); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('invoice', $arr[0]['entity_type']); + $this->assertEquals($this->invoice->hashed_id, $arr[0]['entity_id']); + + } + + public function testTemplateValidation() { $data = [ From 2c7c60f9859bb553027b7529d5ec7d9ac2fb1374 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 17:36:39 +1000 Subject: [PATCH 08/10] Additional tests for System Logs --- tests/Feature/InvoiceEmailTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Feature/InvoiceEmailTest.php b/tests/Feature/InvoiceEmailTest.php index 2eb3e2da172f..0105f25105aa 100644 --- a/tests/Feature/InvoiceEmailTest.php +++ b/tests/Feature/InvoiceEmailTest.php @@ -87,6 +87,12 @@ class InvoiceEmailTest extends TestCase $this->assertEquals('invoice', $arr[0]['entity_type']); + $count = SystemLog::where('client_id', $this->client->id) + ->where('category_id', SystemLog::CATEGORY_MAIL) + ->orderBy('id', 'DESC') + ->count(); + + $this->assertEquals(1, $count); } public function testEntityEmailHistory() @@ -134,6 +140,13 @@ class InvoiceEmailTest extends TestCase $this->assertEquals('invoice', $arr[0]['entity_type']); $this->assertEquals($this->invoice->hashed_id, $arr[0]['entity_id']); + $count = SystemLog::where('company_id', $this->company->id) + ->where('category_id', SystemLog::CATEGORY_MAIL) + ->whereJsonContains('log->history->entity_id', $this->invoice->hashed_id) + ->count(); + + $this->assertEquals(1, $count); + } From dee0b51771294a9a7de789e99738df759410a7b1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 20:36:25 +1000 Subject: [PATCH 09/10] Fixes for reports --- app/Export/CSV/CreditExport.php | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/app/Export/CSV/CreditExport.php b/app/Export/CSV/CreditExport.php index 234a9f9c10e4..69c396ff8f72 100644 --- a/app/Export/CSV/CreditExport.php +++ b/app/Export/CSV/CreditExport.php @@ -86,15 +86,23 @@ class CreditExport extends BaseExport { $query = $this->init(); - $header = $this->buildHeader(); + $headerdisplay = $this->buildHeader(); + $header = []; + + foreach ($this->input['report_keys'] as $key => $value) { + $header[] = ['identifier' => $value, 'display_value' => $headerdisplay[$key]]; + } $report = $query->cursor() ->map(function ($credit) { $row = $this->buildRow($credit); return $this->processMetaData($row, $credit); })->toArray(); + + nlog(array_merge(['column' => $header], $report)); - return array_merge([$header], $report); + + return array_merge(['column' => $header], $report); } private function processMetaData(array $row, Credit $credit): array @@ -112,7 +120,8 @@ class CreditExport extends BaseExport $clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0]; $clean_row[$key]['hashed_id'] = $report_keys[0] == 'credit' ? null : $credit->{$report_keys[0]}->hashed_id ?? null; $clean_row[$key]['value'] = $row[$column_key]; - + $clean_row[$key]['identifier'] = $value; + if(in_array($clean_row[$key]['id'], ['amount', 'balance', 'partial', 'refunded', 'applied','unit_cost','cost','price'])) $clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $credit->client); else @@ -134,14 +143,6 @@ class CreditExport extends BaseExport if (count($this->input['report_keys']) == 0) { $this->input['report_keys'] = array_values($this->entity_keys); - // $this->input['report_keys'] = collect(array_values($this->entity_keys))->map(function ($value){ - - // // if(in_array($value,['client_id','country_id'])) - // // return $value; - // // else - // return 'credit.'.$value; - // })->toArray(); - } $query = Credit::query() From fcb3e039cea746fbafcb8ad89621d44190c2b54f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 23 Aug 2023 20:36:56 +1000 Subject: [PATCH 10/10] Fixes for reports --- app/Export/CSV/CreditExport.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/Export/CSV/CreditExport.php b/app/Export/CSV/CreditExport.php index 69c396ff8f72..2cf25cb56899 100644 --- a/app/Export/CSV/CreditExport.php +++ b/app/Export/CSV/CreditExport.php @@ -99,10 +99,7 @@ class CreditExport extends BaseExport return $this->processMetaData($row, $credit); })->toArray(); - nlog(array_merge(['column' => $header], $report)); - - - return array_merge(['column' => $header], $report); + return array_merge(['columns' => $header], $report); } private function processMetaData(array $row, Credit $credit): array @@ -121,7 +118,7 @@ class CreditExport extends BaseExport $clean_row[$key]['hashed_id'] = $report_keys[0] == 'credit' ? null : $credit->{$report_keys[0]}->hashed_id ?? null; $clean_row[$key]['value'] = $row[$column_key]; $clean_row[$key]['identifier'] = $value; - + if(in_array($clean_row[$key]['id'], ['amount', 'balance', 'partial', 'refunded', 'applied','unit_cost','cost','price'])) $clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $credit->client); else