diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index 78caf1f26929..4aa6dde3e805 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -510,7 +510,6 @@ class PaymentController extends BaseController
$payments->each(function ($payment, $key) use ($action) {
if (auth()->user()->can('edit', $payment)) {
$this->performAction($payment, $action, true);
- // $this->payment_repo->{$action}($payment);
}
});
diff --git a/app/Jobs/Mail/BaseMailerJob.php b/app/Jobs/Mail/BaseMailerJob.php
index bc1a1e8ab5aa..b4b840756acd 100644
--- a/app/Jobs/Mail/BaseMailerJob.php
+++ b/app/Jobs/Mail/BaseMailerJob.php
@@ -69,7 +69,7 @@ class BaseMailerJob implements ShouldQueue
}
public function logMailError($errors, $recipient_object)
- {
+ {info(print_r($errors,1));
SystemLogger::dispatch(
$errors,
SystemLog::CATEGORY_MAIL,
diff --git a/app/Jobs/Payment/EmailPayment.php b/app/Jobs/Payment/EmailPayment.php
index 8c3487a6824c..283b3265b1a9 100644
--- a/app/Jobs/Payment/EmailPayment.php
+++ b/app/Jobs/Payment/EmailPayment.php
@@ -2,6 +2,7 @@
namespace App\Jobs\Payment;
+use App\DataMapper\Analytics\EmailInvoiceFailure;
use App\Events\Invoice\InvoiceWasEmailed;
use App\Events\Invoice\InvoiceWasEmailedAndFailed;
use App\Events\Payment\PaymentWasEmailed;
@@ -10,7 +11,9 @@ use App\Helpers\Email\BuildEmail;
use App\Jobs\Mail\BaseMailerJob;
use App\Jobs\Utils\SystemLogger;
use App\Libraries\MultiDB;
+use App\Mail\Engine\PaymentEmailEngine;
use App\Mail\TemplateEmail;
+use App\Models\ClientContact;
use App\Models\Company;
use App\Models\Payment;
use App\Models\SystemLog;
@@ -21,6 +24,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
+use Turbo124\Beacon\Facades\LightLogs;
class EmailPayment extends BaseMailerJob implements ShouldQueue
{
@@ -33,6 +37,8 @@ class EmailPayment extends BaseMailerJob implements ShouldQueue
private $contact;
private $company;
+
+ public $settings;
/**
* Create a new job instance.
*
@@ -41,12 +47,12 @@ class EmailPayment extends BaseMailerJob implements ShouldQueue
* @param $contact
* @param $company
*/
- public function __construct(Payment $payment, $email_builder, $contact, company)
+ public function __construct(Payment $payment, Company $company, ClientContact $contact)
{
$this->payment = $payment;
- $this->email_builder = $email_builder;
$this->contact = $contact;
$this->company = $company;
+ $this->settings = $payment->client->getMergedSettings();
}
/**
@@ -62,14 +68,15 @@ class EmailPayment extends BaseMailerJob implements ShouldQueue
if ($this->contact->email) {
- MultiDB::setDb($this->payment->company->db); //this may fail if we don't pass the serialized object with the company record
- //todo fix!!
+ MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
+ $email_builder = (new PaymentEmailEngine($this->payment, $this->contact))->build();
+
Mail::to($this->contact->email, $this->contact->present()->name())
- ->send(new TemplateEmail($this->email_builder, $this->contact->user, $this->contact->customer));
+ ->send(new TemplateEmail($email_builder, $this->contact->user, $this->contact->client));
if (count(Mail::failures()) > 0) {
event(new PaymentWasEmailedAndFailed($this->payment, Mail::failures(), Ninja::eventVars()));
@@ -77,21 +84,22 @@ class EmailPayment extends BaseMailerJob implements ShouldQueue
return $this->logMailError(Mail::failures());
}
- //fire any events
event(new PaymentWasEmailed($this->payment, $this->payment->company, Ninja::eventVars()));
- //sleep(5);
}
}
- private function logMailError($errors)
+ public function failed($exception = null)
{
- SystemLogger::dispatch(
- $errors,
- SystemLog::CATEGORY_MAIL,
- SystemLog::EVENT_MAIL_SEND,
- SystemLog::TYPE_FAILURE,
- $this->payment->client
- );
+ info('the job failed');
+
+ $job_failure = new EmailInvoiceFailure();
+ $job_failure->string_metric5 = 'payment';
+ $job_failure->string_metric6 = $exception->getMessage();
+
+ LightLogs::create($job_failure)
+ ->batch();
+
}
+
}
diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php
new file mode 100644
index 000000000000..e95942ce34a1
--- /dev/null
+++ b/app/Mail/Engine/PaymentEmailEngine.php
@@ -0,0 +1,262 @@
+payment = $payment;
+ $this->company = $payment->company;
+ $this->client = $payment->client;
+ $this->contact = $contact ?: $this->client->primary_contact()->first();
+ $this->settings = $this->client->getMergedSettings();
+ $this->template_data = $template_data;
+ }
+
+ public function build()
+ {
+
+ if(is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0)
+ $body_template = $this->template_data['body'];
+ elseif(strlen($this->client->getSetting('email_template_payment')) > 0)
+ $body_template = $this->client->getSetting('email_template_payment');
+ else{
+ $body_template = EmailTemplateDefaults::getDefaultTemplate('email_template_payment', $this->client->locale());
+ }
+
+ /* Use default translations if a custom message has not been set*/
+ if (iconv_strlen($body_template) == 0) {
+ $body_template = trans(
+ 'texts.payment_message',
+ ['amount' => $payment->amount, 'company' => $payment->company->present()->name()],
+ null,
+ $this->client->locale()
+ );
+ }
+
+ if(is_array($this->template_data) && array_key_exists('subject', $this->template_data) && strlen($this->template_data['subject']) > 0){
+ $subject_template = $this->template_data['subject'];
+ }
+ elseif(strlen($this->client->getSetting('email_subject_payment')) > 0){
+ $subject_template = $this->client->getSetting('email_subject_payment');
+ }
+ else{
+ $subject_template = EmailTemplateDefaults::getDefaultTemplate('email_subject_payment', $this->client->locale());
+ }
+
+ if (iconv_strlen($subject_template) == 0) {
+ $subject_template = trans(
+ 'texts.payment_subject',
+ ['number' => $payment->number, 'company' => $payment->company->present()->name()],
+ null,
+ $this->client->locale()
+ );
+ }
+
+ $this->setTemplate($this->client->getSetting('email_style'))
+ ->setContact($this->contact)
+ ->setVariables($this->makeValues())
+ ->setSubject($subject_template)
+ ->setBody($body_template)
+ ->setFooter('')
+ ->setViewLink('')
+ ->setViewText('');
+
+ return $this;
+
+ }
+
+
+ private function makePaymentVariables()
+ {
+ $data = [];
+
+ $data['$from'] = ['value' => '', 'label' => ctrans('texts.from')];
+ $data['$to'] = ['value' => '', 'label' => ctrans('texts.to')];
+ $data['$number'] = ['value' => $this->payment->number ?: ' ', 'label' => ctrans('texts.payment_number')];
+ $data['$entity'] = ['value' => '', 'label' => ctrans('texts.payment')];
+ $data['$payment.amount'] = ['value' => Number::formatMoney($this->payment->amount, $this->client) ?: ' ', 'label' => ctrans('texts.amount')];
+ $data['$amount'] = &$data['$payment.amount'];
+ $data['$payment.date'] = ['value' => $this->formatDate($this->payment->date, $this->client->date_format()), 'label' => ctrans('texts.payment_date')];
+ $data['$transaction_reference'] = ['value' => $this->payment->transaction_reference, 'label' => ctrans('texts.transaction_reference')];
+ $data['$public_notes'] = ['value' => $this->payment->public_notes, 'label' => ctrans('texts.notes')];
+
+ $data['$payment1'] = ['value' => $this->formatCustomFieldValue('payment1', $this->payment->custom_value1) ?: ' ', 'label' => $this->makeCustomField('payment1')];
+ $data['$payment2'] = ['value' => $this->formatCustomFieldValue('payment2', $this->payment->custom_value2) ?: ' ', 'label' => $this->makeCustomField('payment2')];
+ $data['$payment3'] = ['value' => $this->formatCustomFieldValue('payment3', $this->payment->custom_value3) ?: ' ', 'label' => $this->makeCustomField('payment3')];
+ $data['$payment4'] = ['value' => $this->formatCustomFieldValue('payment4', $this->payment->custom_value4) ?: ' ', 'label' => $this->makeCustomField('payment4')];
+ // $data['$type'] = ['value' => $this->payment->type->name ?: '', 'label' => ctrans('texts.payment_type')];
+
+ $data['$client1'] = ['value' => $this->formatCustomFieldValue('client1', $this->client->custom_value1) ?: ' ', 'label' => $this->makeCustomField('client1')];
+ $data['$client2'] = ['value' => $this->formatCustomFieldValue('client2', $this->client->custom_value2) ?: ' ', 'label' => $this->makeCustomField('client2')];
+ $data['$client3'] = ['value' => $this->formatCustomFieldValue('client3', $this->client->custom_value3) ?: ' ', 'label' => $this->makeCustomField('client3')];
+ $data['$client4'] = ['value' => $this->formatCustomFieldValue('client4', $this->client->custom_value4) ?: ' ', 'label' => $this->makeCustomField('client4')];
+ $data['$address1'] = ['value' => $this->client->address1 ?: ' ', 'label' => ctrans('texts.address1')];
+ $data['$address2'] = ['value' => $this->client->address2 ?: ' ', 'label' => ctrans('texts.address2')];
+ $data['$id_number'] = ['value' => $this->client->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
+ $data['$vat_number'] = ['value' => $this->client->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
+ $data['$website'] = ['value' => $this->client->present()->website() ?: ' ', 'label' => ctrans('texts.website')];
+ $data['$phone'] = ['value' => $this->client->present()->phone() ?: ' ', 'label' => ctrans('texts.phone')];
+ $data['$country'] = ['value' => isset($this->client->country->name) ? $this->client->country->name : '', 'label' => ctrans('texts.country')];
+ $data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
+ $data['$client_name'] = ['value' => $this->client->present()->name() ?: ' ', 'label' => ctrans('texts.client_name')];
+ $data['$client.name'] = &$data['$client_name'];
+ $data['$client.address1'] = &$data['$address1'];
+ $data['$client.address2'] = &$data['$address2'];
+ $data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
+ $data['$client.address'] = &$data['$client_address'];
+ $data['$client.id_number'] = &$data['$id_number'];
+ $data['$client.vat_number'] = &$data['$vat_number'];
+ $data['$client.website'] = &$data['$website'];
+ $data['$client.phone'] = &$data['$phone'];
+ $data['$city_state_postal'] = ['value' => $this->client->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
+ $data['$client.city_state_postal'] = &$data['$city_state_postal'];
+ $data['$postal_city_state'] = ['value' => $this->client->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
+ $data['$client.postal_city_state'] = &$data['$postal_city_state'];
+ $data['$client.country'] = &$data['$country'];
+ $data['$client.email'] = &$data['$email'];
+
+ $data['$client.balance'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')];
+ $data['$outstanding'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')];
+ $data['$client_balance'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')];
+ $data['$paid_to_date'] = ['value' => Number::formatMoney($this->client->paid_to_date, $this->client), 'label' => ctrans('texts.paid_to_date')];
+
+ $data['$contact.full_name'] = ['value' => $this->contact->present()->name(), 'label' => ctrans('texts.name')];
+ $data['$contact.email'] = ['value' => $this->contact->email, 'label' => ctrans('texts.email')];
+ $data['$contact.phone'] = ['value' => $this->contact->phone, 'label' => ctrans('texts.phone')];
+
+ $data['$contact.name'] = ['value' => isset($this->contact) ? $this->contact->present()->name() : 'no contact name on record', 'label' => ctrans('texts.contact_name')];
+ $data['$contact.first_name'] = ['value' => isset($this->contact) ? $this->contact->first_name : '', 'label' => ctrans('texts.first_name')];
+ $data['$contact.last_name'] = ['value' => isset($this->contact) ? $this->contact->last_name : '', 'label' => ctrans('texts.last_name')];
+ $data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : ' ', 'label' => $this->makeCustomField('contact1')];
+ $data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : ' ', 'label' => $this->makeCustomField('contact1')];
+ $data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : ' ', 'label' => $this->makeCustomField('contact1')];
+ $data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : ' ', 'label' => $this->makeCustomField('contact1')];
+
+ $data['$company.city_state_postal'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
+ $data['$company.postal_city_state'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
+ $data['$company.name'] = ['value' => $this->company->present()->name() ?: ' ', 'label' => ctrans('texts.company_name')];
+ $data['$company.address1'] = ['value' => $this->settings->address1 ?: ' ', 'label' => ctrans('texts.address1')];
+ $data['$company.address2'] = ['value' => $this->settings->address2 ?: ' ', 'label' => ctrans('texts.address2')];
+ $data['$company.city'] = ['value' => $this->settings->city ?: ' ', 'label' => ctrans('texts.city')];
+ $data['$company.state'] = ['value' => $this->settings->state ?: ' ', 'label' => ctrans('texts.state')];
+ $data['$company.postal_code'] = ['value' => $this->settings->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
+ //$data['$company.country'] = ['value' => $this->getCountryName(), 'label' => ctrans('texts.country')];
+ $data['$company.phone'] = ['value' => $this->settings->phone ?: ' ', 'label' => ctrans('texts.phone')];
+ $data['$company.email'] = ['value' => $this->settings->email ?: ' ', 'label' => ctrans('texts.email')];
+ $data['$company.vat_number'] = ['value' => $this->settings->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
+ $data['$company.id_number'] = ['value' => $this->settings->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
+ $data['$company.website'] = ['value' => $this->settings->website ?: ' ', 'label' => ctrans('texts.website')];
+ $data['$company.address'] = ['value' => $this->company->present()->address($this->settings) ?: ' ', 'label' => ctrans('texts.address')];
+
+ $logo = $this->company->present()->logo($this->settings);
+
+ $data['$company.logo'] = ['value' => $logo ?: ' ', 'label' => ctrans('texts.logo')];
+ $data['$company_logo'] = &$data['$company.logo'];
+ $data['$company1'] = ['value' => $this->formatCustomFieldValue('company1', $this->settings->custom_value1) ?: ' ', 'label' => $this->makeCustomField('company1')];
+ $data['$company2'] = ['value' => $this->formatCustomFieldValue('company2', $this->settings->custom_value2) ?: ' ', 'label' => $this->makeCustomField('company2')];
+ $data['$company3'] = ['value' => $this->formatCustomFieldValue('company3', $this->settings->custom_value3) ?: ' ', 'label' => $this->makeCustomField('company3')];
+ $data['$company4'] = ['value' => $this->formatCustomFieldValue('company4', $this->settings->custom_value4) ?: ' ', 'label' => $this->makeCustomField('company4')];
+
+ $data['$view_link'] = ['value' => ''.ctrans('texts.view_payment').'', 'label' => ctrans('texts.view_payment')];
+ $data['$view_url'] = ['value' => $this->payment->getLink(), 'label' => ctrans('texts.view_payment')];
+
+ $data['$invoices'] = ['value' => $this->formatInvoices(), 'label' => ctrans('texts.invoices')];
+
+ return $data;
+ }
+
+ private function formatInvoices()
+ {
+ $invoice_list = '';
+
+ foreach($this->payment->invoices as $invoice)
+ {
+ $invoice_list .= ctrans('texts.invoice_number_short') . " {$invoice->number} - " . Number::formatMoney($invoice->pivot->amount, $this->client) . "
";
+ }
+
+ return $invoice_list;
+ }
+
+ private function makeCustomField($field) :string
+ {
+ $custom_fields = $this->company->custom_fields;
+
+ if ($custom_fields && property_exists($custom_fields, $field)) {
+ $custom_field = $custom_fields->{$field};
+
+ $custom_field_parts = explode('|', $custom_field);
+
+ return $custom_field_parts[0];
+ }
+
+ return '';
+ }
+
+ private function formatCustomFieldValue($field, $value) :string
+ {
+ $custom_fields = $this->company->custom_fields;
+ $custom_field = '';
+
+ if ($custom_fields && property_exists($custom_fields, $field)) {
+ $custom_field = $custom_fields->{$field};
+ $custom_field_parts = explode('|', $custom_field);
+
+ if(count($custom_field_parts) >= 2)
+ $custom_field = $custom_field_parts[1];
+ }
+
+ switch ($custom_field) {
+ case 'date':
+ return $this->formatDate($value, $this->client->date_format());
+ break;
+
+ default:
+ return is_null($value) ? '' : $value;
+ break;
+ }
+ }
+
+ public function makeValues() :array
+ {
+ $data = [];
+
+ $values = $this->makePaymentVariables();
+
+ foreach ($values as $key => $value) {
+ $data[$key] = $value['value'];
+ }
+
+ return $data;
+ }
+}
+
diff --git a/app/Models/Activity.php b/app/Models/Activity.php
index e271bc920fb4..6ad61022787a 100644
--- a/app/Models/Activity.php
+++ b/app/Models/Activity.php
@@ -80,6 +80,10 @@ class Activity extends StaticModel
const UPDATE_CLIENT = 61; //
const UPDATE_VENDOR = 62; //
+ const INVOICE_REMINDER1_SENT = 63;
+ const INVOICE_REMINDER2_SENT = 64;
+ const INVOICE_REMINDER3_SENT = 65;
+
protected $casts = [
'is_system' => 'boolean',
'updated_at' => 'timestamp',
diff --git a/app/Models/Payment.php b/app/Models/Payment.php
index f938009e0e81..3fcad72d5be5 100644
--- a/app/Models/Payment.php
+++ b/app/Models/Payment.php
@@ -288,4 +288,10 @@ class Payment extends BaseModel
event(new PaymentWasVoided($this, $this->company, Ninja::eventVars()));
}
+
+ public function getLink()
+ {
+ return route('client.payments.show', $this->hashed_id);
+ }
+
}
diff --git a/app/Services/Payment/SendEmail.php b/app/Services/Payment/SendEmail.php
index dc4b42b7d008..388a8cf04add 100644
--- a/app/Services/Payment/SendEmail.php
+++ b/app/Services/Payment/SendEmail.php
@@ -11,7 +11,6 @@
namespace App\Services\Payment;
-use App\Helpers\Email\PaymentEmail;
use App\Jobs\Payment\EmailPayment;
class SendEmail
@@ -33,11 +32,9 @@ class SendEmail
*/
public function run()
{
- $email_builder = (new PaymentEmail())->build($this->payment, $this->contact);
-
- $this->payment->client->contacts->each(function ($contact) use ($email_builder) {
- if ($contact->send && $contact->email) {
- EmailPayment::dispatchNow($this->payment, $email_builder, $contact, $this->payment->company);
+ $this->payment->client->contacts->each(function ($contact) {
+ if ($contact->send_email && $contact->email) {
+ EmailPayment::dispatchNow($this->payment, $this->payment->company, $contact);
}
});
}
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index f6edcc45ee76..5e0815af6611 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -3297,5 +3297,9 @@ return [
'n/a' => 'N/A',
'payment_number' => 'Payment Number',
-
+
+ 'activity_63' => ':user emailed reminder 1 for invoice :invoice to :contact',
+ 'activity_64' => ':user emailed reminder 2 for invoice :invoice to :contact',
+ 'activity_65' => ':user emailed reminder 3 for invoice :invoice to :contact',
+
];