diff --git a/app/Mail/Client/ClientStatement.php b/app/Mail/Client/ClientStatement.php new file mode 100644 index 000000000000..5c3c3c211263 --- /dev/null +++ b/app/Mail/Client/ClientStatement.php @@ -0,0 +1,106 @@ + [], + // 'from_email' => '', + // 'from_name' => '', + // 'reply_to' => '', + // 'cc' => [], + // 'bcc' => [], + // 'subject' => ctrans('texts.your_statement'), + // 'body' => ctrans('texts.client_statement_body', ['start_date' => $this->client_start_date, 'end_date' => $this->client_end_date]), + // 'attachments' => [ + // ['name' => ctrans('texts.statement') . ".pdf", 'file' => base64_encode($pdf)], + // ] + + /** + * Create a new message instance. + * + * @return void + */ + public function __construct(public array $data){} + + /** + * Get the message envelope. + * + * @return \Illuminate\Mail\Mailables\Envelope + */ + public function envelope() + { + return new Envelope( + subject: $this->data['subject'], + tags: [$this->data['company_key']], + replyTo: $this->data['reply_to'], + from: $this->data['from'], + to: $this->data['to'], + bcc: $this->data['bcc'] + ); + } + + /** + * Get the message content definition. + * + * @return \Illuminate\Mail\Mailables\Content + */ + public function content() + { + return new Content( + view: 'email.template.client', + text: 'email.template.text', + with: [ + 'text_body' => $this->data['body'], + 'body' => $this->data['body'], + 'whitelabel' => $this->data['whitelabel'], + 'settings' => $this->data['settings'], + 'whitelabel' => $this->data['whitelabel'], + 'logo' => $this->data['logo'], + 'signature' => $this->data['signature'], + 'company' => $this->data['company'], + 'greeting' => $this->data['greeting'], + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments() + { + $array_of_attachments = []; + + foreach($this->data['attachments'] as $attachment) + { + + $array_of_attachments[] = + Attachment::fromData(fn () => base64_decode($attachment['file']), $attachment['name']) + ->withMime('application/pdf'); + + } + + return $array_of_attachments; + + } +} diff --git a/app/Services/Scheduler/SchedulerService.php b/app/Services/Scheduler/SchedulerService.php index 684fe282c442..11f0f2a7ed18 100644 --- a/app/Services/Scheduler/SchedulerService.php +++ b/app/Services/Scheduler/SchedulerService.php @@ -11,17 +11,26 @@ namespace App\Services\Scheduler; +use App\Mail\Client\ClientStatement; use App\Models\Client; use App\Models\Scheduler; +use App\Utils\Ninja; +use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesHash; +use Illuminate\Mail\Mailables\Address; +use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Str; class SchedulerService { use MakesHash; + use MakesDates; private string $method; + private Client $client; + public function __construct(public Scheduler $scheduler) {} /** @@ -43,13 +52,21 @@ class SchedulerService if(count($this->scheduler->parameters['clients']) >= 1) $query->where('id', $this->transformKeys($this->scheduler->parameters['clients'])); - $statement_properties = $this->calculateStatementProperties(); $query->cursor() - ->each(function ($client) use($statement_properties){ + ->each(function ($_client){ + + $this->client = $_client; + $statement_properties = $this->calculateStatementProperties(); //work out the date range - $pdf = $client->service()->statement($statement_properties); + $pdf = $_client->service()->statement($statement_properties); + + $mail_able_envelope = $this->buildMailableData($pdf); + + Mail::send($mail_able_envelope); + + //calculate next run dates; }); @@ -59,12 +76,15 @@ class SchedulerService { $start_end = $this->calculateStartAndEndDates(); + $this->client_start_date = $this->translateDate($start_end[0], $this->client->date_format(), $this->client->locale()); + $this->client_end_date = $this->translateDate($start_end[1], $this->client->date_format(), $this->client->locale()); + return [ 'start_date' =>$start_end[0], 'end_date' =>$start_end[1], 'show_payments_table' => $this->scheduler->parameters['show_payments_table'], 'show_aging_table' => $this->scheduler->parameters['show_aging_table'], - 'status' => $this->scheduler->status + 'status' => $this->scheduler->parameters['status'] ]; } @@ -74,16 +94,92 @@ class SchedulerService return match ($this->scheduler->parameters['date_range']) { 'this_month' => [now()->firstOfMonth()->format('Y-m-d'), now()->lastOfMonth()->format('Y-m-d')], 'this_quarter' => [now()->firstOfQuarter()->format('Y-m-d'), now()->lastOfQuarter()->format('Y-m-d')], - 'this_year' => [now()->firstOfYear()->format('Y-m-d'), now()->format('Y-m-d')], + 'this_year' => [now()->firstOfYear()->format('Y-m-d'), now()->lastOfYear()->format('Y-m-d')], 'previous_month' => [now()->subMonth()->firstOfMonth()->format('Y-m-d'), now()->subMonth()->lastOfMonth()->format('Y-m-d')], 'previous_quarter' => [now()->subQuarter()->firstOfQuarter()->format('Y-m-d'), now()->subQuarter()->lastOfQuarter()->format('Y-m-d')], - 'previous_year' => [now()->subYear()->firstOfYear()->format('Y-m-d'), now()->subYear()->format('Y-m-d')], - 'custom_range' => [$this->scheduler->parameters['start_date'], $this->scheduler->parameters['end_date']] + 'previous_year' => [now()->subYear()->firstOfYear()->format('Y-m-d'), now()->subYear()->lastOfYear()->format('Y-m-d')], + 'custom_range' => [$this->scheduler->parameters['start_date'], $this->scheduler->parameters['end_date']], + 'default' => [now()->firstOfMonth()->format('Y-m-d'), now()->lastOfMonth()->format('Y-m-d')], }; } - private function thisMonth() + private function buildMailableData($pdf) { + App::setLocale($this->client->locale()); + $primary_contact = $this->client->primary_contact()->first(); + $settings = $this->client->getMergedSettings(); + + App::forgetInstance('translator'); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($settings)); + + $data = [ + 'to' => [new Address($this->client->present()->email(), $this->client->present()->name())], + 'from' => new Address($this->client->company->owner()->email, $this->client->company->owner()->name()), + 'reply_to' => [$this->buildReplyTo($settings)], + 'cc' => $this->buildCc($settings), + 'bcc' => $this->buildBcc($settings), + 'subject' => ctrans('texts.your_statement'), + 'body' => ctrans('texts.client_statement_body', ['start_date' => $this->client_start_date, 'end_date' => $this->client_end_date]), + 'attachments' => [ + ['name' => ctrans('texts.statement') . ".pdf", 'file' => base64_encode($pdf)], + ], + 'company_key' => $this->client->company->company_key, + 'settings' => $settings, + 'whitelabel' => $this->client->user->account->isPaid() ? true : false, + 'logo' => $this->client->company->present()->logo($settings), + 'signature' => $settings->email_signature, + 'company' => $this->client->company, + 'greeting' => ctrans('texts.email_salutation', ['name' => $primary_contact->present()->name()]), + ]; + + return new ClientStatement($data); } + + private function buildReplyTo($settings) + { + + $reply_to_email = str_contains($settings->reply_to_email, "@") ? $settings->reply_to_email : $this->client->company->owner()->email; + + $reply_to_name = strlen($settings->reply_to_name) > 3 ? $settings->reply_to_name : $this->client->company->owner()->present()->name(); + + return new Address($reply_to_email, $reply_to_name); + + } + + private function buildBcc($settings): array + { + $bccs = false; + $bcc_array = []; + + if (strlen($settings->bcc_email) > 1) { + + if (Ninja::isHosted() && $this->client->company->account->isPaid()) { + $bccs = array_slice(explode(',', str_replace(' ', '', $settings->bcc_email)), 0, 2); + } else { + $bccs(explode(',', str_replace(' ', '', $settings->bcc_email))); + } + } + + if(!$bccs) + return $bcc_array; + + foreach($bccs as $bcc) + { + $bcc_array[] = new Address($bcc); + } + + return $bcc_array; + + } + + private function buildCc($settings) + { + return [ + + ]; + } + + } \ No newline at end of file diff --git a/lang/en/texts.php b/lang/en/texts.php index 388d200053c7..f983eaf0238a 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -4924,6 +4924,7 @@ $LANG = array( 'action_add_to_invoice' => 'Add To Invoice', 'danger_zone' => 'Danger Zone', 'import_completed' => 'Import completed', + 'client_statement_body' => 'Your statement from :start_date to :end_date is attached.' ); diff --git a/tests/Feature/Scheduler/SchedulerTest.php b/tests/Feature/Scheduler/SchedulerTest.php index 3e67bd79ea8e..f9eff751acf9 100644 --- a/tests/Feature/Scheduler/SchedulerTest.php +++ b/tests/Feature/Scheduler/SchedulerTest.php @@ -98,12 +98,12 @@ class SchedulerTest extends TestCase 'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY, 'next_run' => '2023-01-14', 'template' => 'client_statement', - 'clients' => [], 'parameters' => [ 'date_range' => 'last_month', 'show_payments_table' => true, 'show_aging_table' => true, - 'status' => 'paid' + 'status' => 'paid', + 'clients' => [], ], ];