mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-03 13:54:34 -04:00
initial changings
This commit is contained in:
parent
9dd6ead39e
commit
3206e46349
@ -13,5 +13,10 @@ namespace Illuminate\Contracts\Mail
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function brevo_config(string $key)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $auto_archive_invoice = false; // @implemented
|
public $auto_archive_invoice = false; // @implemented
|
||||||
|
|
||||||
public $qr_iban = ''; //@implemented
|
public $qr_iban = ''; //@implemented
|
||||||
|
|
||||||
public $besr_id = ''; //@implemented
|
public $besr_id = ''; //@implemented
|
||||||
|
|
||||||
public $lock_invoices = 'off'; //off,when_sent,when_paid //@implemented
|
public $lock_invoices = 'off'; //off,when_sent,when_paid //@implemented
|
||||||
@ -229,7 +229,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $require_quote_signature = false; //@TODO ben to confirm
|
public $require_quote_signature = false; //@TODO ben to confirm
|
||||||
|
|
||||||
//email settings
|
//email settings
|
||||||
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun' //@implemented
|
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun' , 'client_brevo' //@implemented
|
||||||
|
|
||||||
public $gmail_sending_user_id = '0'; //@implemented
|
public $gmail_sending_user_id = '0'; //@implemented
|
||||||
|
|
||||||
@ -444,13 +444,19 @@ class CompanySettings extends BaseSettings
|
|||||||
public $postmark_secret = '';
|
public $postmark_secret = '';
|
||||||
|
|
||||||
public $custom_sending_email = '';
|
public $custom_sending_email = '';
|
||||||
|
|
||||||
public $mailgun_secret = '';
|
public $mailgun_secret = '';
|
||||||
|
|
||||||
public $mailgun_domain = '';
|
public $mailgun_domain = '';
|
||||||
|
|
||||||
public $mailgun_endpoint = 'api.mailgun.net'; //api.eu.mailgun.net
|
public $mailgun_endpoint = 'api.mailgun.net'; //api.eu.mailgun.net
|
||||||
|
|
||||||
|
public $brevo_secret = '';
|
||||||
|
|
||||||
|
public $brevo_domain = '';
|
||||||
|
|
||||||
|
public $brevo_endpoint = 'api.mailgun.net'; //api.eu.mailgun.net
|
||||||
|
|
||||||
public $auto_bill_standard_invoices = false;
|
public $auto_bill_standard_invoices = false;
|
||||||
|
|
||||||
public $email_alignment = 'center'; // center , left, right
|
public $email_alignment = 'center'; // center , left, right
|
||||||
@ -482,9 +488,9 @@ class CompanySettings extends BaseSettings
|
|||||||
public $enable_e_invoice = false;
|
public $enable_e_invoice = false;
|
||||||
|
|
||||||
public $delivery_note_design_id = '';
|
public $delivery_note_design_id = '';
|
||||||
|
|
||||||
public $statement_design_id = '';
|
public $statement_design_id = '';
|
||||||
|
|
||||||
public $payment_receipt_design_id = '';
|
public $payment_receipt_design_id = '';
|
||||||
|
|
||||||
public $payment_refund_design_id = '';
|
public $payment_refund_design_id = '';
|
||||||
@ -494,279 +500,282 @@ class CompanySettings extends BaseSettings
|
|||||||
public $payment_email_all_contacts = false;
|
public $payment_email_all_contacts = false;
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
'payment_email_all_contacts' => 'bool',
|
'payment_email_all_contacts' => 'bool',
|
||||||
'statement_design_id' => 'string',
|
'statement_design_id' => 'string',
|
||||||
'delivery_note_design_id' => 'string',
|
'delivery_note_design_id' => 'string',
|
||||||
'payment_receipt_design_id' => 'string',
|
'payment_receipt_design_id' => 'string',
|
||||||
'payment_refund_design_id' => 'string',
|
'payment_refund_design_id' => 'string',
|
||||||
'classification' => 'string',
|
'classification' => 'string',
|
||||||
'enable_e_invoice' => 'bool',
|
'enable_e_invoice' => 'bool',
|
||||||
'classification' => 'string',
|
'classification' => 'string',
|
||||||
'default_expense_payment_type_id' => 'string',
|
'default_expense_payment_type_id' => 'string',
|
||||||
'e_invoice_type' => 'string',
|
'e_invoice_type' => 'string',
|
||||||
'mailgun_endpoint' => 'string',
|
'mailgun_endpoint' => 'string',
|
||||||
'client_initiated_payments' => 'bool',
|
'brevo_endpoint' => 'string',
|
||||||
'client_initiated_payments_minimum' => 'float',
|
'client_initiated_payments' => 'bool',
|
||||||
'sync_invoice_quote_columns' => 'bool',
|
'client_initiated_payments_minimum' => 'float',
|
||||||
'show_task_item_description' => 'bool',
|
'sync_invoice_quote_columns' => 'bool',
|
||||||
'allow_billable_task_items' => 'bool',
|
'show_task_item_description' => 'bool',
|
||||||
|
'allow_billable_task_items' => 'bool',
|
||||||
'accept_client_input_quote_approval' => 'bool',
|
'accept_client_input_quote_approval' => 'bool',
|
||||||
'custom_sending_email' => 'string',
|
'custom_sending_email' => 'string',
|
||||||
'show_paid_stamp' => 'bool',
|
'show_paid_stamp' => 'bool',
|
||||||
'show_shipping_address' => 'bool',
|
'show_shipping_address' => 'bool',
|
||||||
'company_logo_size' => 'string',
|
'company_logo_size' => 'string',
|
||||||
'show_email_footer' => 'bool',
|
'show_email_footer' => 'bool',
|
||||||
'email_alignment' => 'string',
|
'email_alignment' => 'string',
|
||||||
'auto_bill_standard_invoices' => 'bool',
|
'auto_bill_standard_invoices' => 'bool',
|
||||||
'postmark_secret' => 'string',
|
'postmark_secret' => 'string',
|
||||||
'mailgun_secret' => 'string',
|
'mailgun_secret' => 'string',
|
||||||
'mailgun_domain' => 'string',
|
'mailgun_domain' => 'string',
|
||||||
'send_email_on_mark_paid' => 'bool',
|
'brevo_secret' => 'string',
|
||||||
'vendor_portal_enable_uploads' => 'bool',
|
'brevo_domain' => 'string',
|
||||||
'besr_id' => 'string',
|
'send_email_on_mark_paid' => 'bool',
|
||||||
'qr_iban' => 'string',
|
'vendor_portal_enable_uploads' => 'bool',
|
||||||
'email_subject_purchase_order' => 'string',
|
'besr_id' => 'string',
|
||||||
'email_template_purchase_order' => 'string',
|
'qr_iban' => 'string',
|
||||||
'require_purchase_order_signature' => 'bool',
|
'email_subject_purchase_order' => 'string',
|
||||||
'purchase_order_public_notes' => 'string',
|
'email_template_purchase_order' => 'string',
|
||||||
'purchase_order_terms' => 'string',
|
'require_purchase_order_signature' => 'bool',
|
||||||
'purchase_order_design_id' => 'string',
|
'purchase_order_public_notes' => 'string',
|
||||||
'purchase_order_footer' => 'string',
|
'purchase_order_terms' => 'string',
|
||||||
'purchase_order_number_pattern' => 'string',
|
'purchase_order_design_id' => 'string',
|
||||||
'page_numbering_alignment' => 'string',
|
'purchase_order_footer' => 'string',
|
||||||
'page_numbering' => 'bool',
|
'purchase_order_number_pattern' => 'string',
|
||||||
'auto_archive_invoice_cancelled' => 'bool',
|
'page_numbering_alignment' => 'string',
|
||||||
'email_from_name' => 'string',
|
'page_numbering' => 'bool',
|
||||||
'show_all_tasks_client_portal' => 'string',
|
'auto_archive_invoice_cancelled' => 'bool',
|
||||||
'entity_send_time' => 'int',
|
'email_from_name' => 'string',
|
||||||
'shared_invoice_credit_counter' => 'bool',
|
'show_all_tasks_client_portal' => 'string',
|
||||||
'reply_to_name' => 'string',
|
'entity_send_time' => 'int',
|
||||||
'hide_empty_columns_on_pdf' => 'bool',
|
'shared_invoice_credit_counter' => 'bool',
|
||||||
'enable_reminder_endless' => 'bool',
|
'reply_to_name' => 'string',
|
||||||
'use_credits_payment' => 'string',
|
'hide_empty_columns_on_pdf' => 'bool',
|
||||||
'recurring_invoice_number_pattern' => 'string',
|
'enable_reminder_endless' => 'bool',
|
||||||
'recurring_invoice_number_counter' => 'int',
|
'use_credits_payment' => 'string',
|
||||||
'client_portal_under_payment_minimum'=> 'float',
|
'recurring_invoice_number_pattern' => 'string',
|
||||||
'auto_bill_date' => 'string',
|
'recurring_invoice_number_counter' => 'int',
|
||||||
'primary_color' => 'string',
|
'client_portal_under_payment_minimum' => 'float',
|
||||||
'secondary_color' => 'string',
|
'auto_bill_date' => 'string',
|
||||||
'client_portal_allow_under_payment' => 'bool',
|
'primary_color' => 'string',
|
||||||
'client_portal_allow_over_payment' => 'bool',
|
'secondary_color' => 'string',
|
||||||
'auto_bill' => 'string',
|
'client_portal_allow_under_payment' => 'bool',
|
||||||
'lock_invoices' => 'string',
|
'client_portal_allow_over_payment' => 'bool',
|
||||||
'client_portal_terms' => 'string',
|
'auto_bill' => 'string',
|
||||||
'client_portal_privacy_policy' => 'string',
|
'lock_invoices' => 'string',
|
||||||
'client_can_register' => 'bool',
|
'client_portal_terms' => 'string',
|
||||||
'portal_design_id' => 'string',
|
'client_portal_privacy_policy' => 'string',
|
||||||
'late_fee_endless_percent' => 'float',
|
'client_can_register' => 'bool',
|
||||||
'late_fee_endless_amount' => 'float',
|
'portal_design_id' => 'string',
|
||||||
'auto_email_invoice' => 'bool',
|
'late_fee_endless_percent' => 'float',
|
||||||
'reminder_send_time' => 'int',
|
'late_fee_endless_amount' => 'float',
|
||||||
'email_sending_method' => 'string',
|
'auto_email_invoice' => 'bool',
|
||||||
'gmail_sending_user_id' => 'string',
|
'reminder_send_time' => 'int',
|
||||||
'counter_number_applied' => 'string',
|
'email_sending_method' => 'string',
|
||||||
'quote_number_applied' => 'string',
|
'gmail_sending_user_id' => 'string',
|
||||||
'email_subject_custom1' => 'string',
|
'counter_number_applied' => 'string',
|
||||||
'email_subject_custom2' => 'string',
|
'quote_number_applied' => 'string',
|
||||||
'email_subject_custom3' => 'string',
|
'email_subject_custom1' => 'string',
|
||||||
'email_template_custom1' => 'string',
|
'email_subject_custom2' => 'string',
|
||||||
'email_template_custom2' => 'string',
|
'email_subject_custom3' => 'string',
|
||||||
'email_template_custom3' => 'string',
|
'email_template_custom1' => 'string',
|
||||||
'enable_reminder1' => 'bool',
|
'email_template_custom2' => 'string',
|
||||||
'enable_reminder2' => 'bool',
|
'email_template_custom3' => 'string',
|
||||||
'enable_reminder3' => 'bool',
|
'enable_reminder1' => 'bool',
|
||||||
'num_days_reminder1' => 'int',
|
'enable_reminder2' => 'bool',
|
||||||
'num_days_reminder2' => 'int',
|
'enable_reminder3' => 'bool',
|
||||||
'num_days_reminder3' => 'int',
|
'num_days_reminder1' => 'int',
|
||||||
'schedule_reminder1' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
|
'num_days_reminder2' => 'int',
|
||||||
'schedule_reminder2' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
|
'num_days_reminder3' => 'int',
|
||||||
'schedule_reminder3' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
|
'schedule_reminder1' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
|
||||||
'late_fee_amount1' => 'float',
|
'schedule_reminder2' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
|
||||||
'late_fee_amount2' => 'float',
|
'schedule_reminder3' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
|
||||||
'late_fee_amount3' => 'float',
|
'late_fee_amount1' => 'float',
|
||||||
'late_fee_percent1' => 'float',
|
'late_fee_amount2' => 'float',
|
||||||
'late_fee_percent2' => 'float',
|
'late_fee_amount3' => 'float',
|
||||||
'late_fee_percent3' => 'float',
|
'late_fee_percent1' => 'float',
|
||||||
'endless_reminder_frequency_id' => 'integer',
|
'late_fee_percent2' => 'float',
|
||||||
|
'late_fee_percent3' => 'float',
|
||||||
|
'endless_reminder_frequency_id' => 'integer',
|
||||||
'client_online_payment_notification' => 'bool',
|
'client_online_payment_notification' => 'bool',
|
||||||
'client_manual_payment_notification' => 'bool',
|
'client_manual_payment_notification' => 'bool',
|
||||||
'document_email_attachment' => 'bool',
|
'document_email_attachment' => 'bool',
|
||||||
'enable_client_portal_password' => 'bool',
|
'enable_client_portal_password' => 'bool',
|
||||||
'enable_email_markup' => 'bool',
|
'enable_email_markup' => 'bool',
|
||||||
'enable_client_portal_dashboard' => 'bool',
|
'enable_client_portal_dashboard' => 'bool',
|
||||||
'enable_client_portal' => 'bool',
|
'enable_client_portal' => 'bool',
|
||||||
'email_template_statement' => 'string',
|
'email_template_statement' => 'string',
|
||||||
'email_subject_statement' => 'string',
|
'email_subject_statement' => 'string',
|
||||||
'signature_on_pdf' => 'bool',
|
'signature_on_pdf' => 'bool',
|
||||||
'quote_footer' => 'string',
|
'quote_footer' => 'string',
|
||||||
'page_size' => 'string',
|
'page_size' => 'string',
|
||||||
'page_layout' => 'string',
|
'page_layout' => 'string',
|
||||||
'font_size' => 'int',
|
'font_size' => 'int',
|
||||||
'primary_font' => 'string',
|
'primary_font' => 'string',
|
||||||
'secondary_font' => 'string',
|
'secondary_font' => 'string',
|
||||||
'hide_paid_to_date' => 'bool',
|
'hide_paid_to_date' => 'bool',
|
||||||
'embed_documents' => 'bool',
|
'embed_documents' => 'bool',
|
||||||
'all_pages_header' => 'bool',
|
'all_pages_header' => 'bool',
|
||||||
'all_pages_footer' => 'bool',
|
'all_pages_footer' => 'bool',
|
||||||
'project_number_pattern' => 'string',
|
'project_number_pattern' => 'string',
|
||||||
'project_number_counter' => 'int',
|
'project_number_counter' => 'int',
|
||||||
'task_number_pattern' => 'string',
|
'task_number_pattern' => 'string',
|
||||||
'task_number_counter' => 'int',
|
'task_number_counter' => 'int',
|
||||||
'expense_number_pattern' => 'string',
|
'expense_number_pattern' => 'string',
|
||||||
'expense_number_counter' => 'int',
|
'expense_number_counter' => 'int',
|
||||||
'recurring_expense_number_pattern' => 'string',
|
'recurring_expense_number_pattern' => 'string',
|
||||||
'recurring_expense_number_counter' => 'int',
|
'recurring_expense_number_counter' => 'int',
|
||||||
'recurring_quote_number_pattern' => 'string',
|
'recurring_quote_number_pattern' => 'string',
|
||||||
'recurring_quote_number_counter' => 'int',
|
'recurring_quote_number_counter' => 'int',
|
||||||
'vendor_number_pattern' => 'string',
|
'vendor_number_pattern' => 'string',
|
||||||
'vendor_number_counter' => 'int',
|
'vendor_number_counter' => 'int',
|
||||||
'ticket_number_pattern' => 'string',
|
'ticket_number_pattern' => 'string',
|
||||||
'ticket_number_counter' => 'int',
|
'ticket_number_counter' => 'int',
|
||||||
'payment_number_pattern' => 'string',
|
'payment_number_pattern' => 'string',
|
||||||
'payment_number_counter' => 'int',
|
'payment_number_counter' => 'int',
|
||||||
'reply_to_email' => 'string',
|
'reply_to_email' => 'string',
|
||||||
'bcc_email' => 'string',
|
'bcc_email' => 'string',
|
||||||
'pdf_email_attachment' => 'bool',
|
'pdf_email_attachment' => 'bool',
|
||||||
'ubl_email_attachment' => 'bool',
|
'ubl_email_attachment' => 'bool',
|
||||||
'email_style' => 'string',
|
'email_style' => 'string',
|
||||||
'email_style_custom' => 'string',
|
'email_style_custom' => 'string',
|
||||||
'company_gateway_ids' => 'string',
|
'company_gateway_ids' => 'string',
|
||||||
'address1' => 'string',
|
'address1' => 'string',
|
||||||
'address2' => 'string',
|
'address2' => 'string',
|
||||||
'city' => 'string',
|
'city' => 'string',
|
||||||
'company_logo' => 'string',
|
'company_logo' => 'string',
|
||||||
'country_id' => 'string',
|
'country_id' => 'string',
|
||||||
'client_number_pattern' => 'string',
|
'client_number_pattern' => 'string',
|
||||||
'client_number_counter' => 'integer',
|
'client_number_counter' => 'integer',
|
||||||
'credit_number_pattern' => 'string',
|
'credit_number_pattern' => 'string',
|
||||||
'credit_number_counter' => 'integer',
|
'credit_number_counter' => 'integer',
|
||||||
'currency_id' => 'string',
|
'currency_id' => 'string',
|
||||||
'custom_value1' => 'string',
|
'custom_value1' => 'string',
|
||||||
'custom_value2' => 'string',
|
'custom_value2' => 'string',
|
||||||
'custom_value3' => 'string',
|
'custom_value3' => 'string',
|
||||||
'custom_value4' => 'string',
|
'custom_value4' => 'string',
|
||||||
'custom_message_dashboard' => 'string',
|
'custom_message_dashboard' => 'string',
|
||||||
'custom_message_unpaid_invoice' => 'string',
|
'custom_message_unpaid_invoice' => 'string',
|
||||||
'custom_message_paid_invoice' => 'string',
|
'custom_message_paid_invoice' => 'string',
|
||||||
'custom_message_unapproved_quote' => 'string',
|
'custom_message_unapproved_quote' => 'string',
|
||||||
'default_task_rate' => 'float',
|
'default_task_rate' => 'float',
|
||||||
'email_signature' => 'string',
|
'email_signature' => 'string',
|
||||||
'email_subject_invoice' => 'string',
|
'email_subject_invoice' => 'string',
|
||||||
'email_subject_quote' => 'string',
|
'email_subject_quote' => 'string',
|
||||||
'email_subject_credit' => 'string',
|
'email_subject_credit' => 'string',
|
||||||
'email_subject_payment' => 'string',
|
'email_subject_payment' => 'string',
|
||||||
'email_subject_payment_partial' => 'string',
|
'email_subject_payment_partial' => 'string',
|
||||||
'email_template_invoice' => 'string',
|
'email_template_invoice' => 'string',
|
||||||
'email_template_quote' => 'string',
|
'email_template_quote' => 'string',
|
||||||
'email_template_credit' => 'string',
|
'email_template_credit' => 'string',
|
||||||
'email_template_payment' => 'string',
|
'email_template_payment' => 'string',
|
||||||
'email_template_payment_partial' => 'string',
|
'email_template_payment_partial' => 'string',
|
||||||
'email_subject_reminder1' => 'string',
|
'email_subject_reminder1' => 'string',
|
||||||
'email_subject_reminder2' => 'string',
|
'email_subject_reminder2' => 'string',
|
||||||
'email_subject_reminder3' => 'string',
|
'email_subject_reminder3' => 'string',
|
||||||
'email_subject_reminder_endless' => 'string',
|
'email_subject_reminder_endless' => 'string',
|
||||||
'email_template_reminder1' => 'string',
|
'email_template_reminder1' => 'string',
|
||||||
'email_template_reminder2' => 'string',
|
'email_template_reminder2' => 'string',
|
||||||
'email_template_reminder3' => 'string',
|
'email_template_reminder3' => 'string',
|
||||||
'email_template_reminder_endless' => 'string',
|
'email_template_reminder_endless' => 'string',
|
||||||
'inclusive_taxes' => 'bool',
|
'inclusive_taxes' => 'bool',
|
||||||
'invoice_number_pattern' => 'string',
|
'invoice_number_pattern' => 'string',
|
||||||
'invoice_number_counter' => 'integer',
|
'invoice_number_counter' => 'integer',
|
||||||
'invoice_design_id' => 'string',
|
'invoice_design_id' => 'string',
|
||||||
// 'invoice_fields' => 'string',
|
// 'invoice_fields' => 'string',
|
||||||
'invoice_taxes' => 'int',
|
'invoice_taxes' => 'int',
|
||||||
//'enabled_item_tax_rates' => 'int',
|
//'enabled_item_tax_rates' => 'int',
|
||||||
'invoice_footer' => 'string',
|
'invoice_footer' => 'string',
|
||||||
'invoice_labels' => 'string',
|
'invoice_labels' => 'string',
|
||||||
'invoice_terms' => 'string',
|
'invoice_terms' => 'string',
|
||||||
'credit_footer' => 'string',
|
'credit_footer' => 'string',
|
||||||
'credit_terms' => 'string',
|
'credit_terms' => 'string',
|
||||||
'name' => 'string',
|
'name' => 'string',
|
||||||
'payment_terms' => 'string',
|
'payment_terms' => 'string',
|
||||||
'payment_type_id' => 'string',
|
'payment_type_id' => 'string',
|
||||||
'phone' => 'string',
|
'phone' => 'string',
|
||||||
'postal_code' => 'string',
|
'postal_code' => 'string',
|
||||||
'quote_design_id' => 'string',
|
'quote_design_id' => 'string',
|
||||||
'credit_design_id' => 'string',
|
'credit_design_id' => 'string',
|
||||||
'quote_number_pattern' => 'string',
|
'quote_number_pattern' => 'string',
|
||||||
'quote_number_counter' => 'integer',
|
'quote_number_counter' => 'integer',
|
||||||
'quote_terms' => 'string',
|
'quote_terms' => 'string',
|
||||||
'recurring_number_prefix' => 'string',
|
'recurring_number_prefix' => 'string',
|
||||||
'reset_counter_frequency_id' => 'integer',
|
'reset_counter_frequency_id' => 'integer',
|
||||||
'reset_counter_date' => 'string',
|
'reset_counter_date' => 'string',
|
||||||
'require_invoice_signature' => 'bool',
|
'require_invoice_signature' => 'bool',
|
||||||
'require_quote_signature' => 'bool',
|
'require_quote_signature' => 'bool',
|
||||||
'state' => 'string',
|
'state' => 'string',
|
||||||
'email' => 'string',
|
'email' => 'string',
|
||||||
'vat_number' => 'string',
|
'vat_number' => 'string',
|
||||||
'id_number' => 'string',
|
'id_number' => 'string',
|
||||||
'tax_name1' => 'string',
|
'tax_name1' => 'string',
|
||||||
'tax_name2' => 'string',
|
'tax_name2' => 'string',
|
||||||
'tax_name3' => 'string',
|
'tax_name3' => 'string',
|
||||||
'tax_rate1' => 'float',
|
'tax_rate1' => 'float',
|
||||||
'tax_rate2' => 'float',
|
'tax_rate2' => 'float',
|
||||||
'tax_rate3' => 'float',
|
'tax_rate3' => 'float',
|
||||||
'show_accept_quote_terms' => 'bool',
|
'show_accept_quote_terms' => 'bool',
|
||||||
'show_accept_invoice_terms' => 'bool',
|
'show_accept_invoice_terms' => 'bool',
|
||||||
'timezone_id' => 'string',
|
'timezone_id' => 'string',
|
||||||
'valid_until' => 'string',
|
'valid_until' => 'string',
|
||||||
'date_format_id' => 'string',
|
'date_format_id' => 'string',
|
||||||
'military_time' => 'bool',
|
'military_time' => 'bool',
|
||||||
'language_id' => 'string',
|
'language_id' => 'string',
|
||||||
'show_currency_code' => 'bool',
|
'show_currency_code' => 'bool',
|
||||||
'send_reminders' => 'bool',
|
'send_reminders' => 'bool',
|
||||||
'enable_client_portal_tasks' => 'bool',
|
'enable_client_portal_tasks' => 'bool',
|
||||||
'auto_archive_invoice' => 'bool',
|
'auto_archive_invoice' => 'bool',
|
||||||
'auto_archive_quote' => 'bool',
|
'auto_archive_quote' => 'bool',
|
||||||
'auto_convert_quote' => 'bool',
|
'auto_convert_quote' => 'bool',
|
||||||
'shared_invoice_quote_counter' => 'bool',
|
'shared_invoice_quote_counter' => 'bool',
|
||||||
'counter_padding' => 'integer',
|
'counter_padding' => 'integer',
|
||||||
//'design' => 'string',
|
//'design' => 'string',
|
||||||
'website' => 'string',
|
'website' => 'string',
|
||||||
'pdf_variables' => 'object',
|
'pdf_variables' => 'object',
|
||||||
'portal_custom_head' => 'string',
|
'portal_custom_head' => 'string',
|
||||||
'portal_custom_css' => 'string',
|
'portal_custom_css' => 'string',
|
||||||
'portal_custom_footer' => 'string',
|
'portal_custom_footer' => 'string',
|
||||||
'portal_custom_js' => 'string',
|
'portal_custom_js' => 'string',
|
||||||
'client_portal_enable_uploads' => 'bool',
|
'client_portal_enable_uploads' => 'bool',
|
||||||
'purchase_order_number_counter' => 'integer',
|
'purchase_order_number_counter' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $free_plan_casts = [
|
public static $free_plan_casts = [
|
||||||
'currency_id' => 'string',
|
'currency_id' => 'string',
|
||||||
'company_gateway_ids' => 'string',
|
'company_gateway_ids' => 'string',
|
||||||
'address1' => 'string',
|
'address1' => 'string',
|
||||||
'address2' => 'string',
|
'address2' => 'string',
|
||||||
'city' => 'string',
|
'city' => 'string',
|
||||||
'company_logo' => 'string',
|
'company_logo' => 'string',
|
||||||
'country_id' => 'string',
|
'country_id' => 'string',
|
||||||
'custom_value1' => 'string',
|
'custom_value1' => 'string',
|
||||||
'custom_value2' => 'string',
|
'custom_value2' => 'string',
|
||||||
'custom_value3' => 'string',
|
'custom_value3' => 'string',
|
||||||
'custom_value4' => 'string',
|
'custom_value4' => 'string',
|
||||||
'inclusive_taxes' => 'bool',
|
'inclusive_taxes' => 'bool',
|
||||||
'name' => 'string',
|
'name' => 'string',
|
||||||
'payment_terms' => 'string',
|
'payment_terms' => 'string',
|
||||||
'payment_type_id' => 'string',
|
'payment_type_id' => 'string',
|
||||||
'phone' => 'string',
|
'phone' => 'string',
|
||||||
'postal_code' => 'string',
|
'postal_code' => 'string',
|
||||||
'state' => 'string',
|
'state' => 'string',
|
||||||
'email' => 'string',
|
'email' => 'string',
|
||||||
'vat_number' => 'string',
|
'vat_number' => 'string',
|
||||||
'id_number' => 'string',
|
'id_number' => 'string',
|
||||||
'tax_name1' => 'string',
|
'tax_name1' => 'string',
|
||||||
'tax_name2' => 'string',
|
'tax_name2' => 'string',
|
||||||
'tax_name3' => 'string',
|
'tax_name3' => 'string',
|
||||||
'tax_rate1' => 'float',
|
'tax_rate1' => 'float',
|
||||||
'tax_rate2' => 'float',
|
'tax_rate2' => 'float',
|
||||||
'tax_rate3' => 'float',
|
'tax_rate3' => 'float',
|
||||||
'timezone_id' => 'string',
|
'timezone_id' => 'string',
|
||||||
'date_format_id' => 'string',
|
'date_format_id' => 'string',
|
||||||
'military_time' => 'bool',
|
'military_time' => 'bool',
|
||||||
'language_id' => 'string',
|
'language_id' => 'string',
|
||||||
'show_currency_code' => 'bool',
|
'show_currency_code' => 'bool',
|
||||||
'website' => 'string',
|
'website' => 'string',
|
||||||
'default_task_rate' => 'float',
|
'default_task_rate' => 'float',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -840,9 +849,9 @@ class CompanySettings extends BaseSettings
|
|||||||
public static function setProperties($settings): stdClass
|
public static function setProperties($settings): stdClass
|
||||||
{
|
{
|
||||||
$company_settings = (object) get_class_vars(self::class);
|
$company_settings = (object) get_class_vars(self::class);
|
||||||
|
|
||||||
foreach ($company_settings as $key => $value) {
|
foreach ($company_settings as $key => $value) {
|
||||||
if (! property_exists($settings, $key)) {
|
if (!property_exists($settings, $key)) {
|
||||||
$settings->{$key} = self::castAttribute($key, $company_settings->{$key});
|
$settings->{$key} = self::castAttribute($key, $company_settings->{$key});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -855,7 +864,7 @@ class CompanySettings extends BaseSettings
|
|||||||
*
|
*
|
||||||
* @return stdClass
|
* @return stdClass
|
||||||
*/
|
*/
|
||||||
public static function notificationDefaults() :stdClass
|
public static function notificationDefaults(): stdClass
|
||||||
{
|
{
|
||||||
$notification = new stdClass;
|
$notification = new stdClass;
|
||||||
$notification->email = [];
|
$notification->email = [];
|
||||||
@ -871,7 +880,7 @@ class CompanySettings extends BaseSettings
|
|||||||
*
|
*
|
||||||
* @return stdClass
|
* @return stdClass
|
||||||
*/
|
*/
|
||||||
public static function notificationAdminDefaults() :stdClass
|
public static function notificationAdminDefaults(): stdClass
|
||||||
{
|
{
|
||||||
$notification = new stdClass;
|
$notification = new stdClass;
|
||||||
$notification->email = [];
|
$notification->email = [];
|
||||||
@ -888,7 +897,7 @@ class CompanySettings extends BaseSettings
|
|||||||
*
|
*
|
||||||
* @return stdClass The stdClass of PDF variables
|
* @return stdClass The stdClass of PDF variables
|
||||||
*/
|
*/
|
||||||
public static function getEntityVariableDefaults() :stdClass
|
public static function getEntityVariableDefaults(): stdClass
|
||||||
{
|
{
|
||||||
$variables = [
|
$variables = [
|
||||||
'client_details' => [
|
'client_details' => [
|
||||||
@ -975,7 +984,7 @@ class CompanySettings extends BaseSettings
|
|||||||
'$product.tax',
|
'$product.tax',
|
||||||
'$product.line_total',
|
'$product.line_total',
|
||||||
],
|
],
|
||||||
'task_columns' =>[
|
'task_columns' => [
|
||||||
'$task.service',
|
'$task.service',
|
||||||
'$task.description',
|
'$task.description',
|
||||||
'$task.rate',
|
'$task.rate',
|
||||||
|
@ -213,7 +213,7 @@ class SettingsData
|
|||||||
|
|
||||||
public bool $show_accept_quote_terms = false; //@TODO ben to confirm
|
public bool $show_accept_quote_terms = false; //@TODO ben to confirm
|
||||||
|
|
||||||
public string $email_sending_method = 'default'; // enum 'default', 'gmail', 'office365', 'client_postmark', 'client_mailgun' //@implemented
|
public string $email_sending_method = 'default'; // enum 'default', 'gmail', 'office365', 'client_postmark', 'client_mailgun' , 'brevo_mailgun' //@implemented
|
||||||
|
|
||||||
public string $gmail_sending_user_id = '0'; //@implemented
|
public string $gmail_sending_user_id = '0'; //@implemented
|
||||||
|
|
||||||
@ -433,6 +433,12 @@ class SettingsData
|
|||||||
|
|
||||||
public string $mailgun_endpoint = 'api.mailgun.net'; // api.eu.mailgun.net
|
public string $mailgun_endpoint = 'api.mailgun.net'; // api.eu.mailgun.net
|
||||||
|
|
||||||
|
public string $brevo_secret = '';
|
||||||
|
|
||||||
|
public string $brevo_domain = '';
|
||||||
|
|
||||||
|
public string $brevo_endpoint = 'api.mailgun.net'; // api.eu.mailgun.net
|
||||||
|
|
||||||
public bool $auto_bill_standard_invoices = false;
|
public bool $auto_bill_standard_invoices = false;
|
||||||
|
|
||||||
public string $email_alignment = 'center'; // center, left, right
|
public string $email_alignment = 'center'; // center, left, right
|
||||||
@ -464,13 +470,13 @@ class SettingsData
|
|||||||
public bool $enable_e_invoice = false;
|
public bool $enable_e_invoice = false;
|
||||||
|
|
||||||
public string $classification = '';
|
public string $classification = '';
|
||||||
|
|
||||||
private mixed $object;
|
private mixed $object;
|
||||||
|
|
||||||
public function cast(mixed $object)
|
public function cast(mixed $object)
|
||||||
{
|
{
|
||||||
if(is_array($object)) {
|
if (is_array($object)) {
|
||||||
$object = (object)$object;
|
$object = (object) $object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_object($object)) {
|
if (is_object($object)) {
|
||||||
@ -478,9 +484,9 @@ class SettingsData
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
settype($object->{$key}, gettype($this->{$key}));
|
settype($object->{$key}, gettype($this->{$key}));
|
||||||
} catch(\Exception | \Error | \Throwable $e) {
|
} catch (\Exception | \Error | \Throwable $e) {
|
||||||
|
|
||||||
if(property_exists($this, $key)) {
|
if (property_exists($this, $key)) {
|
||||||
$object->{$key} = $this->{$key};
|
$object->{$key} = $this->{$key};
|
||||||
} else {
|
} else {
|
||||||
unset($object->{$key});
|
unset($object->{$key});
|
||||||
@ -506,11 +512,11 @@ class SettingsData
|
|||||||
|
|
||||||
public function toObject(): object
|
public function toObject(): object
|
||||||
{
|
{
|
||||||
return (object)$this->object;
|
return (object) $this->object;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
return (array)$this->object;
|
return (array) $this->object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,9 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
protected $client_mailgun_domain = false;
|
protected $client_mailgun_domain = false;
|
||||||
|
|
||||||
|
protected $client_brevo_secret = false;
|
||||||
|
|
||||||
|
protected $client_brevo_domain = false;
|
||||||
|
|
||||||
public function __construct(NinjaMailerObject $nmo, bool $override = false)
|
public function __construct(NinjaMailerObject $nmo, bool $override = false)
|
||||||
{
|
{
|
||||||
@ -112,16 +115,16 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
/* If we have an invitation present, we pass the invitation key into the email headers*/
|
/* If we have an invitation present, we pass the invitation key into the email headers*/
|
||||||
if ($this->nmo->invitation) {
|
if ($this->nmo->invitation) {
|
||||||
$this->nmo
|
$this->nmo
|
||||||
->mailable
|
->mailable
|
||||||
->withSymfonyMessage(function ($message) {
|
->withSymfonyMessage(function ($message) {
|
||||||
$message->getHeaders()->addTextHeader('x-invitation', $this->nmo->invitation->key);
|
$message->getHeaders()->addTextHeader('x-invitation', $this->nmo->invitation->key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//send email
|
//send email
|
||||||
try {
|
try {
|
||||||
nlog("Trying to send to {$this->nmo->to_user->email} ". now()->toDateTimeString());
|
nlog("Trying to send to {$this->nmo->to_user->email} " . now()->toDateTimeString());
|
||||||
nlog("Using mailer => ". $this->mailer);
|
nlog("Using mailer => " . $this->mailer);
|
||||||
|
|
||||||
$mailer = Mail::mailer($this->mailer);
|
$mailer = Mail::mailer($this->mailer);
|
||||||
|
|
||||||
@ -133,23 +136,27 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->nmo->settings->mailgun_endpoint);
|
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->nmo->settings->mailgun_endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->client_brevo_secret) {
|
||||||
|
$mailer->brevo_config($this->client_brevo_secret, $this->client_brevo_domain, $this->nmo->settings->brevo_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
$mailer
|
$mailer
|
||||||
->to($this->nmo->to_user->email)
|
->to($this->nmo->to_user->email)
|
||||||
->send($this->nmo->mailable);
|
->send($this->nmo->mailable);
|
||||||
|
|
||||||
/* Count the amount of emails sent across all the users accounts */
|
/* Count the amount of emails sent across all the users accounts */
|
||||||
Cache::increment("email_quota".$this->company->account->key);
|
Cache::increment("email_quota" . $this->company->account->key);
|
||||||
|
|
||||||
LightLogs::create(new EmailSuccess($this->nmo->company->company_key, $this->nmo->mailable->subject))
|
LightLogs::create(new EmailSuccess($this->nmo->company->company_key, $this->nmo->mailable->subject))
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
} catch(\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
|
} catch (\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
|
||||||
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
$this->logMailError($e->getMessage(), $this->company->clients()->first());
|
$this->logMailError($e->getMessage(), $this->company->clients()->first());
|
||||||
return;
|
return;
|
||||||
} catch(\Symfony\Component\Mime\Exception\LogicException $e) {
|
} catch (\Symfony\Component\Mime\Exception\LogicException $e) {
|
||||||
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
@ -200,11 +207,11 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
app('sentry')->captureException($e);
|
app('sentry')->captureException($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Releasing immediately does not add in the backoff */
|
/* Releasing immediately does not add in the backoff */
|
||||||
sleep(rand(0, 3));
|
sleep(rand(0, 3));
|
||||||
|
|
||||||
$this->release($this->backoff()[$this->attempts()-1]);
|
$this->release($this->backoff()[$this->attempts() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->nmo = null;
|
$this->nmo = null;
|
||||||
@ -272,6 +279,10 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$this->mailer = 'mailgun';
|
$this->mailer = 'mailgun';
|
||||||
$this->setMailgunMailer();
|
$this->setMailgunMailer();
|
||||||
return $this;
|
return $this;
|
||||||
|
case 'client_brevo':
|
||||||
|
$this->mailer = 'brevo';
|
||||||
|
$this->setBrevoMailer();
|
||||||
|
return $this;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -303,8 +314,8 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
if (env($this->company->id . '_MAIL_FROM_ADDRESS')) {
|
if (env($this->company->id . '_MAIL_FROM_ADDRESS')) {
|
||||||
$this->nmo
|
$this->nmo
|
||||||
->mailable
|
->mailable
|
||||||
->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME')));
|
->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,6 +333,10 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
$this->client_mailgun_domain = false;
|
$this->client_mailgun_domain = false;
|
||||||
|
|
||||||
|
$this->client_brevo_secret = false;
|
||||||
|
|
||||||
|
$this->client_brevo_domain = false;
|
||||||
|
|
||||||
//always dump the drivers to prevent reuse
|
//always dump the drivers to prevent reuse
|
||||||
app('mail.manager')->forgetMailers();
|
app('mail.manager')->forgetMailers();
|
||||||
}
|
}
|
||||||
@ -381,8 +396,32 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$sending_user = (isset($this->nmo->settings->email_from_name) && strlen($this->nmo->settings->email_from_name) > 2) ? $this->nmo->settings->email_from_name : $user->name();
|
$sending_user = (isset($this->nmo->settings->email_from_name) && strlen($this->nmo->settings->email_from_name) > 2) ? $this->nmo->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
$this->nmo
|
$this->nmo
|
||||||
->mailable
|
->mailable
|
||||||
->from($sending_email, $sending_user);
|
->from($sending_email, $sending_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Brevo using client supplied secret
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
|
private function setBrevoMailer()
|
||||||
|
{
|
||||||
|
if (strlen($this->nmo->settings->brevo_secret) > 2 && strlen($this->nmo->settings->brevo_domain) > 2) {
|
||||||
|
$this->client_brevo_secret = $this->nmo->settings->brevo_secret;
|
||||||
|
$this->client_brevo_domain = $this->nmo->settings->brevo_domain;
|
||||||
|
} else {
|
||||||
|
$this->nmo->settings->email_sending_method = 'default';
|
||||||
|
return $this->setMailDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
|
$sending_email = (isset($this->nmo->settings->custom_sending_email) && stripos($this->nmo->settings->custom_sending_email, "@")) ? $this->nmo->settings->custom_sending_email : $user->email;
|
||||||
|
$sending_user = (isset($this->nmo->settings->email_from_name) && strlen($this->nmo->settings->email_from_name) > 2) ? $this->nmo->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
|
$this->nmo
|
||||||
|
->mailable
|
||||||
|
->from($sending_email, $sending_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -404,8 +443,8 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$sending_user = (isset($this->nmo->settings->email_from_name) && strlen($this->nmo->settings->email_from_name) > 2) ? $this->nmo->settings->email_from_name : $user->name();
|
$sending_user = (isset($this->nmo->settings->email_from_name) && strlen($this->nmo->settings->email_from_name) > 2) ? $this->nmo->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
$this->nmo
|
$this->nmo
|
||||||
->mailable
|
->mailable
|
||||||
->from($sending_email, $sending_user);
|
->from($sending_email, $sending_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -417,7 +456,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$user = $this->resolveSendingUser();
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
$token = $this->refreshOfficeToken($user);
|
$token = $this->refreshOfficeToken($user);
|
||||||
@ -431,11 +470,11 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->nmo
|
$this->nmo
|
||||||
->mailable
|
->mailable
|
||||||
->from($user->email, $user->name())
|
->from($user->email, $user->name())
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
->withSymfonyMessage(function ($message) use ($token) {
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -459,7 +498,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
||||||
} catch(\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
||||||
$this->nmo->settings->email_sending_method = 'default';
|
$this->nmo->settings->email_sending_method = 'default';
|
||||||
return $this->setMailDriver();
|
return $this->setMailDriver();
|
||||||
@ -479,7 +518,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
* Now that our token is refreshed and valid we can boot the
|
* Now that our token is refreshed and valid we can boot the
|
||||||
* mail driver at runtime and also set the token which will persist
|
* mail driver at runtime and also set the token which will persist
|
||||||
* just for this request.
|
* just for this request.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$token = $user->oauth_user_token->access_token;
|
$token = $user->oauth_user_token->access_token;
|
||||||
|
|
||||||
@ -490,11 +529,11 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->nmo
|
$this->nmo
|
||||||
->mailable
|
->mailable
|
||||||
->from($user->email, $user->name())
|
->from($user->email, $user->name())
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
->withSymfonyMessage(function ($message) use ($token) {
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -507,7 +546,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
private function preFlightChecksFail(): bool
|
private function preFlightChecksFail(): bool
|
||||||
{
|
{
|
||||||
/* Always send regardless */
|
/* Always send regardless */
|
||||||
if($this->override) {
|
if ($this->override) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +566,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* GMail users are uncapped */
|
/* GMail users are uncapped */
|
||||||
if (Ninja::isHosted() && (in_array($this->nmo->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun']))) {
|
if (Ninja::isHosted() && (in_array($this->nmo->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun', 'client_brevo']))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +584,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
if (!str_contains($this->nmo->to_user->email, "@")) {
|
if (!str_contains($this->nmo->to_user->email, "@")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* On the hosted platform if the user has not verified their account we fail here - but still check what they are trying to send! */
|
/* On the hosted platform if the user has not verified their account we fail here - but still check what they are trying to send! */
|
||||||
if (Ninja::isHosted() && $this->company->account && !$this->company->account->account_sms_verified) {
|
if (Ninja::isHosted() && $this->company->account && !$this->company->account->account_sms_verified) {
|
||||||
if (class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class)) {
|
if (class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class)) {
|
||||||
@ -570,7 +609,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
* @param \App\Models\User | \App\Models\Client | null $recipient_object
|
* @param \App\Models\User | \App\Models\Client | null $recipient_object
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function logMailError($errors, $recipient_object) :void
|
private function logMailError($errors, $recipient_object): void
|
||||||
{
|
{
|
||||||
(new SystemLogger(
|
(new SystemLogger(
|
||||||
$errors,
|
$errors,
|
||||||
@ -586,7 +625,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$job_failure->string_metric6 = substr($errors, 0, 150);
|
$job_failure->string_metric6 = substr($errors, 0, 150);
|
||||||
|
|
||||||
LightLogs::create($job_failure)
|
LightLogs::create($job_failure)
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
$job_failure = null;
|
$job_failure = null;
|
||||||
}
|
}
|
||||||
@ -611,14 +650,14 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
$token = json_decode($guzzle->post($url, [
|
$token = json_decode($guzzle->post($url, [
|
||||||
'form_params' => [
|
'form_params' => [
|
||||||
'client_id' => config('ninja.o365.client_id') ,
|
'client_id' => config('ninja.o365.client_id'),
|
||||||
'client_secret' => config('ninja.o365.client_secret') ,
|
'client_secret' => config('ninja.o365.client_secret'),
|
||||||
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
||||||
'grant_type' => 'refresh_token',
|
'grant_type' => 'refresh_token',
|
||||||
'refresh_token' => $user->oauth_user_refresh_token
|
'refresh_token' => $user->oauth_user_refresh_token
|
||||||
],
|
],
|
||||||
])->getBody()->getContents());
|
])->getBody()->getContents());
|
||||||
|
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
||||||
$user->oauth_user_token = $token->access_token;
|
$user->oauth_user_token = $token->access_token;
|
||||||
|
@ -54,7 +54,7 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
/* Defines the name used in polymorphic tables */
|
/* Defines the name used in polymorphic tables */
|
||||||
Relation::morphMap([
|
Relation::morphMap([
|
||||||
'invoices' => Invoice::class,
|
'invoices' => Invoice::class,
|
||||||
'proposals' => Proposal::class,
|
'proposals' => Proposal::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -96,11 +96,10 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
'transport' => 'postmark',
|
'transport' => 'postmark',
|
||||||
'token' => $postmark_key
|
'token' => $postmark_key
|
||||||
]));
|
]));
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Mailer::macro('mailgun_config', function (string $secret, string $domain, string $endpoint = 'api.mailgun.net') {
|
Mailer::macro('mailgun_config', function (string $secret, string $domain, string $endpoint = 'api.mailgun.net') {
|
||||||
// @phpstan-ignore /** @phpstan-ignore-next-line **/
|
// @phpstan-ignore /** @phpstan-ignore-next-line **/
|
||||||
Mailer::setSymfonyTransport(app('mail.manager')->createSymfonyTransport([
|
Mailer::setSymfonyTransport(app('mail.manager')->createSymfonyTransport([
|
||||||
@ -110,7 +109,20 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
'endpoint' => $endpoint,
|
'endpoint' => $endpoint,
|
||||||
'scheme' => config('services.mailgun.scheme'),
|
'scheme' => config('services.mailgun.scheme'),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
});
|
||||||
|
|
||||||
|
Mailer::macro('brevo_config', function (string $secret, string $domain, string $endpoint = 'api.mailgun.net') {
|
||||||
|
// @phpstan-ignore /** @phpstan-ignore-next-line **/
|
||||||
|
Mailer::setSymfonyTransport(app('mail.manager')->createSymfonyTransport([
|
||||||
|
'transport' => 'brevo',
|
||||||
|
'secret' => $secret,
|
||||||
|
'domain' => $domain,
|
||||||
|
'endpoint' => $endpoint,
|
||||||
|
'scheme' => config('services.brevo.scheme'),
|
||||||
|
]));
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -55,6 +55,12 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
protected ?string $client_mailgun_endpoint = null;
|
protected ?string $client_mailgun_endpoint = null;
|
||||||
|
|
||||||
|
protected ?string $client_brevo_secret = null;
|
||||||
|
|
||||||
|
protected ?string $client_brevo_domain = null;
|
||||||
|
|
||||||
|
protected ?string $client_brevo_endpoint = null;
|
||||||
|
|
||||||
private string $mailer = 'default';
|
private string $mailer = 'default';
|
||||||
|
|
||||||
public Mailable $mailable;
|
public Mailable $mailable;
|
||||||
@ -62,7 +68,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
public function __construct(public EmailObject $email_object, public Company $company)
|
public function __construct(public EmailObject $email_object, public Company $company)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The backoff time between retries.
|
* The backoff time between retries.
|
||||||
*
|
*
|
||||||
@ -78,7 +84,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->setOverride()
|
$this->setOverride()
|
||||||
->buildMailable();
|
->buildMailable();
|
||||||
|
|
||||||
if ($this->preFlightChecksFail()) {
|
if ($this->preFlightChecksFail()) {
|
||||||
return;
|
return;
|
||||||
@ -87,7 +93,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
$this->email();
|
$this->email();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the override flag
|
* Sets the override flag
|
||||||
*
|
*
|
||||||
@ -99,7 +105,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the mailable
|
* Populates the mailable
|
||||||
*
|
*
|
||||||
@ -108,10 +114,10 @@ class AdminEmail implements ShouldQueue
|
|||||||
public function buildMailable(): self
|
public function buildMailable(): self
|
||||||
{
|
{
|
||||||
$this->mailable = new AdminEmailMailable($this->email_object);
|
$this->mailable = new AdminEmailMailable($this->email_object);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to send the email
|
* Attempts to send the email
|
||||||
*
|
*
|
||||||
@ -133,24 +139,28 @@ class AdminEmail implements ShouldQueue
|
|||||||
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint);
|
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->client_brevo_secret) {
|
||||||
|
$mailer->brevo_config($this->client_brevo_secret, $this->client_brevo_domain, $this->client_brevo_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
/* Attempt the send! */
|
/* Attempt the send! */
|
||||||
try {
|
try {
|
||||||
nlog("Using mailer => ". $this->mailer. " ". now()->toDateTimeString());
|
nlog("Using mailer => " . $this->mailer . " " . now()->toDateTimeString());
|
||||||
|
|
||||||
$mailer->send($this->mailable);
|
$mailer->send($this->mailable);
|
||||||
|
|
||||||
Cache::increment("email_quota".$this->company->account->key);
|
Cache::increment("email_quota" . $this->company->account->key);
|
||||||
|
|
||||||
LightLogs::create(new EmailSuccess($this->company->company_key, $this->mailable->subject))
|
LightLogs::create(new EmailSuccess($this->company->company_key, $this->mailable->subject))
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
} catch(\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
|
} catch (\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
|
||||||
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
$this->logMailError($e->getMessage(), $this->company->clients()->first());
|
$this->logMailError($e->getMessage(), $this->company->clients()->first());
|
||||||
return;
|
return;
|
||||||
} catch(\Symfony\Component\Mime\Exception\LogicException $e) {
|
} catch (\Symfony\Component\Mime\Exception\LogicException $e) {
|
||||||
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
@ -178,12 +188,12 @@ class AdminEmail implements ShouldQueue
|
|||||||
if ($e instanceof ClientException) { //postmark specific failure
|
if ($e instanceof ClientException) { //postmark specific failure
|
||||||
$response = $e->getResponse();
|
$response = $e->getResponse();
|
||||||
$message_body = json_decode($response->getBody()->getContents());
|
$message_body = json_decode($response->getBody()->getContents());
|
||||||
|
|
||||||
if ($message_body && property_exists($message_body, 'Message')) {
|
if ($message_body && property_exists($message_body, 'Message')) {
|
||||||
$message = $message_body->Message;
|
$message = $message_body->Message;
|
||||||
nlog($message);
|
nlog($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
return;
|
return;
|
||||||
@ -202,7 +212,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
sleep(rand(0, 3));
|
sleep(rand(0, 3));
|
||||||
|
|
||||||
$this->release($this->backoff()[$this->attempts()-1]);
|
$this->release($this->backoff()[$this->attempts() - 1]);
|
||||||
|
|
||||||
$message = null;
|
$message = null;
|
||||||
}
|
}
|
||||||
@ -211,16 +221,16 @@ class AdminEmail implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On the hosted platform we scan all outbound email for
|
* On the hosted platform we scan all outbound email for
|
||||||
* spam. This sequence processes the filters we use on all
|
* spam. This sequence processes the filters we use on all
|
||||||
* emails.
|
* emails.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function preFlightChecksFail(): bool
|
public function preFlightChecksFail(): bool
|
||||||
{
|
{
|
||||||
/* Always send if disabled */
|
/* Always send if disabled */
|
||||||
if($this->override) {
|
if ($this->override) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +290,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hasInValidEmails
|
* hasInValidEmails
|
||||||
*
|
*
|
||||||
@ -333,6 +343,10 @@ class AdminEmail implements ShouldQueue
|
|||||||
$this->mailer = 'mailgun';
|
$this->mailer = 'mailgun';
|
||||||
$this->setMailgunMailer();
|
$this->setMailgunMailer();
|
||||||
return $this;
|
return $this;
|
||||||
|
case 'client_brevo':
|
||||||
|
$this->mailer = 'brevo';
|
||||||
|
$this->setBrevoMailer();
|
||||||
|
return $this;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$this->mailer = config('mail.default');
|
$this->mailer = config('mail.default');
|
||||||
@ -365,7 +379,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
if (env($this->company->id . '_MAIL_FROM_ADDRESS')) {
|
if (env($this->company->id . '_MAIL_FROM_ADDRESS')) {
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME')));
|
->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,6 +400,12 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
$this->client_mailgun_endpoint = null;
|
$this->client_mailgun_endpoint = null;
|
||||||
|
|
||||||
|
$this->client_brevo_secret = null;
|
||||||
|
|
||||||
|
$this->client_brevo_domain = null;
|
||||||
|
|
||||||
|
$this->client_brevo_endpoint = null;
|
||||||
|
|
||||||
//always dump the drivers to prevent reuse
|
//always dump the drivers to prevent reuse
|
||||||
app('mail.manager')->forgetMailers();
|
app('mail.manager')->forgetMailers();
|
||||||
}
|
}
|
||||||
@ -402,7 +422,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
/* Always ensure the user is set on the correct account */
|
/* Always ensure the user is set on the correct account */
|
||||||
if ($user->account_id != $this->company->account_id) {
|
if ($user->account_id != $this->company->account_id) {
|
||||||
$this->email_object->settings->email_sending_method = 'default';
|
$this->email_object->settings->email_sending_method = 'default';
|
||||||
|
|
||||||
return $this->setMailDriver();
|
return $this->setMailDriver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,7 +468,31 @@ class AdminEmail implements ShouldQueue
|
|||||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($sending_email, $sending_user);
|
->from($sending_email, $sending_user);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Configures Brevo using client supplied secret
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
|
private function setBrevoMailer()
|
||||||
|
{
|
||||||
|
if (strlen($this->email_object->settings->brevo_secret) > 2 && strlen($this->email_object->settings->brevo_domain) > 2) {
|
||||||
|
$this->client_brevo_secret = $this->email_object->settings->brevo_secret;
|
||||||
|
$this->client_brevo_domain = $this->email_object->settings->brevo_domain;
|
||||||
|
$this->client_brevo_endpoint = $this->email_object->settings->brevo_endpoint;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->email_object->settings->email_sending_method = 'default';
|
||||||
|
return $this->setMailDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
|
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||||
|
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
|
$this->mailable
|
||||||
|
->from($sending_email, $sending_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -468,9 +512,9 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($sending_email, $sending_user);
|
->from($sending_email, $sending_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -480,9 +524,9 @@ class AdminEmail implements ShouldQueue
|
|||||||
private function setOfficeMailer()
|
private function setOfficeMailer()
|
||||||
{
|
{
|
||||||
$user = $this->resolveSendingUser();
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
$token = $this->refreshOfficeToken($user);
|
$token = $this->refreshOfficeToken($user);
|
||||||
@ -496,10 +540,10 @@ class AdminEmail implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($user->email, $user->name())
|
->from($user->email, $user->name())
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
->withSymfonyMessage(function ($message) use ($token) {
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -511,7 +555,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
$user = $this->resolveSendingUser();
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
$google = (new Google())->init();
|
$google = (new Google())->init();
|
||||||
@ -523,7 +567,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
||||||
} catch(\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
||||||
$this->email_object->settings->email_sending_method = 'default';
|
$this->email_object->settings->email_sending_method = 'default';
|
||||||
return $this->setMailDriver();
|
return $this->setMailDriver();
|
||||||
@ -543,7 +587,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
* Now that our token is refreshed and valid we can boot the
|
* Now that our token is refreshed and valid we can boot the
|
||||||
* mail driver at runtime and also set the token which will persist
|
* mail driver at runtime and also set the token which will persist
|
||||||
* just for this request.
|
* just for this request.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$token = $user->oauth_user_token->access_token;
|
$token = $user->oauth_user_token->access_token;
|
||||||
|
|
||||||
@ -554,10 +598,10 @@ class AdminEmail implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($user->email, $user->name())
|
->from($user->email, $user->name())
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
->withSymfonyMessage(function ($message) use ($token) {
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -567,7 +611,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
* @param null | \App\Models\Client $recipient_object
|
* @param null | \App\Models\Client $recipient_object
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function logMailError($errors, $recipient_object) :void
|
private function logMailError($errors, $recipient_object): void
|
||||||
{
|
{
|
||||||
(new SystemLogger(
|
(new SystemLogger(
|
||||||
$errors,
|
$errors,
|
||||||
@ -583,7 +627,7 @@ class AdminEmail implements ShouldQueue
|
|||||||
$job_failure->string_metric6 = substr($errors, 0, 150);
|
$job_failure->string_metric6 = substr($errors, 0, 150);
|
||||||
|
|
||||||
LightLogs::create($job_failure)
|
LightLogs::create($job_failure)
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
$job_failure = null;
|
$job_failure = null;
|
||||||
}
|
}
|
||||||
@ -604,14 +648,14 @@ class AdminEmail implements ShouldQueue
|
|||||||
|
|
||||||
$token = json_decode($guzzle->post($url, [
|
$token = json_decode($guzzle->post($url, [
|
||||||
'form_params' => [
|
'form_params' => [
|
||||||
'client_id' => config('ninja.o365.client_id') ,
|
'client_id' => config('ninja.o365.client_id'),
|
||||||
'client_secret' => config('ninja.o365.client_secret') ,
|
'client_secret' => config('ninja.o365.client_secret'),
|
||||||
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
||||||
'grant_type' => 'refresh_token',
|
'grant_type' => 'refresh_token',
|
||||||
'refresh_token' => $user->oauth_user_refresh_token
|
'refresh_token' => $user->oauth_user_refresh_token
|
||||||
],
|
],
|
||||||
])->getBody()->getContents());
|
])->getBody()->getContents());
|
||||||
|
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
||||||
$user->oauth_user_token = $token->access_token;
|
$user->oauth_user_token = $token->access_token;
|
||||||
|
@ -60,6 +60,12 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
protected ?string $client_mailgun_endpoint = null;
|
protected ?string $client_mailgun_endpoint = null;
|
||||||
|
|
||||||
|
protected ?string $client_brevo_secret = null;
|
||||||
|
|
||||||
|
protected ?string $client_brevo_domain = null;
|
||||||
|
|
||||||
|
protected ?string $client_brevo_endpoint = null;
|
||||||
|
|
||||||
private string $mailer = 'default';
|
private string $mailer = 'default';
|
||||||
|
|
||||||
public Mailable $mailable;
|
public Mailable $mailable;
|
||||||
@ -67,7 +73,7 @@ class Email implements ShouldQueue
|
|||||||
public function __construct(public EmailObject $email_object, public Company $company)
|
public function __construct(public EmailObject $email_object, public Company $company)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The backoff time between retries.
|
* The backoff time between retries.
|
||||||
*
|
*
|
||||||
@ -83,9 +89,9 @@ class Email implements ShouldQueue
|
|||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->setOverride()
|
$this->setOverride()
|
||||||
->initModels()
|
->initModels()
|
||||||
->setDefaults()
|
->setDefaults()
|
||||||
->buildMailable();
|
->buildMailable();
|
||||||
|
|
||||||
/** Ensure quota's on hosted platform are respected. :) */
|
/** Ensure quota's on hosted platform are respected. :) */
|
||||||
$this->setMailDriver();
|
$this->setMailDriver();
|
||||||
@ -98,7 +104,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
$this->tearDown();
|
$this->tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the override flag
|
* Sets the override flag
|
||||||
*
|
*
|
||||||
@ -110,7 +116,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initilializes the models
|
* Initilializes the models
|
||||||
*
|
*
|
||||||
@ -122,19 +128,19 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
$this->email_object->invitation_id ? $this->email_object->invitation = $this->email_object->entity->invitations()->where('id', $this->email_object->invitation_id)->first() : $this->email_object->invitation = null;
|
$this->email_object->invitation_id ? $this->email_object->invitation = $this->email_object->entity->invitations()->where('id', $this->email_object->invitation_id)->first() : $this->email_object->invitation = null;
|
||||||
|
|
||||||
$this->email_object->invitation_id ? $this->email_object->contact = $this->email_object->invitation->contact : $this->email_object->contact = null;
|
$this->email_object->invitation_id ? $this->email_object->contact = $this->email_object->invitation->contact : $this->email_object->contact = null;
|
||||||
|
|
||||||
$this->email_object->client_id ? $this->email_object->client = Client::withTrashed()->find($this->email_object->client_id) : $this->email_object->client = null;
|
$this->email_object->client_id ? $this->email_object->client = Client::withTrashed()->find($this->email_object->client_id) : $this->email_object->client = null;
|
||||||
|
|
||||||
$this->email_object->vendor_id ? $this->email_object->vendor = Vendor::withTrashed()->find($this->email_object->vendor_id) : $this->email_object->vendor = null;
|
|
||||||
|
|
||||||
if (!$this->email_object->contact) {
|
|
||||||
$this->email_object->vendor_contact_id ? $this->email_object->contact = VendorContact::withTrashed()->find($this->email_object->vendor_contact_id) : null;
|
|
||||||
|
|
||||||
$this->email_object->client_contact_id ? $this->email_object->contact = ClientContact::withTrashed()->find($this->email_object->client_contact_id) : null;
|
$this->email_object->vendor_id ? $this->email_object->vendor = Vendor::withTrashed()->find($this->email_object->vendor_id) : $this->email_object->vendor = null;
|
||||||
|
|
||||||
|
if (!$this->email_object->contact) {
|
||||||
|
$this->email_object->vendor_contact_id ? $this->email_object->contact = VendorContact::withTrashed()->find($this->email_object->vendor_contact_id) : null;
|
||||||
|
|
||||||
|
$this->email_object->client_contact_id ? $this->email_object->contact = ClientContact::withTrashed()->find($this->email_object->client_contact_id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->email_object->user_id ? $this->email_object->user = User::withTrashed()->find($this->email_object->user_id) : $this->email_object->user = $this->company->owner();
|
$this->email_object->user_id ? $this->email_object->user = User::withTrashed()->find($this->email_object->user_id) : $this->email_object->user = $this->company->owner();
|
||||||
|
|
||||||
$this->email_object->company_key = $this->company->company_key;
|
$this->email_object->company_key = $this->company->company_key;
|
||||||
|
|
||||||
@ -151,12 +157,12 @@ class Email implements ShouldQueue
|
|||||||
$this->email_object->signature = $this->email_object->settings->email_signature;
|
$this->email_object->signature = $this->email_object->settings->email_signature;
|
||||||
|
|
||||||
$this->email_object->invitation_key = $this->email_object->invitation ? $this->email_object->invitation->key : null;
|
$this->email_object->invitation_key = $this->email_object->invitation ? $this->email_object->invitation->key : null;
|
||||||
|
|
||||||
$this->resolveVariables();
|
$this->resolveVariables();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the correct set of variables
|
* Generates the correct set of variables
|
||||||
* @todo handle payment engine here also
|
* @todo handle payment engine here also
|
||||||
@ -165,7 +171,7 @@ class Email implements ShouldQueue
|
|||||||
private function resolveVariables(): self
|
private function resolveVariables(): self
|
||||||
{
|
{
|
||||||
$_variables = $this->email_object->variables;
|
$_variables = $this->email_object->variables;
|
||||||
|
|
||||||
match (class_basename($this->email_object->entity)) {
|
match (class_basename($this->email_object->entity)) {
|
||||||
"Invoice" => $this->email_object->variables = (new HtmlEngine($this->email_object->invitation))->makeValues(),
|
"Invoice" => $this->email_object->variables = (new HtmlEngine($this->email_object->invitation))->makeValues(),
|
||||||
"Quote" => $this->email_object->variables = (new HtmlEngine($this->email_object->invitation))->makeValues(),
|
"Quote" => $this->email_object->variables = (new HtmlEngine($this->email_object->invitation))->makeValues(),
|
||||||
@ -181,7 +187,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tearDown
|
* tearDown
|
||||||
*
|
*
|
||||||
@ -199,7 +205,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the email defaults
|
* Builds the email defaults
|
||||||
*
|
*
|
||||||
@ -211,7 +217,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the mailable
|
* Populates the mailable
|
||||||
*
|
*
|
||||||
@ -220,10 +226,10 @@ class Email implements ShouldQueue
|
|||||||
public function buildMailable(): self
|
public function buildMailable(): self
|
||||||
{
|
{
|
||||||
$this->mailable = new EmailMailable($this->email_object);
|
$this->mailable = new EmailMailable($this->email_object);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to send the email
|
* Attempts to send the email
|
||||||
*
|
*
|
||||||
@ -245,24 +251,28 @@ class Email implements ShouldQueue
|
|||||||
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint);
|
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->client_brevo_secret) {
|
||||||
|
$mailer->brevo_config($this->client_brevo_secret, $this->client_brevo_domain, $this->client_brevo_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
/* Attempt the send! */
|
/* Attempt the send! */
|
||||||
try {
|
try {
|
||||||
nlog("Using mailer => ". $this->mailer. " ". now()->toDateTimeString());
|
nlog("Using mailer => " . $this->mailer . " " . now()->toDateTimeString());
|
||||||
|
|
||||||
$mailer->send($this->mailable);
|
$mailer->send($this->mailable);
|
||||||
|
|
||||||
Cache::increment("email_quota".$this->company->account->key);
|
Cache::increment("email_quota" . $this->company->account->key);
|
||||||
|
|
||||||
LightLogs::create(new EmailSuccess($this->company->company_key, $this->mailable->subject))
|
LightLogs::create(new EmailSuccess($this->company->company_key, $this->mailable->subject))
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
} catch(\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
|
} catch (\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
|
||||||
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
$this->logMailError($e->getMessage(), $this->company->clients()->first());
|
$this->logMailError($e->getMessage(), $this->company->clients()->first());
|
||||||
return;
|
return;
|
||||||
} catch(\Symfony\Component\Mime\Exception\LogicException $e) {
|
} catch (\Symfony\Component\Mime\Exception\LogicException $e) {
|
||||||
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
@ -288,7 +298,7 @@ class Email implements ShouldQueue
|
|||||||
$address_object = reset($this->email_object->to);
|
$address_object = reset($this->email_object->to);
|
||||||
|
|
||||||
$email = $address_object->address ?? '';
|
$email = $address_object->address ?? '';
|
||||||
|
|
||||||
$message = "Recipient {$email} has been suppressed and cannot receive emails from you.";
|
$message = "Recipient {$email} has been suppressed and cannot receive emails from you.";
|
||||||
|
|
||||||
$this->fail();
|
$this->fail();
|
||||||
@ -306,12 +316,12 @@ class Email implements ShouldQueue
|
|||||||
if ($e instanceof ClientException) { //postmark specific failure
|
if ($e instanceof ClientException) { //postmark specific failure
|
||||||
$response = $e->getResponse();
|
$response = $e->getResponse();
|
||||||
$message_body = json_decode($response->getBody()->getContents());
|
$message_body = json_decode($response->getBody()->getContents());
|
||||||
|
|
||||||
if ($message_body && property_exists($message_body, 'Message')) {
|
if ($message_body && property_exists($message_body, 'Message')) {
|
||||||
$message = $message_body->Message;
|
$message = $message_body->Message;
|
||||||
nlog($message);
|
nlog($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->fail();
|
$this->fail();
|
||||||
$this->cleanUpMailers();
|
$this->cleanUpMailers();
|
||||||
return;
|
return;
|
||||||
@ -333,7 +343,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
sleep(rand(0, 3));
|
sleep(rand(0, 3));
|
||||||
|
|
||||||
$this->release($this->backoff()[$this->attempts()-1]);
|
$this->release($this->backoff()[$this->attempts() - 1]);
|
||||||
|
|
||||||
$message = null;
|
$message = null;
|
||||||
}
|
}
|
||||||
@ -342,16 +352,16 @@ class Email implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On the hosted platform we scan all outbound email for
|
* On the hosted platform we scan all outbound email for
|
||||||
* spam. This sequence processes the filters we use on all
|
* spam. This sequence processes the filters we use on all
|
||||||
* emails.
|
* emails.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function preFlightChecksFail(): bool
|
public function preFlightChecksFail(): bool
|
||||||
{
|
{
|
||||||
/* Always send if disabled */
|
/* Always send if disabled */
|
||||||
if($this->override) {
|
if ($this->override) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +421,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hasInValidEmails
|
* hasInValidEmails
|
||||||
*
|
*
|
||||||
@ -436,7 +446,7 @@ class Email implements ShouldQueue
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($address_object->name == " " || $address_object->name == "") {
|
if ($address_object->name == " " || $address_object->name == "") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,6 +482,10 @@ class Email implements ShouldQueue
|
|||||||
$this->mailer = 'mailgun';
|
$this->mailer = 'mailgun';
|
||||||
$this->setMailgunMailer();
|
$this->setMailgunMailer();
|
||||||
return $this;
|
return $this;
|
||||||
|
case 'client_brevo':
|
||||||
|
$this->mailer = 'brevo';
|
||||||
|
$this->setBrevoMailer();
|
||||||
|
return $this;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$this->mailer = config('mail.default');
|
$this->mailer = config('mail.default');
|
||||||
@ -504,7 +518,7 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
if (env($this->company->id . '_MAIL_FROM_ADDRESS')) {
|
if (env($this->company->id . '_MAIL_FROM_ADDRESS')) {
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME')));
|
->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,6 +539,12 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
$this->client_mailgun_endpoint = null;
|
$this->client_mailgun_endpoint = null;
|
||||||
|
|
||||||
|
$this->client_brevo_secret = null;
|
||||||
|
|
||||||
|
$this->client_brevo_domain = null;
|
||||||
|
|
||||||
|
$this->client_brevo_endpoint = null;
|
||||||
|
|
||||||
//always dump the drivers to prevent reuse
|
//always dump the drivers to prevent reuse
|
||||||
app('mail.manager')->forgetMailers();
|
app('mail.manager')->forgetMailers();
|
||||||
}
|
}
|
||||||
@ -541,7 +561,7 @@ class Email implements ShouldQueue
|
|||||||
/* Always ensure the user is set on the correct account */
|
/* Always ensure the user is set on the correct account */
|
||||||
if ($user->account_id != $this->company->account_id) {
|
if ($user->account_id != $this->company->account_id) {
|
||||||
$this->email_object->settings->email_sending_method = 'default';
|
$this->email_object->settings->email_sending_method = 'default';
|
||||||
|
|
||||||
return $this->setMailDriver();
|
return $this->setMailDriver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -587,7 +607,31 @@ class Email implements ShouldQueue
|
|||||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($sending_email, $sending_user);
|
->from($sending_email, $sending_user);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Configures Brevo using client supplied secret
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
|
private function setBrevoMailer()
|
||||||
|
{
|
||||||
|
if (strlen($this->email_object->settings->brevo_secret) > 2 && strlen($this->email_object->settings->brevo_domain) > 2) {
|
||||||
|
$this->client_brevo_secret = $this->email_object->settings->brevo_secret;
|
||||||
|
$this->client_brevo_domain = $this->email_object->settings->brevo_domain;
|
||||||
|
$this->client_brevo_endpoint = $this->email_object->settings->brevo_endpoint;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->email_object->settings->email_sending_method = 'default';
|
||||||
|
return $this->setMailDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
|
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||||
|
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
|
$this->mailable
|
||||||
|
->from($sending_email, $sending_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -607,9 +651,9 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($sending_email, $sending_user);
|
->from($sending_email, $sending_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -619,9 +663,9 @@ class Email implements ShouldQueue
|
|||||||
private function setOfficeMailer()
|
private function setOfficeMailer()
|
||||||
{
|
{
|
||||||
$user = $this->resolveSendingUser();
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
$token = $this->refreshOfficeToken($user);
|
$token = $this->refreshOfficeToken($user);
|
||||||
@ -635,10 +679,10 @@ class Email implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($user->email, $user->name())
|
->from($user->email, $user->name())
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
->withSymfonyMessage(function ($message) use ($token) {
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -650,7 +694,7 @@ class Email implements ShouldQueue
|
|||||||
$user = $this->resolveSendingUser();
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
$google = (new Google())->init();
|
$google = (new Google())->init();
|
||||||
@ -662,7 +706,7 @@ class Email implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
||||||
} catch(\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
||||||
$this->email_object->settings->email_sending_method = 'default';
|
$this->email_object->settings->email_sending_method = 'default';
|
||||||
return $this->setMailDriver();
|
return $this->setMailDriver();
|
||||||
@ -682,7 +726,7 @@ class Email implements ShouldQueue
|
|||||||
* Now that our token is refreshed and valid we can boot the
|
* Now that our token is refreshed and valid we can boot the
|
||||||
* mail driver at runtime and also set the token which will persist
|
* mail driver at runtime and also set the token which will persist
|
||||||
* just for this request.
|
* just for this request.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$token = $user->oauth_user_token->access_token;
|
$token = $user->oauth_user_token->access_token;
|
||||||
|
|
||||||
@ -693,10 +737,10 @@ class Email implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->mailable
|
$this->mailable
|
||||||
->from($user->email, $user->name())
|
->from($user->email, $user->name())
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
->withSymfonyMessage(function ($message) use ($token) {
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -706,7 +750,7 @@ class Email implements ShouldQueue
|
|||||||
* @param null | \App\Models\Client $recipient_object
|
* @param null | \App\Models\Client $recipient_object
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function logMailError($errors, $recipient_object) :void
|
private function logMailError($errors, $recipient_object): void
|
||||||
{
|
{
|
||||||
(new SystemLogger(
|
(new SystemLogger(
|
||||||
$errors,
|
$errors,
|
||||||
@ -722,7 +766,7 @@ class Email implements ShouldQueue
|
|||||||
$job_failure->string_metric6 = substr($errors, 0, 150);
|
$job_failure->string_metric6 = substr($errors, 0, 150);
|
||||||
|
|
||||||
LightLogs::create($job_failure)
|
LightLogs::create($job_failure)
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
$job_failure = null;
|
$job_failure = null;
|
||||||
}
|
}
|
||||||
@ -743,14 +787,14 @@ class Email implements ShouldQueue
|
|||||||
|
|
||||||
$token = json_decode($guzzle->post($url, [
|
$token = json_decode($guzzle->post($url, [
|
||||||
'form_params' => [
|
'form_params' => [
|
||||||
'client_id' => config('ninja.o365.client_id') ,
|
'client_id' => config('ninja.o365.client_id'),
|
||||||
'client_secret' => config('ninja.o365.client_secret') ,
|
'client_secret' => config('ninja.o365.client_secret'),
|
||||||
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
||||||
'grant_type' => 'refresh_token',
|
'grant_type' => 'refresh_token',
|
||||||
'refresh_token' => $user->oauth_user_refresh_token
|
'refresh_token' => $user->oauth_user_refresh_token
|
||||||
],
|
],
|
||||||
])->getBody()->getContents());
|
])->getBody()->getContents());
|
||||||
|
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
||||||
$user->oauth_user_token = $token->access_token;
|
$user->oauth_user_token = $token->access_token;
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
"sprain/swiss-qr-bill": "^4.3",
|
"sprain/swiss-qr-bill": "^4.3",
|
||||||
"square/square": "30.0.0.*",
|
"square/square": "30.0.0.*",
|
||||||
"stripe/stripe-php": "^12",
|
"stripe/stripe-php": "^12",
|
||||||
|
"symfony/brevo-mailer": "^7.0",
|
||||||
"symfony/http-client": "^6.0",
|
"symfony/http-client": "^6.0",
|
||||||
"symfony/mailgun-mailer": "^6.1",
|
"symfony/mailgun-mailer": "^6.1",
|
||||||
"symfony/postmark-mailer": "^6.1",
|
"symfony/postmark-mailer": "^6.1",
|
||||||
@ -179,4 +180,4 @@
|
|||||||
],
|
],
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true
|
"prefer-stable": true
|
||||||
}
|
}
|
||||||
|
73
composer.lock
generated
73
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "28b57fe6eac3d71c607125cda9a6a537",
|
"content-hash": "ba57ee16621201bac10def4422ba9161",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "afosto/yaac",
|
"name": "afosto/yaac",
|
||||||
@ -10716,6 +10716,75 @@
|
|||||||
},
|
},
|
||||||
"time": "2023-10-16T18:04:12+00:00"
|
"time": "2023-10-16T18:04:12+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/brevo-mailer",
|
||||||
|
"version": "v7.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/brevo-mailer.git",
|
||||||
|
"reference": "83db87e0f44653cd40aeef54a2f57ab6bfccadfe"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/brevo-mailer/zipball/83db87e0f44653cd40aeef54a2f57ab6bfccadfe",
|
||||||
|
"reference": "83db87e0f44653cd40aeef54a2f57ab6bfccadfe",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"symfony/mailer": "^5.4.21|^6.2.7|^7.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/mime": "<6.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/http-client": "^6.3|^7.0",
|
||||||
|
"symfony/webhook": "^6.3|^7.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-mailer-bridge",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\Mailer\\Bridge\\Brevo\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Pierre Tanguy",
|
||||||
|
"homepage": "https://github.com/petanguy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony Brevo Mailer Bridge",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/brevo-mailer/tree/v7.0.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-11-06T17:20:05+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v6.4.1",
|
"version": "v6.4.1",
|
||||||
@ -18046,5 +18115,5 @@
|
|||||||
"platform-dev": {
|
"platform-dev": {
|
||||||
"php": "^8.1|^8.2"
|
"php": "^8.1|^8.2"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.3.0"
|
"plugin-api-version": "2.6.0"
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ return [
|
|||||||
| sending an e-mail. You will specify which one you are using for your
|
| sending an e-mail. You will specify which one you are using for your
|
||||||
| mailers below. You are free to add additional mailers as required.
|
| mailers below. You are free to add additional mailers as required.
|
||||||
|
|
|
|
||||||
| Supported: "smtp", "sendmail", "mailgun", "ses",
|
| Supported: "smtp", "sendmail", "mailgun", "brevo", "ses",
|
||||||
| "postmark", "log", "array", "failover"
|
| "postmark", "log", "array", "failover"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
@ -54,6 +54,10 @@ return [
|
|||||||
'transport' => 'mailgun',
|
'transport' => 'mailgun',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'brevo' => [
|
||||||
|
'transport' => 'brevo',
|
||||||
|
],
|
||||||
|
|
||||||
'postmark' => [
|
'postmark' => [
|
||||||
'transport' => 'postmark',
|
'transport' => 'postmark',
|
||||||
],
|
],
|
||||||
|
@ -12,7 +12,7 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This file is for storing the credentials for third party services such
|
| This file is for storing the credentials for third party services such
|
||||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
| as Mailgun, Brevo, Postmark, AWS and more. This file provides the de facto
|
||||||
| location for this type of information, allowing packages to have
|
| location for this type of information, allowing packages to have
|
||||||
| a conventional file to locate the various service credentials.
|
| a conventional file to locate the various service credentials.
|
||||||
|
|
|
|
||||||
@ -25,6 +25,13 @@ return [
|
|||||||
'scheme' => 'https',
|
'scheme' => 'https',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'brevo' => [
|
||||||
|
'domain' => env('BREVO_DOMAIN', ''),
|
||||||
|
'secret' => env('BREVO_SECRET', ''),
|
||||||
|
'endpoint' => env('BREVO_ENDPOINT', 'api.mailgun.net'),
|
||||||
|
'scheme' => 'https',
|
||||||
|
],
|
||||||
|
|
||||||
'postmark' => [
|
'postmark' => [
|
||||||
'token' => env('POSTMARK_SECRET', ''),
|
'token' => env('POSTMARK_SECRET', ''),
|
||||||
],
|
],
|
||||||
@ -55,8 +62,8 @@ return [
|
|||||||
],
|
],
|
||||||
|
|
||||||
'stripe' => [
|
'stripe' => [
|
||||||
'model' => App\Models\User::class,
|
'model' => App\Models\User::class,
|
||||||
'key' => env('STRIPE_KEY'),
|
'key' => env('STRIPE_KEY'),
|
||||||
'secret' => env('STRIPE_SECRET'),
|
'secret' => env('STRIPE_SECRET'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -2237,6 +2237,8 @@ $lang = array(
|
|||||||
'encryption' => 'Encryption',
|
'encryption' => 'Encryption',
|
||||||
'mailgun_domain' => 'Mailgun Domain',
|
'mailgun_domain' => 'Mailgun Domain',
|
||||||
'mailgun_private_key' => 'Mailgun Private Key',
|
'mailgun_private_key' => 'Mailgun Private Key',
|
||||||
|
'brevo_domain' => 'Brevo Domain',
|
||||||
|
'brevo_private_key' => 'Brevo Private Key',
|
||||||
'send_test_email' => 'Send test email',
|
'send_test_email' => 'Send test email',
|
||||||
'select_label' => 'Select Label',
|
'select_label' => 'Select Label',
|
||||||
'label' => 'Label',
|
'label' => 'Label',
|
||||||
@ -3857,308 +3859,308 @@ $lang = array(
|
|||||||
'registration_url' => 'Registration URL',
|
'registration_url' => 'Registration URL',
|
||||||
'show_product_cost' => 'Show Product Cost',
|
'show_product_cost' => 'Show Product Cost',
|
||||||
'complete' => 'Complete',
|
'complete' => 'Complete',
|
||||||
'next' => 'Next',
|
'next' => 'Next',
|
||||||
'next_step' => 'Next step',
|
'next_step' => 'Next step',
|
||||||
'notification_credit_sent_subject' => 'Credit :invoice was sent to :client',
|
'notification_credit_sent_subject' => 'Credit :invoice was sent to :client',
|
||||||
'notification_credit_viewed_subject' => 'Credit :invoice was viewed by :client',
|
'notification_credit_viewed_subject' => 'Credit :invoice was viewed by :client',
|
||||||
'notification_credit_sent' => 'The following client :client was emailed Credit :invoice for :amount.',
|
'notification_credit_sent' => 'The following client :client was emailed Credit :invoice for :amount.',
|
||||||
'notification_credit_viewed' => 'The following client :client viewed Credit :credit for :amount.',
|
'notification_credit_viewed' => 'The following client :client viewed Credit :credit for :amount.',
|
||||||
'reset_password_text' => 'Enter your email to reset your password.',
|
'reset_password_text' => 'Enter your email to reset your password.',
|
||||||
'password_reset' => 'Password reset',
|
'password_reset' => 'Password reset',
|
||||||
'account_login_text' => 'Welcome! Glad to see you.',
|
'account_login_text' => 'Welcome! Glad to see you.',
|
||||||
'request_cancellation' => 'Request cancellation',
|
'request_cancellation' => 'Request cancellation',
|
||||||
'delete_payment_method' => 'Delete Payment Method',
|
'delete_payment_method' => 'Delete Payment Method',
|
||||||
'about_to_delete_payment_method' => 'You are about to delete the payment method.',
|
'about_to_delete_payment_method' => 'You are about to delete the payment method.',
|
||||||
'action_cant_be_reversed' => 'Action can\'t be reversed',
|
'action_cant_be_reversed' => 'Action can\'t be reversed',
|
||||||
'profile_updated_successfully' => 'The profile has been updated successfully.',
|
'profile_updated_successfully' => 'The profile has been updated successfully.',
|
||||||
'currency_ethiopian_birr' => 'Ethiopian Birr',
|
'currency_ethiopian_birr' => 'Ethiopian Birr',
|
||||||
'client_information_text' => 'Use a permanent address where you can receive mail.',
|
'client_information_text' => 'Use a permanent address where you can receive mail.',
|
||||||
'status_id' => 'Invoice Status',
|
'status_id' => 'Invoice Status',
|
||||||
'email_already_register' => 'This email is already linked to an account',
|
'email_already_register' => 'This email is already linked to an account',
|
||||||
'locations' => 'Locations',
|
'locations' => 'Locations',
|
||||||
'freq_indefinitely' => 'Indefinitely',
|
'freq_indefinitely' => 'Indefinitely',
|
||||||
'cycles_remaining' => 'Cycles remaining',
|
'cycles_remaining' => 'Cycles remaining',
|
||||||
'i_understand_delete' => 'I understand, delete',
|
'i_understand_delete' => 'I understand, delete',
|
||||||
'download_files' => 'Download Files',
|
'download_files' => 'Download Files',
|
||||||
'download_timeframe' => 'Use this link to download your files, the link will expire in 1 hour.',
|
'download_timeframe' => 'Use this link to download your files, the link will expire in 1 hour.',
|
||||||
'new_signup' => 'New Signup',
|
'new_signup' => 'New Signup',
|
||||||
'new_signup_text' => 'A new account has been created by :user - :email - from IP address: :ip',
|
'new_signup_text' => 'A new account has been created by :user - :email - from IP address: :ip',
|
||||||
'notification_payment_paid_subject' => 'Payment was made by :client',
|
'notification_payment_paid_subject' => 'Payment was made by :client',
|
||||||
'notification_partial_payment_paid_subject' => 'Partial payment was made by :client',
|
'notification_partial_payment_paid_subject' => 'Partial payment was made by :client',
|
||||||
'notification_payment_paid' => 'A payment of :amount was made by client :client towards :invoice',
|
'notification_payment_paid' => 'A payment of :amount was made by client :client towards :invoice',
|
||||||
'notification_partial_payment_paid' => 'A partial payment of :amount was made by client :client towards :invoice',
|
'notification_partial_payment_paid' => 'A partial payment of :amount was made by client :client towards :invoice',
|
||||||
'notification_bot' => 'Notification Bot',
|
'notification_bot' => 'Notification Bot',
|
||||||
'invoice_number_placeholder' => 'Invoice # :invoice',
|
'invoice_number_placeholder' => 'Invoice # :invoice',
|
||||||
'entity_number_placeholder' => ':entity # :entity_number',
|
'entity_number_placeholder' => ':entity # :entity_number',
|
||||||
'email_link_not_working' => 'If the button above isn\'t working for you, please click on the link',
|
'email_link_not_working' => 'If the button above isn\'t working for you, please click on the link',
|
||||||
'display_log' => 'Display Log',
|
'display_log' => 'Display Log',
|
||||||
'send_fail_logs_to_our_server' => 'Report errors in realtime',
|
'send_fail_logs_to_our_server' => 'Report errors in realtime',
|
||||||
'setup' => 'Setup',
|
'setup' => 'Setup',
|
||||||
'quick_overview_statistics' => 'Quick overview & statistics',
|
'quick_overview_statistics' => 'Quick overview & statistics',
|
||||||
'update_your_personal_info' => 'Update your personal information',
|
'update_your_personal_info' => 'Update your personal information',
|
||||||
'name_website_logo' => 'Name, website & logo',
|
'name_website_logo' => 'Name, website & logo',
|
||||||
'make_sure_use_full_link' => 'Make sure you use full link to your site',
|
'make_sure_use_full_link' => 'Make sure you use full link to your site',
|
||||||
'personal_address' => 'Personal address',
|
'personal_address' => 'Personal address',
|
||||||
'enter_your_personal_address' => 'Enter your personal address',
|
'enter_your_personal_address' => 'Enter your personal address',
|
||||||
'enter_your_shipping_address' => 'Enter your shipping address',
|
'enter_your_shipping_address' => 'Enter your shipping address',
|
||||||
'list_of_invoices' => 'List of invoices',
|
'list_of_invoices' => 'List of invoices',
|
||||||
'with_selected' => 'With selected',
|
'with_selected' => 'With selected',
|
||||||
'invoice_still_unpaid' => 'This invoice is still not paid. Click the button to complete the payment',
|
'invoice_still_unpaid' => 'This invoice is still not paid. Click the button to complete the payment',
|
||||||
'list_of_recurring_invoices' => 'List of recurring invoices',
|
'list_of_recurring_invoices' => 'List of recurring invoices',
|
||||||
'details_of_recurring_invoice' => 'Here are some details about recurring invoice',
|
'details_of_recurring_invoice' => 'Here are some details about recurring invoice',
|
||||||
'cancellation' => 'Cancellation',
|
'cancellation' => 'Cancellation',
|
||||||
'about_cancellation' => 'In case you want to stop the recurring invoice, please click to request the cancellation.',
|
'about_cancellation' => 'In case you want to stop the recurring invoice, please click to request the cancellation.',
|
||||||
'cancellation_warning' => 'Warning! You are requesting a cancellation of this service. Your service may be cancelled with no further notification to you.',
|
'cancellation_warning' => 'Warning! You are requesting a cancellation of this service. Your service may be cancelled with no further notification to you.',
|
||||||
'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!',
|
'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!',
|
||||||
'list_of_payments' => 'List of payments',
|
'list_of_payments' => 'List of payments',
|
||||||
'payment_details' => 'Details of the payment',
|
'payment_details' => 'Details of the payment',
|
||||||
'list_of_payment_invoices' => 'List of invoices affected by the payment',
|
'list_of_payment_invoices' => 'List of invoices affected by the payment',
|
||||||
'list_of_payment_methods' => 'List of payment methods',
|
'list_of_payment_methods' => 'List of payment methods',
|
||||||
'payment_method_details' => 'Details of payment method',
|
'payment_method_details' => 'Details of payment method',
|
||||||
'permanently_remove_payment_method' => 'Permanently remove this payment method.',
|
'permanently_remove_payment_method' => 'Permanently remove this payment method.',
|
||||||
'warning_action_cannot_be_reversed' => 'Warning! This action can not be reversed!',
|
'warning_action_cannot_be_reversed' => 'Warning! This action can not be reversed!',
|
||||||
'confirmation' => 'Confirmation',
|
'confirmation' => 'Confirmation',
|
||||||
'list_of_quotes' => 'Quotes',
|
'list_of_quotes' => 'Quotes',
|
||||||
'waiting_for_approval' => 'Waiting for approval',
|
'waiting_for_approval' => 'Waiting for approval',
|
||||||
'quote_still_not_approved' => 'This quote is still not approved',
|
'quote_still_not_approved' => 'This quote is still not approved',
|
||||||
'list_of_credits' => 'Credits',
|
'list_of_credits' => 'Credits',
|
||||||
'required_extensions' => 'Required extensions',
|
'required_extensions' => 'Required extensions',
|
||||||
'php_version' => 'PHP version',
|
'php_version' => 'PHP version',
|
||||||
'writable_env_file' => 'Writable .env file',
|
'writable_env_file' => 'Writable .env file',
|
||||||
'env_not_writable' => '.env file is not writable by the current user.',
|
'env_not_writable' => '.env file is not writable by the current user.',
|
||||||
'minumum_php_version' => 'Minimum PHP version',
|
'minumum_php_version' => 'Minimum PHP version',
|
||||||
'satisfy_requirements' => 'Make sure all requirements are satisfied.',
|
'satisfy_requirements' => 'Make sure all requirements are satisfied.',
|
||||||
'oops_issues' => 'Oops, something does not look right!',
|
'oops_issues' => 'Oops, something does not look right!',
|
||||||
'open_in_new_tab' => 'Open in new tab',
|
'open_in_new_tab' => 'Open in new tab',
|
||||||
'complete_your_payment' => 'Complete payment',
|
'complete_your_payment' => 'Complete payment',
|
||||||
'authorize_for_future_use' => 'Authorize payment method for future use',
|
'authorize_for_future_use' => 'Authorize payment method for future use',
|
||||||
'page' => 'Page',
|
'page' => 'Page',
|
||||||
'per_page' => 'Per page',
|
'per_page' => 'Per page',
|
||||||
'of' => 'Of',
|
'of' => 'Of',
|
||||||
'view_credit' => 'View Credit',
|
'view_credit' => 'View Credit',
|
||||||
'to_view_entity_password' => 'To view the :entity you need to enter password.',
|
'to_view_entity_password' => 'To view the :entity you need to enter password.',
|
||||||
'showing_x_of' => 'Showing :first to :last out of :total results',
|
'showing_x_of' => 'Showing :first to :last out of :total results',
|
||||||
'no_results' => 'No results found.',
|
'no_results' => 'No results found.',
|
||||||
'payment_failed_subject' => 'Payment failed for Client :client',
|
'payment_failed_subject' => 'Payment failed for Client :client',
|
||||||
'payment_failed_body' => 'A payment made by client :client failed with message :message',
|
'payment_failed_body' => 'A payment made by client :client failed with message :message',
|
||||||
'register' => 'Register',
|
'register' => 'Register',
|
||||||
'register_label' => 'Create your account in seconds',
|
'register_label' => 'Create your account in seconds',
|
||||||
'password_confirmation' => 'Confirm your password',
|
'password_confirmation' => 'Confirm your password',
|
||||||
'verification' => 'Verification',
|
'verification' => 'Verification',
|
||||||
'complete_your_bank_account_verification' => 'Before using a bank account it must be verified.',
|
'complete_your_bank_account_verification' => 'Before using a bank account it must be verified.',
|
||||||
'checkout_com' => 'Checkout.com',
|
'checkout_com' => 'Checkout.com',
|
||||||
'footer_label' => 'Copyright © :year :company.',
|
'footer_label' => 'Copyright © :year :company.',
|
||||||
'credit_card_invalid' => 'Provided credit card number is not valid.',
|
'credit_card_invalid' => 'Provided credit card number is not valid.',
|
||||||
'month_invalid' => 'Provided month is not valid.',
|
'month_invalid' => 'Provided month is not valid.',
|
||||||
'year_invalid' => 'Provided year is not valid.',
|
'year_invalid' => 'Provided year is not valid.',
|
||||||
'https_required' => 'HTTPS is required, form will fail',
|
'https_required' => 'HTTPS is required, form will fail',
|
||||||
'if_you_need_help' => 'If you need help you can post to our',
|
'if_you_need_help' => 'If you need help you can post to our',
|
||||||
'update_password_on_confirm' => 'After updating password, your account will be confirmed.',
|
'update_password_on_confirm' => 'After updating password, your account will be confirmed.',
|
||||||
'bank_account_not_linked' => 'To pay with a bank account, first you have to add it as payment method.',
|
'bank_account_not_linked' => 'To pay with a bank account, first you have to add it as payment method.',
|
||||||
'application_settings_label' => 'Let\'s store basic information about your Invoice Ninja!',
|
'application_settings_label' => 'Let\'s store basic information about your Invoice Ninja!',
|
||||||
'recommended_in_production' => 'Highly recommended in production',
|
'recommended_in_production' => 'Highly recommended in production',
|
||||||
'enable_only_for_development' => 'Enable only for development',
|
'enable_only_for_development' => 'Enable only for development',
|
||||||
'test_pdf' => 'Test PDF',
|
'test_pdf' => 'Test PDF',
|
||||||
'checkout_authorize_label' => 'Checkout.com can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.',
|
'checkout_authorize_label' => 'Checkout.com can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.',
|
||||||
'sofort_authorize_label' => 'Bank account (SOFORT) can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store payment details" during payment process.',
|
'sofort_authorize_label' => 'Bank account (SOFORT) can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store payment details" during payment process.',
|
||||||
'node_status' => 'Node status',
|
'node_status' => 'Node status',
|
||||||
'npm_status' => 'NPM status',
|
'npm_status' => 'NPM status',
|
||||||
'node_status_not_found' => 'I could not find Node anywhere. Is it installed?',
|
'node_status_not_found' => 'I could not find Node anywhere. Is it installed?',
|
||||||
'npm_status_not_found' => 'I could not find NPM anywhere. Is it installed?',
|
'npm_status_not_found' => 'I could not find NPM anywhere. Is it installed?',
|
||||||
'locked_invoice' => 'This invoice is locked and unable to be modified',
|
'locked_invoice' => 'This invoice is locked and unable to be modified',
|
||||||
'downloads' => 'Downloads',
|
'downloads' => 'Downloads',
|
||||||
'resource' => 'Resource',
|
'resource' => 'Resource',
|
||||||
'document_details' => 'Details about the document',
|
'document_details' => 'Details about the document',
|
||||||
'hash' => 'Hash',
|
'hash' => 'Hash',
|
||||||
'resources' => 'Resources',
|
'resources' => 'Resources',
|
||||||
'allowed_file_types' => 'Allowed file types:',
|
'allowed_file_types' => 'Allowed file types:',
|
||||||
'common_codes' => 'Common codes and their meanings',
|
'common_codes' => 'Common codes and their meanings',
|
||||||
'payment_error_code_20087' => '20087: Bad Track Data (invalid CVV and/or expiry date)',
|
'payment_error_code_20087' => '20087: Bad Track Data (invalid CVV and/or expiry date)',
|
||||||
'download_selected' => 'Download selected',
|
'download_selected' => 'Download selected',
|
||||||
'to_pay_invoices' => 'To pay invoices, you have to',
|
'to_pay_invoices' => 'To pay invoices, you have to',
|
||||||
'add_payment_method_first' => 'add payment method',
|
'add_payment_method_first' => 'add payment method',
|
||||||
'no_items_selected' => 'No items selected.',
|
'no_items_selected' => 'No items selected.',
|
||||||
'payment_due' => 'Payment due',
|
'payment_due' => 'Payment due',
|
||||||
'account_balance' => 'Account Balance',
|
'account_balance' => 'Account Balance',
|
||||||
'thanks' => 'Thanks',
|
'thanks' => 'Thanks',
|
||||||
'minimum_required_payment' => 'Minimum required payment is :amount',
|
'minimum_required_payment' => 'Minimum required payment is :amount',
|
||||||
'under_payments_disabled' => 'Company doesn\'t support underpayments.',
|
'under_payments_disabled' => 'Company doesn\'t support underpayments.',
|
||||||
'over_payments_disabled' => 'Company doesn\'t support overpayments.',
|
'over_payments_disabled' => 'Company doesn\'t support overpayments.',
|
||||||
'saved_at' => 'Saved at :time',
|
'saved_at' => 'Saved at :time',
|
||||||
'credit_payment' => 'Credit applied to Invoice :invoice_number',
|
'credit_payment' => 'Credit applied to Invoice :invoice_number',
|
||||||
'credit_subject' => 'New credit :number from :account',
|
'credit_subject' => 'New credit :number from :account',
|
||||||
'credit_message' => 'To view your credit for :amount, click the link below.',
|
'credit_message' => 'To view your credit for :amount, click the link below.',
|
||||||
'payment_type_Crypto' => 'Cryptocurrency',
|
'payment_type_Crypto' => 'Cryptocurrency',
|
||||||
'payment_type_Credit' => 'Credit',
|
'payment_type_Credit' => 'Credit',
|
||||||
'store_for_future_use' => 'Store for future use',
|
'store_for_future_use' => 'Store for future use',
|
||||||
'pay_with_credit' => 'Pay with credit',
|
'pay_with_credit' => 'Pay with credit',
|
||||||
'payment_method_saving_failed' => 'Payment method can\'t be saved for future use.',
|
'payment_method_saving_failed' => 'Payment method can\'t be saved for future use.',
|
||||||
'pay_with' => 'Pay with',
|
'pay_with' => 'Pay with',
|
||||||
'n/a' => 'N/A',
|
'n/a' => 'N/A',
|
||||||
'by_clicking_next_you_accept_terms' => 'By clicking "Next step" you accept terms.',
|
'by_clicking_next_you_accept_terms' => 'By clicking "Next step" you accept terms.',
|
||||||
'not_specified' => 'Not specified',
|
'not_specified' => 'Not specified',
|
||||||
'before_proceeding_with_payment_warning' => 'Before proceeding with payment, you have to fill following fields',
|
'before_proceeding_with_payment_warning' => 'Before proceeding with payment, you have to fill following fields',
|
||||||
'after_completing_go_back_to_previous_page' => 'After completing, go back to previous page.',
|
'after_completing_go_back_to_previous_page' => 'After completing, go back to previous page.',
|
||||||
'pay' => 'Pay',
|
'pay' => 'Pay',
|
||||||
'instructions' => 'Instructions',
|
'instructions' => 'Instructions',
|
||||||
'notification_invoice_reminder1_sent_subject' => 'Reminder 1 for Invoice :invoice was sent to :client',
|
'notification_invoice_reminder1_sent_subject' => 'Reminder 1 for Invoice :invoice was sent to :client',
|
||||||
'notification_invoice_reminder2_sent_subject' => 'Reminder 2 for Invoice :invoice was sent to :client',
|
'notification_invoice_reminder2_sent_subject' => 'Reminder 2 for Invoice :invoice was sent to :client',
|
||||||
'notification_invoice_reminder3_sent_subject' => 'Reminder 3 for Invoice :invoice was sent to :client',
|
'notification_invoice_reminder3_sent_subject' => 'Reminder 3 for Invoice :invoice was sent to :client',
|
||||||
'notification_invoice_custom_sent_subject' => 'Custom reminder for Invoice :invoice was sent to :client',
|
'notification_invoice_custom_sent_subject' => 'Custom reminder for Invoice :invoice was sent to :client',
|
||||||
'notification_invoice_reminder_endless_sent_subject' => 'Endless reminder for Invoice :invoice was sent to :client',
|
'notification_invoice_reminder_endless_sent_subject' => 'Endless reminder for Invoice :invoice was sent to :client',
|
||||||
'assigned_user' => 'Assigned User',
|
'assigned_user' => 'Assigned User',
|
||||||
'setup_steps_notice' => 'To proceed to next step, make sure you test each section.',
|
'setup_steps_notice' => 'To proceed to next step, make sure you test each section.',
|
||||||
'setup_phantomjs_note' => 'Note about Phantom JS. Read more.',
|
'setup_phantomjs_note' => 'Note about Phantom JS. Read more.',
|
||||||
'minimum_payment' => 'Minimum Payment',
|
'minimum_payment' => 'Minimum Payment',
|
||||||
'no_action_provided' => 'No action provided. If you believe this is wrong, please contact the support.',
|
'no_action_provided' => 'No action provided. If you believe this is wrong, please contact the support.',
|
||||||
'no_payable_invoices_selected' => 'No payable invoices selected. Make sure you are not trying to pay draft invoice or invoice with zero balance due.',
|
'no_payable_invoices_selected' => 'No payable invoices selected. Make sure you are not trying to pay draft invoice or invoice with zero balance due.',
|
||||||
'required_payment_information' => 'Required payment details',
|
'required_payment_information' => 'Required payment details',
|
||||||
'required_payment_information_more' => 'To complete a payment we need more details about you.',
|
'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.',
|
'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. \n :error',
|
'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact. \n :error',
|
||||||
'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice',
|
'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice',
|
||||||
'save_payment_method_details' => 'Save payment method details',
|
'save_payment_method_details' => 'Save payment method details',
|
||||||
'new_card' => 'New card',
|
'new_card' => 'New card',
|
||||||
'new_bank_account' => 'New bank account',
|
'new_bank_account' => 'New bank account',
|
||||||
'company_limit_reached' => 'Limit of :limit companies per account.',
|
'company_limit_reached' => 'Limit of :limit companies per account.',
|
||||||
'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
|
'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
|
||||||
'credit_number_taken' => 'Credit number already taken',
|
'credit_number_taken' => 'Credit number already taken',
|
||||||
'credit_not_found' => 'Credit not found',
|
'credit_not_found' => 'Credit not found',
|
||||||
'invoices_dont_match_client' => 'Selected invoices are not from a single client',
|
'invoices_dont_match_client' => 'Selected invoices are not from a single client',
|
||||||
'duplicate_credits_submitted' => 'Duplicate credits submitted.',
|
'duplicate_credits_submitted' => 'Duplicate credits submitted.',
|
||||||
'duplicate_invoices_submitted' => 'Duplicate invoices submitted.',
|
'duplicate_invoices_submitted' => 'Duplicate invoices submitted.',
|
||||||
'credit_with_no_invoice' => 'You must have an invoice set when using a credit in a payment',
|
'credit_with_no_invoice' => 'You must have an invoice set when using a credit in a payment',
|
||||||
'client_id_required' => 'Client id is required',
|
'client_id_required' => 'Client id is required',
|
||||||
'expense_number_taken' => 'Expense number already taken',
|
'expense_number_taken' => 'Expense number already taken',
|
||||||
'invoice_number_taken' => 'Invoice number already taken',
|
'invoice_number_taken' => 'Invoice number already taken',
|
||||||
'payment_id_required' => 'Payment `id` required.',
|
'payment_id_required' => 'Payment `id` required.',
|
||||||
'unable_to_retrieve_payment' => 'Unable to retrieve specified payment',
|
'unable_to_retrieve_payment' => 'Unable to retrieve specified payment',
|
||||||
'invoice_not_related_to_payment' => 'Invoice id :invoice is not related to this payment',
|
'invoice_not_related_to_payment' => 'Invoice id :invoice is not related to this payment',
|
||||||
'credit_not_related_to_payment' => 'Credit id :credit is not related to this payment',
|
'credit_not_related_to_payment' => 'Credit id :credit is not related to this payment',
|
||||||
'max_refundable_invoice' => 'Attempting to refund more than allowed for invoice id :invoice, maximum refundable amount is :amount',
|
'max_refundable_invoice' => 'Attempting to refund more than allowed for invoice id :invoice, maximum refundable amount is :amount',
|
||||||
'refund_without_invoices' => 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.',
|
'refund_without_invoices' => 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.',
|
||||||
'refund_without_credits' => 'Attempting to refund a payment with credits attached, please specify valid credits/s to be refunded.',
|
'refund_without_credits' => 'Attempting to refund a payment with credits attached, please specify valid credits/s to be refunded.',
|
||||||
'max_refundable_credit' => 'Attempting to refund more than allowed for credit :credit, maximum refundable amount is :amount',
|
'max_refundable_credit' => 'Attempting to refund more than allowed for credit :credit, maximum refundable amount is :amount',
|
||||||
'project_client_do_not_match' => 'Project client does not match entity client',
|
'project_client_do_not_match' => 'Project client does not match entity client',
|
||||||
'quote_number_taken' => 'Quote number already taken',
|
'quote_number_taken' => 'Quote number already taken',
|
||||||
'recurring_invoice_number_taken' => 'Recurring Invoice number :number already taken',
|
'recurring_invoice_number_taken' => 'Recurring Invoice number :number already taken',
|
||||||
'user_not_associated_with_account' => 'User not associated with this account',
|
'user_not_associated_with_account' => 'User not associated with this account',
|
||||||
'amounts_do_not_balance' => 'Amounts do not balance correctly.',
|
'amounts_do_not_balance' => 'Amounts do not balance correctly.',
|
||||||
'insufficient_applied_amount_remaining' => 'Insufficient applied amount remaining to cover payment.',
|
'insufficient_applied_amount_remaining' => 'Insufficient applied amount remaining to cover payment.',
|
||||||
'insufficient_credit_balance' => 'Insufficient balance on credit.',
|
'insufficient_credit_balance' => 'Insufficient balance on credit.',
|
||||||
'one_or_more_invoices_paid' => 'One or more of these invoices have been paid',
|
'one_or_more_invoices_paid' => 'One or more of these invoices have been paid',
|
||||||
'invoice_cannot_be_refunded' => 'Invoice id :number cannot be refunded',
|
'invoice_cannot_be_refunded' => 'Invoice id :number cannot be refunded',
|
||||||
'attempted_refund_failed' => 'Attempting to refund :amount only :refundable_amount available for refund',
|
'attempted_refund_failed' => 'Attempting to refund :amount only :refundable_amount available for refund',
|
||||||
'user_not_associated_with_this_account' => 'This user is unable to be attached to this company. Perhaps they have already registered a user on another account?',
|
'user_not_associated_with_this_account' => 'This user is unable to be attached to this company. Perhaps they have already registered a user on another account?',
|
||||||
'migration_completed' => 'Migration completed',
|
'migration_completed' => 'Migration completed',
|
||||||
'migration_completed_description' => 'Your migration has completed, please review your data after logging in.',
|
'migration_completed_description' => 'Your migration has completed, please review your data after logging in.',
|
||||||
'api_404' => '404 | Nothing to see here!',
|
'api_404' => '404 | Nothing to see here!',
|
||||||
'large_account_update_parameter' => 'Cannot load a large account without a updated_at parameter',
|
'large_account_update_parameter' => 'Cannot load a large account without a updated_at parameter',
|
||||||
'no_backup_exists' => 'No backup exists for this activity',
|
'no_backup_exists' => 'No backup exists for this activity',
|
||||||
'company_user_not_found' => 'Company User record not found',
|
'company_user_not_found' => 'Company User record not found',
|
||||||
'no_credits_found' => 'No credits found.',
|
'no_credits_found' => 'No credits found.',
|
||||||
'action_unavailable' => 'The requested action :action is not available.',
|
'action_unavailable' => 'The requested action :action is not available.',
|
||||||
'no_documents_found' => 'No Documents Found',
|
'no_documents_found' => 'No Documents Found',
|
||||||
'no_group_settings_found' => 'No group settings found',
|
'no_group_settings_found' => 'No group settings found',
|
||||||
'access_denied' => 'Insufficient privileges to access/modify this resource',
|
'access_denied' => 'Insufficient privileges to access/modify this resource',
|
||||||
'invoice_cannot_be_marked_paid' => 'Invoice cannot be marked as paid',
|
'invoice_cannot_be_marked_paid' => 'Invoice cannot be marked as paid',
|
||||||
'invoice_license_or_environment' => 'Invalid license, or invalid environment :environment',
|
'invoice_license_or_environment' => 'Invalid license, or invalid environment :environment',
|
||||||
'route_not_available' => 'Route not available',
|
'route_not_available' => 'Route not available',
|
||||||
'invalid_design_object' => 'Invalid custom design object',
|
'invalid_design_object' => 'Invalid custom design object',
|
||||||
'quote_not_found' => 'Quote/s not found',
|
'quote_not_found' => 'Quote/s not found',
|
||||||
'quote_unapprovable' => 'Unable to approve this quote as it has expired.',
|
'quote_unapprovable' => 'Unable to approve this quote as it has expired.',
|
||||||
'scheduler_has_run' => 'Scheduler has run',
|
'scheduler_has_run' => 'Scheduler has run',
|
||||||
'scheduler_has_never_run' => 'Scheduler has never run',
|
'scheduler_has_never_run' => 'Scheduler has never run',
|
||||||
'self_update_not_available' => 'Self update not available on this system.',
|
'self_update_not_available' => 'Self update not available on this system.',
|
||||||
'user_detached' => 'User detached from company',
|
'user_detached' => 'User detached from company',
|
||||||
'create_webhook_failure' => 'Failed to create Webhook',
|
'create_webhook_failure' => 'Failed to create Webhook',
|
||||||
'payment_message_extended' => 'Thank you for your payment of :amount for :invoice',
|
'payment_message_extended' => 'Thank you for your payment of :amount for :invoice',
|
||||||
'online_payments_minimum_note' => 'Note: Online payments are supported only if amount is bigger than $1 or currency equivalent.',
|
'online_payments_minimum_note' => 'Note: Online payments are supported only if amount is bigger than $1 or currency equivalent.',
|
||||||
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method',
|
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method',
|
||||||
'vendor_address1' => 'Vendor Street',
|
'vendor_address1' => 'Vendor Street',
|
||||||
'vendor_address2' => 'Vendor Apt/Suite',
|
'vendor_address2' => 'Vendor Apt/Suite',
|
||||||
'partially_unapplied' => 'Partially Unapplied',
|
'partially_unapplied' => 'Partially Unapplied',
|
||||||
'select_a_gmail_user' => 'Please select a user authenticated with Gmail',
|
'select_a_gmail_user' => 'Please select a user authenticated with Gmail',
|
||||||
'list_long_press' => 'List Long Press',
|
'list_long_press' => 'List Long Press',
|
||||||
'show_actions' => 'Show Actions',
|
'show_actions' => 'Show Actions',
|
||||||
'start_multiselect' => 'Start Multiselect',
|
'start_multiselect' => 'Start Multiselect',
|
||||||
'email_sent_to_confirm_email' => 'An email has been sent to confirm the email address',
|
'email_sent_to_confirm_email' => 'An email has been sent to confirm the email address',
|
||||||
'converted_paid_to_date' => 'Converted Paid to Date',
|
'converted_paid_to_date' => 'Converted Paid to Date',
|
||||||
'converted_credit_balance' => 'Converted Credit Balance',
|
'converted_credit_balance' => 'Converted Credit Balance',
|
||||||
'converted_total' => 'Converted Total',
|
'converted_total' => 'Converted Total',
|
||||||
'reply_to_name' => 'Reply-To Name',
|
'reply_to_name' => 'Reply-To Name',
|
||||||
'payment_status_-2' => 'Partially Unapplied',
|
'payment_status_-2' => 'Partially Unapplied',
|
||||||
'color_theme' => 'Color Theme',
|
'color_theme' => 'Color Theme',
|
||||||
'start_migration' => 'Start Migration',
|
'start_migration' => 'Start Migration',
|
||||||
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
||||||
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
||||||
'hello' => 'Hello',
|
'hello' => 'Hello',
|
||||||
'group_documents' => 'Group documents',
|
'group_documents' => 'Group documents',
|
||||||
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
|
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
|
||||||
'migration_select_company_label' => 'Select companies to migrate',
|
'migration_select_company_label' => 'Select companies to migrate',
|
||||||
'force_migration' => 'Force migration',
|
'force_migration' => 'Force migration',
|
||||||
'require_password_with_social_login' => 'Require Password with Social Login',
|
'require_password_with_social_login' => 'Require Password with Social Login',
|
||||||
'stay_logged_in' => 'Stay Logged In',
|
'stay_logged_in' => 'Stay Logged In',
|
||||||
'session_about_to_expire' => 'Warning: Your session is about to expire',
|
'session_about_to_expire' => 'Warning: Your session is about to expire',
|
||||||
'count_hours' => ':count Hours',
|
'count_hours' => ':count Hours',
|
||||||
'count_day' => '1 Day',
|
'count_day' => '1 Day',
|
||||||
'count_days' => ':count Days',
|
'count_days' => ':count Days',
|
||||||
'web_session_timeout' => 'Web Session Timeout',
|
'web_session_timeout' => 'Web Session Timeout',
|
||||||
'security_settings' => 'Security Settings',
|
'security_settings' => 'Security Settings',
|
||||||
'resend_email' => 'Resend Email',
|
'resend_email' => 'Resend Email',
|
||||||
'confirm_your_email_address' => 'Please confirm your email address',
|
'confirm_your_email_address' => 'Please confirm your email address',
|
||||||
'freshbooks' => 'FreshBooks',
|
'freshbooks' => 'FreshBooks',
|
||||||
'invoice2go' => 'Invoice2go',
|
'invoice2go' => 'Invoice2go',
|
||||||
'invoicely' => 'Invoicely',
|
'invoicely' => 'Invoicely',
|
||||||
'waveaccounting' => 'Wave Accounting',
|
'waveaccounting' => 'Wave Accounting',
|
||||||
'zoho' => 'Zoho',
|
'zoho' => 'Zoho',
|
||||||
'accounting' => 'Accounting',
|
'accounting' => 'Accounting',
|
||||||
'required_files_missing' => 'Please provide all CSVs.',
|
'required_files_missing' => 'Please provide all CSVs.',
|
||||||
'migration_auth_label' => 'Let\'s continue by authenticating.',
|
'migration_auth_label' => 'Let\'s continue by authenticating.',
|
||||||
'api_secret' => 'API secret',
|
'api_secret' => 'API secret',
|
||||||
'migration_api_secret_notice' => 'You can find API_SECRET in the .env file or Invoice Ninja v5. If property is missing, leave field blank.',
|
'migration_api_secret_notice' => 'You can find API_SECRET in the .env file or Invoice Ninja v5. If property is missing, leave field blank.',
|
||||||
'billing_coupon_notice' => 'Your discount will be applied on the checkout.',
|
'billing_coupon_notice' => 'Your discount will be applied on the checkout.',
|
||||||
'use_last_email' => 'Use last email',
|
'use_last_email' => 'Use last email',
|
||||||
'activate_company' => 'Activate Company',
|
'activate_company' => 'Activate Company',
|
||||||
'activate_company_help' => 'Enable emails, recurring invoices and notifications',
|
'activate_company_help' => 'Enable emails, recurring invoices and notifications',
|
||||||
'an_error_occurred_try_again' => 'An error occurred, please try again',
|
'an_error_occurred_try_again' => 'An error occurred, please try again',
|
||||||
'please_first_set_a_password' => 'Please first set a password',
|
'please_first_set_a_password' => 'Please first set a password',
|
||||||
'changing_phone_disables_two_factor' => 'Warning: Changing your phone number will disable 2FA',
|
'changing_phone_disables_two_factor' => 'Warning: Changing your phone number will disable 2FA',
|
||||||
'help_translate' => 'Help Translate',
|
'help_translate' => 'Help Translate',
|
||||||
'please_select_a_country' => 'Please select a country',
|
'please_select_a_country' => 'Please select a country',
|
||||||
'disabled_two_factor' => 'Successfully disabled 2FA',
|
'disabled_two_factor' => 'Successfully disabled 2FA',
|
||||||
'connected_google' => 'Successfully connected account',
|
'connected_google' => 'Successfully connected account',
|
||||||
'disconnected_google' => 'Successfully disconnected account',
|
'disconnected_google' => 'Successfully disconnected account',
|
||||||
'delivered' => 'Delivered',
|
'delivered' => 'Delivered',
|
||||||
'spam' => 'Spam',
|
'spam' => 'Spam',
|
||||||
'view_docs' => 'View Docs',
|
'view_docs' => 'View Docs',
|
||||||
'enter_phone_to_enable_two_factor' => 'Please provide a mobile phone number to enable two factor authentication',
|
'enter_phone_to_enable_two_factor' => 'Please provide a mobile phone number to enable two factor authentication',
|
||||||
'send_sms' => 'Send SMS',
|
'send_sms' => 'Send SMS',
|
||||||
'sms_code' => 'SMS Code',
|
'sms_code' => 'SMS Code',
|
||||||
'connect_google' => 'Connect Google',
|
'connect_google' => 'Connect Google',
|
||||||
'disconnect_google' => 'Disconnect Google',
|
'disconnect_google' => 'Disconnect Google',
|
||||||
'disable_two_factor' => 'Disable Two Factor',
|
'disable_two_factor' => 'Disable Two Factor',
|
||||||
'invoice_task_datelog' => 'Invoice Task Datelog',
|
'invoice_task_datelog' => 'Invoice Task Datelog',
|
||||||
'invoice_task_datelog_help' => 'Add date details to the invoice line items',
|
'invoice_task_datelog_help' => 'Add date details to the invoice line items',
|
||||||
'promo_code' => 'Promo code',
|
'promo_code' => 'Promo code',
|
||||||
'recurring_invoice_issued_to' => 'Recurring invoice issued to',
|
'recurring_invoice_issued_to' => 'Recurring invoice issued to',
|
||||||
'subscription' => 'Subscription',
|
'subscription' => 'Subscription',
|
||||||
'new_subscription' => 'New Subscription',
|
'new_subscription' => 'New Subscription',
|
||||||
'deleted_subscription' => 'Successfully deleted subscription',
|
'deleted_subscription' => 'Successfully deleted subscription',
|
||||||
'removed_subscription' => 'Successfully removed subscription',
|
'removed_subscription' => 'Successfully removed subscription',
|
||||||
'restored_subscription' => 'Successfully restored subscription',
|
'restored_subscription' => 'Successfully restored subscription',
|
||||||
'search_subscription' => 'Search 1 Subscription',
|
'search_subscription' => 'Search 1 Subscription',
|
||||||
'search_subscriptions' => 'Search :count Subscriptions',
|
'search_subscriptions' => 'Search :count Subscriptions',
|
||||||
'subdomain_is_not_available' => 'Subdomain is not available',
|
'subdomain_is_not_available' => 'Subdomain is not available',
|
||||||
'connect_gmail' => 'Connect Gmail',
|
'connect_gmail' => 'Connect Gmail',
|
||||||
'disconnect_gmail' => 'Disconnect Gmail',
|
'disconnect_gmail' => 'Disconnect Gmail',
|
||||||
'connected_gmail' => 'Successfully connected Gmail',
|
'connected_gmail' => 'Successfully connected Gmail',
|
||||||
'disconnected_gmail' => 'Successfully disconnected Gmail',
|
'disconnected_gmail' => 'Successfully disconnected Gmail',
|
||||||
'update_fail_help' => 'Changes to the codebase may be blocking the update, you can run this command to discard the changes:',
|
'update_fail_help' => 'Changes to the codebase may be blocking the update, you can run this command to discard the changes:',
|
||||||
'client_id_number' => 'Client ID Number',
|
'client_id_number' => 'Client ID Number',
|
||||||
'count_minutes' => ':count Minutes',
|
'count_minutes' => ':count Minutes',
|
||||||
'password_timeout' => 'Password Timeout',
|
'password_timeout' => 'Password Timeout',
|
||||||
'shared_invoice_credit_counter' => 'Share Invoice/Credit Counter',
|
'shared_invoice_credit_counter' => 'Share Invoice/Credit Counter',
|
||||||
'activity_80' => ':user created subscription :subscription',
|
'activity_80' => ':user created subscription :subscription',
|
||||||
'activity_81' => ':user updated subscription :subscription',
|
'activity_81' => ':user updated subscription :subscription',
|
||||||
'activity_82' => ':user archived subscription :subscription',
|
'activity_82' => ':user archived subscription :subscription',
|
||||||
@ -4891,6 +4893,7 @@ $lang = array(
|
|||||||
'email_alignment' => 'Email Alignment',
|
'email_alignment' => 'Email Alignment',
|
||||||
'pdf_preview_location' => 'PDF Preview Location',
|
'pdf_preview_location' => 'PDF Preview Location',
|
||||||
'mailgun' => 'Mailgun',
|
'mailgun' => 'Mailgun',
|
||||||
|
'brevo' => 'Brevo',
|
||||||
'postmark' => 'Postmark',
|
'postmark' => 'Postmark',
|
||||||
'microsoft' => 'Microsoft',
|
'microsoft' => 'Microsoft',
|
||||||
'click_plus_to_create_record' => 'Click + to create a record',
|
'click_plus_to_create_record' => 'Click + to create a record',
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user