mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 12:12:48 +01:00
Added auto-reminder emails
This commit is contained in:
parent
4741fad4be
commit
98cabd4138
@ -19,3 +19,5 @@ MAIL_USERNAME
|
||||
MAIL_FROM_ADDRESS
|
||||
MAIL_FROM_NAME
|
||||
MAIL_PASSWORD
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
@ -111,7 +111,7 @@ class CheckData extends Command {
|
||||
->first(['invoices.amount', 'invoices.is_recurring', 'invoices.is_quote', 'invoices.deleted_at', 'invoices.id', 'invoices.is_deleted']);
|
||||
|
||||
// Check if this invoice was once set as recurring invoice
|
||||
if (!$invoice->is_recurring && DB::table('invoices')
|
||||
if ($invoice && !$invoice->is_recurring && DB::table('invoices')
|
||||
->where('recurring_invoice_id', '=', $activity->invoice_id)
|
||||
->first(['invoices.id'])) {
|
||||
$invoice->is_recurring = 1;
|
||||
|
@ -37,12 +37,14 @@ class SendRecurringInvoices extends Command
|
||||
$this->info(count($invoices).' recurring invoice(s) found');
|
||||
|
||||
foreach ($invoices as $recurInvoice) {
|
||||
$this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO'));
|
||||
|
||||
$this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO'));
|
||||
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
|
||||
|
||||
if ($invoice && !$invoice->isPaid()) {
|
||||
$recurInvoice->account->loadLocalizationSettings($invoice->client);
|
||||
if ($invoice->account->pdf_email_attachment) {
|
||||
$invoice->updateCachedPDF();
|
||||
}
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
}
|
||||
|
65
app/Console/Commands/SendReminders.php
Normal file
65
app/Console/Commands/SendReminders.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php namespace App\Console\Commands;
|
||||
|
||||
use DB;
|
||||
use DateTime;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use App\Models\Account;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Ninja\Repositories\accountRepository;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
|
||||
class SendReminders extends Command
|
||||
{
|
||||
protected $name = 'ninja:send-reminders';
|
||||
protected $description = 'Send reminder emails';
|
||||
protected $mailer;
|
||||
protected $invoiceRepo;
|
||||
protected $accountRepo;
|
||||
|
||||
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->accountRepo = $accountRepo;
|
||||
}
|
||||
|
||||
public function fire()
|
||||
{
|
||||
$this->info(date('Y-m-d').' Running SendReminders...');
|
||||
$today = new DateTime();
|
||||
|
||||
$accounts = $this->accountRepo->findWithReminders();
|
||||
$this->info(count($accounts).' accounts found');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account);
|
||||
$this->info($account->name . ': ' . count($invoices).' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $invoice->getReminder()) {
|
||||
$this->mailer->sendInvoice($invoice, $reminder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
}
|
||||
|
||||
protected function getArguments()
|
||||
{
|
||||
return array(
|
||||
//array('example', InputArgument::REQUIRED, 'An example argument.'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
//array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null),
|
||||
);
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel {
|
||||
'App\Console\Commands\ResetData',
|
||||
'App\Console\Commands\CheckData',
|
||||
'App\Console\Commands\SendRenewalInvoices',
|
||||
'App\Console\Commands\SendReminders',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -249,10 +249,14 @@ class AccountController extends BaseController
|
||||
if ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
$data['customDesign'] = ($account->custom_design && !$design) ? $account->custom_design : $design;
|
||||
}
|
||||
} else if ($subSection == ACCOUNT_EMAIL_TEMPLATES) {
|
||||
$data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE);
|
||||
$data['quoteEmail'] = $account->getEmailTemplate(ENTITY_QUOTE);
|
||||
$data['paymentEmail'] = $account->getEmailTemplate(ENTITY_PAYMENT);
|
||||
} else if ($subSection == ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
$data['templates'] = [];
|
||||
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) {
|
||||
$data['templates'][$type] = [
|
||||
'subject' => $account->getEmailSubject($type),
|
||||
'template' => $account->getEmailTemplate($type),
|
||||
];
|
||||
}
|
||||
$data['emailFooter'] = $account->getEmailFooter();
|
||||
$data['title'] = trans('texts.email_templates');
|
||||
} else if ($subSection == ACCOUNT_USER_MANAGEMENT) {
|
||||
@ -289,7 +293,7 @@ class AccountController extends BaseController
|
||||
return AccountController::saveInvoiceDesign();
|
||||
} elseif ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
return AccountController::saveCustomizeDesign();
|
||||
} elseif ($subSection == ACCOUNT_EMAIL_TEMPLATES) {
|
||||
} elseif ($subSection == ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
return AccountController::saveEmailTemplates();
|
||||
}
|
||||
} elseif ($section == ACCOUNT_PRODUCTS) {
|
||||
@ -315,16 +319,28 @@ class AccountController extends BaseController
|
||||
if (Auth::user()->account->isPro()) {
|
||||
$account = Auth::user()->account;
|
||||
|
||||
$account->email_template_invoice = Input::get('email_template_invoice', $account->getEmailTemplate(ENTITY_INVOICE));
|
||||
$account->email_template_quote = Input::get('email_template_quote', $account->getEmailTemplate(ENTITY_QUOTE));
|
||||
$account->email_template_payment = Input::get('email_template_payment', $account->getEmailTemplate(ENTITY_PAYMENT));
|
||||
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) {
|
||||
$subjectField = "email_subject_{$type}";
|
||||
$account->$subjectField = Input::get($subjectField, $account->getEmailSubject($type));
|
||||
|
||||
$bodyField = "email_template_{$type}";
|
||||
$account->$bodyField = Input::get($bodyField, $account->getEmailTemplate($type));
|
||||
}
|
||||
|
||||
foreach ([REMINDER1, REMINDER2, REMINDER3] as $type) {
|
||||
$enableField = "enable_{$type}";
|
||||
$account->$enableField = Input::get($enableField) ? true : false;
|
||||
|
||||
$numDaysField = "num_days_{$type}";
|
||||
$account->$numDaysField = Input::get($numDaysField);
|
||||
}
|
||||
|
||||
$account->save();
|
||||
|
||||
Session::flash('message', trans('texts.updated_settings'));
|
||||
}
|
||||
|
||||
return Redirect::to('company/advanced_settings/email_templates');
|
||||
return Redirect::to('company/advanced_settings/templates_and_reminders');
|
||||
}
|
||||
|
||||
private function saveProducts()
|
||||
@ -346,7 +362,7 @@ class AccountController extends BaseController
|
||||
$rules = [];
|
||||
$user = Auth::user();
|
||||
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
|
||||
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('substr(string, start)')), 0, MAX_SUBDOMAIN_LENGTH));
|
||||
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH));
|
||||
if (!$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) {
|
||||
$subdomain = null;
|
||||
}
|
||||
@ -361,7 +377,6 @@ class AccountController extends BaseController
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
} else {
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$account->subdomain = $subdomain;
|
||||
$account->iframe_url = $iframeURL;
|
||||
|
@ -239,7 +239,7 @@ class AccountGatewayController extends BaseController
|
||||
foreach ($fields as $field => $details) {
|
||||
$value = trim(Input::get($gateway->id.'_'.$field));
|
||||
// if the new value is masked use the original value
|
||||
if ($value && $value === str_repeat('*', strlen($value))) {
|
||||
if ($oldConfig && $value && $value === str_repeat('*', strlen($value))) {
|
||||
$value = $oldConfig->$field;
|
||||
}
|
||||
if (!$value && ($field == 'testMode' || $field == 'developerMode')) {
|
||||
|
@ -62,12 +62,10 @@ class InvoiceApiController extends Controller
|
||||
// check if the invoice number is set and unique
|
||||
if (!isset($data['invoice_number']) && !isset($data['id'])) {
|
||||
$data['invoice_number'] = Auth::user()->account->getNextInvoiceNumber();
|
||||
} else if (isset($data['invoice_number'])) {
|
||||
} else if (isset($data['invoice_number'])) {
|
||||
$invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first();
|
||||
if ($invoice) {
|
||||
$error = trans('validation.unique', ['attribute' => 'texts.invoice_number']);
|
||||
} else {
|
||||
$data['id'] = $invoice->public_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use App;
|
||||
use Auth;
|
||||
use Session;
|
||||
use Utils;
|
||||
@ -174,11 +175,16 @@ class InvoiceController extends BaseController
|
||||
|
||||
public function view($invitationKey)
|
||||
{
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->firstOrFail();
|
||||
$invoice = $invitation->invoice;
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
|
||||
|
||||
if (!$invitation) {
|
||||
App::abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
return View::make('invoices.deleted');
|
||||
App::abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
$invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country');
|
||||
@ -186,7 +192,7 @@ class InvoiceController extends BaseController
|
||||
$account = $client->account;
|
||||
|
||||
if (!$client || $client->is_deleted) {
|
||||
return View::make('invoices.deleted');
|
||||
App::abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
if ($account->subdomain) {
|
||||
@ -198,7 +204,7 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
if (!Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||
if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||
Activity::viewInvoice($invitation);
|
||||
Event::fire(new InvoiceViewed($invoice));
|
||||
}
|
||||
@ -261,6 +267,7 @@ class InvoiceController extends BaseController
|
||||
'contact' => $contact,
|
||||
'paymentTypes' => $paymentTypes,
|
||||
'paymentURL' => $paymentURL,
|
||||
'phantomjs' => Input::has('phantomjs'),
|
||||
);
|
||||
|
||||
return View::make('invoices.view', $data);
|
||||
@ -521,7 +528,7 @@ class InvoiceController extends BaseController
|
||||
|
||||
$pdfUpload = Input::get('pdfupload');
|
||||
if (!empty($pdfUpload) && strpos($pdfUpload, 'data:application/pdf;base64,') === 0) {
|
||||
$this->storePDF(Input::get('pdfupload'), $invoice);
|
||||
$invoice->updateCachedPDF(Input::get('pdfupload'));
|
||||
}
|
||||
|
||||
if ($action == 'clone') {
|
||||
@ -684,10 +691,4 @@ class InvoiceController extends BaseController
|
||||
|
||||
return View::make('invoices.history', $data);
|
||||
}
|
||||
|
||||
private function storePDF($encodedString, $invoice)
|
||||
{
|
||||
$encodedString = str_replace('data:application/pdf;base64,', '', $encodedString);
|
||||
file_put_contents($invoice->getPDFPath(), base64_decode($encodedString));
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +225,6 @@ Route::get('/forgot_password', function() {
|
||||
return Redirect::to(NINJA_APP_URL.'/forgot', 301);
|
||||
});
|
||||
|
||||
|
||||
if (!defined('CONTACT_EMAIL')) {
|
||||
define('CONTACT_EMAIL', Config::get('mail.from.address'));
|
||||
define('CONTACT_NAME', Config::get('mail.from.name'));
|
||||
@ -260,7 +259,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('ACCOUNT_CHART_BUILDER', 'chart_builder');
|
||||
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
|
||||
define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations');
|
||||
define('ACCOUNT_EMAIL_TEMPLATES', 'email_templates');
|
||||
define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders');
|
||||
define('ACCOUNT_TOKEN_MANAGEMENT', 'token_management');
|
||||
define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design');
|
||||
|
||||
@ -391,6 +390,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('ZAPIER_URL', 'https://zapier.com/developer/invite/11276/85cf0ee4beae8e802c6c579eb4e351f1/');
|
||||
define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/');
|
||||
define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html');
|
||||
define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/single/browser/v1/');
|
||||
|
||||
define('COUNT_FREE_DESIGNS', 4);
|
||||
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
|
||||
@ -425,6 +425,10 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('PAYMENT_TYPE_TOKEN', 'PAYMENT_TYPE_TOKEN');
|
||||
define('PAYMENT_TYPE_ANY', 'PAYMENT_TYPE_ANY');
|
||||
|
||||
define('REMINDER1', 'reminder1');
|
||||
define('REMINDER2', 'reminder2');
|
||||
define('REMINDER3', 'reminder3');
|
||||
|
||||
$creditCards = [
|
||||
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
|
||||
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
|
||||
@ -487,4 +491,5 @@ if (Auth::check() && Auth::user()->id === 1)
|
||||
{
|
||||
Auth::loginUsingId(1);
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
|
@ -776,4 +776,14 @@ class Utils
|
||||
}
|
||||
return $domain;
|
||||
}
|
||||
|
||||
public static function replaceSubdomain($domain, $subdomain) {
|
||||
$parsedUrl = parse_url($domain);
|
||||
$host = explode('.', $parsedUrl['host']);
|
||||
if (count($host) > 0) {
|
||||
$oldSubdomain = $host[0];
|
||||
$domain = str_replace("://{$oldSubdomain}.", "://{$subdomain}.", $domain);
|
||||
}
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,13 @@ class Account extends Eloquent
|
||||
return $this->hasMany('App\Models\User');
|
||||
}
|
||||
|
||||
public function getPrimaryUser()
|
||||
{
|
||||
return $this->hasMany('App\Models\User')
|
||||
->whereRaw('public_id = 0 OR public_id IS NULL')
|
||||
->first();
|
||||
}
|
||||
|
||||
public function clients()
|
||||
{
|
||||
return $this->hasMany('App\Models\Client');
|
||||
@ -405,15 +412,35 @@ class Account extends Eloquent
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmailSubject($entityType)
|
||||
{
|
||||
$field = "email_subject_{$entityType}";
|
||||
$value = $this->$field;
|
||||
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (strpos($entityType, 'reminder') !== false) {
|
||||
$entityType = 'reminder';
|
||||
}
|
||||
|
||||
return trans("texts.{$entityType}_subject", ['invoice' => '$invoice', 'account' => '$account']);
|
||||
}
|
||||
|
||||
public function getEmailTemplate($entityType, $message = false)
|
||||
{
|
||||
$field = "email_template_$entityType";
|
||||
$field = "email_template_{$entityType}";
|
||||
$template = $this->$field;
|
||||
|
||||
if ($template) {
|
||||
return $template;
|
||||
}
|
||||
|
||||
if (strpos($entityType, 'reminder') >= 0) {
|
||||
$entityType = ENTITY_INVOICE;
|
||||
}
|
||||
|
||||
$template = "\$client,<p/>\r\n\r\n" .
|
||||
trans("texts.{$entityType}_message", ['amount' => '$amount']) . "<p/>\r\n\r\n" .
|
||||
"<a href=\"\$link\">\$link</a><p/>\r\n\r\n";
|
||||
@ -431,7 +458,7 @@ class Account extends Eloquent
|
||||
// Add line breaks if HTML isn't already being used
|
||||
return strip_tags($this->email_footer) == $this->email_footer ? nl2br($this->email_footer) : $this->email_footer;
|
||||
} else {
|
||||
return "<p>" . trans('texts.email_signature') . "<br>\$account</p>";
|
||||
return "<p>" . trans('texts.email_signature') . "\n<br>\$account</p>";
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,6 +476,20 @@ class Account extends Eloquent
|
||||
{
|
||||
return $this->token_billing_type_id == TOKEN_BILLING_OPT_OUT;
|
||||
}
|
||||
|
||||
public function getSiteUrl()
|
||||
{
|
||||
$url = SITE_URL;
|
||||
$iframe_url = $this->iframe_url;
|
||||
|
||||
if ($iframe_url) {
|
||||
return "{$iframe_url}/?";
|
||||
} else if ($this->subdomain) {
|
||||
$url = Utils::replaceSubdomain($url, $this->subdomain);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
Account::updated(function ($account) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Models;
|
||||
|
||||
use Utils;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Invitation extends EntityModel
|
||||
@ -37,12 +38,9 @@ class Invitation extends EntityModel
|
||||
$iframe_url = $this->account->iframe_url;
|
||||
|
||||
if ($iframe_url) {
|
||||
return "{$iframe_url}?{$this->invitation_key}";
|
||||
return "{$iframe_url}/?{$this->invitation_key}";
|
||||
} else if ($this->account->subdomain) {
|
||||
$parsedUrl = parse_url($url);
|
||||
$host = explode('.', $parsedUrl['host']);
|
||||
$subdomain = $host[0];
|
||||
$url = str_replace("://{$subdomain}.", "://{$this->account->subdomain}.", $url);
|
||||
$url = Utils::replaceSubdomain($url, $this->subdomain);
|
||||
}
|
||||
|
||||
return "{$url}/view/{$this->invitation_key}";
|
||||
|
@ -262,6 +262,57 @@ class Invoice extends EntityModel
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getReminder() {
|
||||
for ($i=1; $i<=3; $i++) {
|
||||
$field = "enable_reminder{$i}";
|
||||
if (!$this->account->$field) {
|
||||
continue;
|
||||
}
|
||||
$field = "num_days_reminder{$i}";
|
||||
$date = date('Y-m-d', strtotime("- {$this->account->$field} days"));
|
||||
|
||||
if ($this->due_date == $date) {
|
||||
return "reminder{$i}";
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateCachedPDF($encodedString = false)
|
||||
{
|
||||
if (!$encodedString) {
|
||||
$invitation = $this->invitations[0];
|
||||
$key = $invitation->getLink();
|
||||
|
||||
$curl = curl_init();
|
||||
$jsonEncodedData = json_encode([
|
||||
'targetUrl' => SITE_URL . "/view/{$key}/?phantomjs=true",
|
||||
'requestType' => 'raw',
|
||||
]);
|
||||
|
||||
$opts = [
|
||||
CURLOPT_URL => PHANTOMJS_CLOUD . env('PHANTOMJS_CLOUD_KEY'),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $jsonEncodedData,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: '.strlen($jsonEncodedData)],
|
||||
];
|
||||
|
||||
curl_setopt_array($curl, $opts);
|
||||
$encodedString = strip_tags(curl_exec($curl));
|
||||
curl_close($curl);
|
||||
|
||||
if (!$encodedString) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$encodedString = str_replace('data:application/pdf;base64,', '', $encodedString);
|
||||
file_put_contents($this->getPDFPath(), base64_decode($encodedString));
|
||||
}
|
||||
}
|
||||
|
||||
Invoice::creating(function ($invoice) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
use Utils;
|
||||
use Event;
|
||||
use URL;
|
||||
use Auth;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
@ -12,7 +13,7 @@ use App\Events\InvoiceSent;
|
||||
|
||||
class ContactMailer extends Mailer
|
||||
{
|
||||
public function sendInvoice(Invoice $invoice)
|
||||
public function sendInvoice(Invoice $invoice, $reminder = false)
|
||||
{
|
||||
$invoice->load('invitations', 'client.language', 'account');
|
||||
$entityType = $invoice->getEntityType();
|
||||
@ -23,65 +24,69 @@ class ContactMailer extends Mailer
|
||||
$account->loadLocalizationSettings($client);
|
||||
|
||||
$view = 'invoice';
|
||||
$subject = trans("texts.{$entityType}_subject", ['invoice' => $invoice->invoice_number, 'account' => $invoice->account->getDisplayName()]);
|
||||
$accountName = $invoice->account->getDisplayName();
|
||||
$emailTemplate = $invoice->account->getEmailTemplate($entityType);
|
||||
$invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $client->getCurrencyId());
|
||||
$emailTemplate = $invoice->account->getEmailTemplate($reminder ?: $entityType);
|
||||
$emailSubject = $invoice->account->getEmailSubject($reminder ?: $entityType);
|
||||
|
||||
$this->initClosure($invoice);
|
||||
$response = false;
|
||||
$sent = false;
|
||||
|
||||
foreach ($invoice->invitations as $invitation) {
|
||||
if (!$invitation->user || !$invitation->user->email || $invitation->user->trashed()) {
|
||||
return false;
|
||||
if (Auth::check()) {
|
||||
$user = Auth::user();
|
||||
} else {
|
||||
$user = $invitation->user;
|
||||
if ($invitation->user->trashed()) {
|
||||
$user = $account->getPrimaryUser();
|
||||
}
|
||||
}
|
||||
if (!$invitation->contact || !$invitation->contact->email || $invitation->contact->trashed()) {
|
||||
return false;
|
||||
|
||||
if (!$user->email || !$user->confirmed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$invitation->contact->email
|
||||
|| $invitation->contact->trashed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$invitation->sent_date = \Carbon::now()->toDateTimeString();
|
||||
$invitation->save();
|
||||
|
||||
$variables = [
|
||||
'$footer' => $invoice->account->getEmailFooter(),
|
||||
'$link' => $invitation->getLink(),
|
||||
'$client' => $client->getDisplayName(),
|
||||
'$account' => $accountName,
|
||||
'$contact' => $invitation->contact->getDisplayName(),
|
||||
'$amount' => $invoiceAmount,
|
||||
'$advancedRawInvoice->' => '$'
|
||||
'account' => $account,
|
||||
'client' => $client,
|
||||
'invitation' => $invitation,
|
||||
'amount' => $invoice->getRequestedAmount()
|
||||
];
|
||||
|
||||
// Add variables for available payment types
|
||||
foreach (Gateway::getPaymentTypeLinks() as $type) {
|
||||
$variables["\${$type}_link"] = URL::to("/payment/{$invitation->invitation_key}/{$type}");
|
||||
}
|
||||
|
||||
$data['body'] = str_replace(array_keys($variables), array_values($variables), $emailTemplate);
|
||||
$data['body'] = preg_replace_callback('/\{\{\$?(.*)\}\}/', $this->advancedTemplateHandler, $data['body']);
|
||||
$data['body'] = $this->processVariables($emailTemplate, $variables);
|
||||
$data['link'] = $invitation->getLink();
|
||||
$data['entityType'] = $entityType;
|
||||
$data['invoice_id'] = $invoice->id;
|
||||
|
||||
$fromEmail = $invitation->user->email;
|
||||
$subject = $this->processVariables($emailSubject, $variables);
|
||||
$fromEmail = $user->email;
|
||||
$response = $this->sendTo($invitation->contact->email, $fromEmail, $accountName, $subject, $view, $data);
|
||||
|
||||
if ($response !== true) {
|
||||
return $response;
|
||||
if ($response === true) {
|
||||
$sent = true;
|
||||
Activity::emailInvoice($invitation);
|
||||
}
|
||||
|
||||
Activity::emailInvoice($invitation);
|
||||
}
|
||||
|
||||
if (!$invoice->isSent()) {
|
||||
$invoice->invoice_status_id = INVOICE_STATUS_SENT;
|
||||
$invoice->save();
|
||||
if ($sent === true) {
|
||||
if (!$invoice->isSent()) {
|
||||
$invoice->invoice_status_id = INVOICE_STATUS_SENT;
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
$account->loadLocalizationSettings();
|
||||
Event::fire(new InvoiceSent($invoice));
|
||||
}
|
||||
|
||||
$account->loadLocalizationSettings();
|
||||
|
||||
Event::fire(new InvoiceSent($invoice));
|
||||
|
||||
return $response;
|
||||
return $response ?: trans('texts.email_error');
|
||||
}
|
||||
|
||||
public function sendPaymentConfirmation(Payment $payment)
|
||||
@ -93,30 +98,38 @@ class ContactMailer extends Mailer
|
||||
|
||||
$invoice = $payment->invoice;
|
||||
$view = 'payment_confirmation';
|
||||
$subject = trans('texts.payment_subject', ['invoice' => $invoice->invoice_number]);
|
||||
$accountName = $account->getDisplayName();
|
||||
$emailTemplate = $account->getEmailTemplate(ENTITY_PAYMENT);
|
||||
$emailSubject = $invoice->account->getEmailSubject(ENTITY_PAYMENT);
|
||||
|
||||
$variables = [
|
||||
'$footer' => $account->getEmailFooter(),
|
||||
'$client' => $client->getDisplayName(),
|
||||
'$account' => $accountName,
|
||||
'$amount' => Utils::formatMoney($payment->amount, $client->getCurrencyId())
|
||||
];
|
||||
|
||||
$this->initClosure($invoice);
|
||||
|
||||
if ($payment->invitation) {
|
||||
$user = $payment->invitation->user;
|
||||
$contact = $payment->contact;
|
||||
$variables['$link'] = $payment->invitation->getLink();
|
||||
$invitation = $payment->invitation;
|
||||
} else {
|
||||
$user = $payment->user;
|
||||
$contact = $client->contacts[0];
|
||||
$variables['$link'] = $payment->invoice->invitations[0]->getLink();
|
||||
$invitation = $payment->invoice->invitations[0];
|
||||
}
|
||||
|
||||
$data = ['body' => str_replace(array_keys($variables), array_values($variables), $emailTemplate)];
|
||||
|
||||
//$data['invoice_id'] = $payment->invoice->id;
|
||||
$variables = [
|
||||
'account' => $account,
|
||||
'client' => $client,
|
||||
'invitation' => $invitation,
|
||||
'amount' => $payment->amount
|
||||
];
|
||||
|
||||
$data = [
|
||||
'body' => $this->processVariables($emailTemplate, $variables)
|
||||
];
|
||||
$subject = $this->processVariables($emailSubject, $variables);
|
||||
|
||||
$data['invoice_id'] = $payment->invoice->id;
|
||||
if ($invoice->account->pdf_email_attachment) {
|
||||
$invoice->updateCachedPDF();
|
||||
}
|
||||
|
||||
if ($user->email && $contact->email) {
|
||||
$this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data);
|
||||
@ -148,6 +161,31 @@ class ContactMailer extends Mailer
|
||||
$this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
|
||||
}
|
||||
|
||||
private function processVariables($template, $data)
|
||||
{
|
||||
$variables = [
|
||||
'$footer' => $data['account']->getEmailFooter(),
|
||||
'$link' => $data['invitation']->getLink(),
|
||||
'$client' => $data['client']->getDisplayName(),
|
||||
'$account' => $data['account']->getDisplayName(),
|
||||
'$contact' => $data['invitation']->contact->getDisplayName(),
|
||||
'$amount' => Utils::formatMoney($data['amount'], $data['client']->getCurrencyId()),
|
||||
'$invoice' => $data['invitation']->invoice->invoice_number,
|
||||
'$quote' => $data['invitation']->invoice->invoice_number,
|
||||
'$advancedRawInvoice->' => '$'
|
||||
];
|
||||
|
||||
// Add variables for available payment types
|
||||
foreach (Gateway::getPaymentTypeLinks() as $type) {
|
||||
$variables["\${$type}_link"] = URL::to("/payment/{$data['invitation']->invitation_key}/{$type}");
|
||||
}
|
||||
|
||||
$str = str_replace(array_keys($variables), array_values($variables), $template);
|
||||
$str = preg_replace_callback('/\{\{\$?(.*)\}\}/', $this->advancedTemplateHandler, $str);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
private function initClosure($object)
|
||||
{
|
||||
$this->advancedTemplateHandler = function($match) use ($object) {
|
||||
|
@ -21,25 +21,25 @@ class Mailer
|
||||
$replyEmail = $fromEmail;
|
||||
$fromEmail = CONTACT_EMAIL;
|
||||
|
||||
$message->to($toEmail)
|
||||
->from($fromEmail, $fromName)
|
||||
->replyTo($replyEmail, $fromName)
|
||||
->subject($subject);
|
||||
|
||||
if (isset($data['invoice_id'])) {
|
||||
$invoice = Invoice::with('account')->where('id', '=', $data['invoice_id'])->get()->first();
|
||||
if($invoice->account->pdf_email_attachment && file_exists($invoice->getPDFPath())) {
|
||||
$invoice = Invoice::with('account')->where('id', '=', $data['invoice_id'])->first();
|
||||
if ($invoice->account->pdf_email_attachment && file_exists($invoice->getPDFPath())) {
|
||||
$message->attach(
|
||||
$invoice->getPDFPath(),
|
||||
array('as' => $invoice->getFileName(), 'mime' => 'application/pdf')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$message->to($toEmail)
|
||||
->from($fromEmail, $fromName)
|
||||
->replyTo($replyEmail, $fromName)
|
||||
->subject($subject);
|
||||
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (Exception $exception) {
|
||||
Utils::logError('Email Error: ' . $exception->getMessage());
|
||||
if (isset($_ENV['POSTMARK_API_TOKEN'])) {
|
||||
$response = $exception->getResponse()->getBody()->getContents();
|
||||
$response = json_decode($response);
|
||||
|
@ -392,4 +392,9 @@ class AccountRepository
|
||||
$userAccount->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function findWithReminders()
|
||||
{
|
||||
return Account::whereRaw('enable_reminder1 = 1 OR enable_reminder2 = 1 OR enable_reminder3 = 1')->get();
|
||||
}
|
||||
}
|
||||
|
@ -658,4 +658,25 @@ class InvoiceRepository
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
public function findNeedingReminding($account)
|
||||
{
|
||||
$dates = [];
|
||||
for ($i=1; $i<=3; $i++) {
|
||||
$field = "enable_reminder{$i}";
|
||||
if (!$account->$field) {
|
||||
continue;
|
||||
}
|
||||
$field = "num_days_reminder{$i}";
|
||||
$dates[] = "due_date = '" . date('Y-m-d', strtotime("- {$account->$field} days")) . "'";
|
||||
}
|
||||
$sql = implode(' OR ', $dates);
|
||||
|
||||
$invoices = Invoice::whereAccountId($account->id)
|
||||
->where('balance', '>', 0)
|
||||
->whereRaw($sql)
|
||||
->get();
|
||||
|
||||
return $invoices;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddReminderEmails extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->string('email_subject_invoice')->nullable();
|
||||
$table->string('email_subject_quote')->nullable();
|
||||
$table->string('email_subject_payment')->nullable();
|
||||
|
||||
$table->string('email_subject_reminder1')->nullable();
|
||||
$table->string('email_subject_reminder2')->nullable();
|
||||
$table->string('email_subject_reminder3')->nullable();
|
||||
|
||||
$table->text('email_template_reminder1')->nullable();
|
||||
$table->text('email_template_reminder2')->nullable();
|
||||
$table->text('email_template_reminder3')->nullable();
|
||||
|
||||
$table->boolean('enable_reminder1')->default(false);
|
||||
$table->boolean('enable_reminder2')->default(false);
|
||||
$table->boolean('enable_reminder3')->default(false);
|
||||
|
||||
$table->smallInteger('num_days_reminder1')->default(7);
|
||||
$table->smallInteger('num_days_reminder2')->default(14);
|
||||
$table->smallInteger('num_days_reminder3')->default(30);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->dropColumn('email_subject_invoice');
|
||||
$table->dropColumn('email_subject_quote');
|
||||
$table->dropColumn('email_subject_payment');
|
||||
|
||||
$table->dropColumn('email_subject_reminder1');
|
||||
$table->dropColumn('email_subject_reminder2');
|
||||
$table->dropColumn('email_subject_reminder3');
|
||||
|
||||
$table->dropColumn('email_template_reminder1');
|
||||
$table->dropColumn('email_template_reminder2');
|
||||
$table->dropColumn('email_template_reminder3');
|
||||
|
||||
$table->dropColumn('enable_reminder1');
|
||||
$table->dropColumn('enable_reminder2');
|
||||
$table->dropColumn('enable_reminder3');
|
||||
|
||||
$table->dropColumn('num_days_reminder1');
|
||||
$table->dropColumn('num_days_reminder2');
|
||||
$table->dropColumn('num_days_reminder3');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -14,13 +14,8 @@ class DatabaseSeeder extends Seeder {
|
||||
Eloquent::unguard();
|
||||
|
||||
$this->call('ConstantsSeeder');
|
||||
$this->command->info('Seeded the constants');
|
||||
|
||||
$this->call('CountriesSeeder');
|
||||
$this->command->info('Seeded the countries');
|
||||
|
||||
$this->call('PaymentLibrariesSeeder');
|
||||
$this->command->info('Seeded the Payment Libraries');
|
||||
}
|
||||
|
||||
}
|
@ -31707,7 +31707,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
|
||||
var match = matches[i];
|
||||
field = match.substring(2, match.indexOf('Value'));
|
||||
field = toSnakeCase(field);
|
||||
var value = getDescendantProp(invoice, field) || ' ';
|
||||
var value = getDescendantProp(invoice, field) || ' ';
|
||||
value = doubleDollarSign(value);
|
||||
|
||||
if (field.toLowerCase().indexOf('date') >= 0 && value != ' ') {
|
||||
@ -31827,7 +31827,7 @@ NINJA.invoiceLines = function(invoice) {
|
||||
row.push({style:["quantity", rowStyle], text:qty || ' '});
|
||||
}
|
||||
if (showItemTaxes) {
|
||||
row.push({style:["tax", rowStyle], text:tax ? tax.toString() + '%' : ' '});
|
||||
row.push({style:["tax", rowStyle], text:tax ? (tax.toString() + '%') : ' '});
|
||||
}
|
||||
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
|
||||
|
||||
|
@ -159,7 +159,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
|
||||
var match = matches[i];
|
||||
field = match.substring(2, match.indexOf('Value'));
|
||||
field = toSnakeCase(field);
|
||||
var value = getDescendantProp(invoice, field) || ' ';
|
||||
var value = getDescendantProp(invoice, field) || ' ';
|
||||
value = doubleDollarSign(value);
|
||||
|
||||
if (field.toLowerCase().indexOf('date') >= 0 && value != ' ') {
|
||||
@ -279,7 +279,7 @@ NINJA.invoiceLines = function(invoice) {
|
||||
row.push({style:["quantity", rowStyle], text:qty || ' '});
|
||||
}
|
||||
if (showItemTaxes) {
|
||||
row.push({style:["tax", rowStyle], text:(tax ? tax.toString() + '%') : ' '});
|
||||
row.push({style:["tax", rowStyle], text:tax ? (tax.toString() + '%') : ' '});
|
||||
}
|
||||
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
|
||||
|
||||
|
@ -776,6 +776,18 @@
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
);
|
@ -775,6 +775,18 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
);
|
||||
|
@ -389,7 +389,7 @@ return array(
|
||||
'deleted_quotes' => 'Successfully deleted :count quotes',
|
||||
'converted_to_invoice' => 'Successfully converted quote to invoice',
|
||||
|
||||
'quote_subject' => 'New quote from :account',
|
||||
'quote_subject' => 'New quote $quote from :account',
|
||||
'quote_message' => 'To view your quote for :amount, click the link below.',
|
||||
'quote_link_message' => 'To view your client quote click the link below:',
|
||||
'notification_quote_sent_subject' => 'Quote :invoice was sent to :client',
|
||||
@ -424,7 +424,7 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
@ -775,6 +775,18 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
@ -753,6 +753,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
|
@ -775,6 +775,18 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
);
|
@ -767,6 +767,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
|
@ -768,6 +768,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
|
@ -416,7 +416,7 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
@ -770,6 +770,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
);
|
||||
|
@ -424,7 +424,7 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
@ -777,6 +777,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
|
@ -424,7 +424,7 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
@ -775,6 +775,18 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
);
|
@ -770,6 +770,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
);
|
||||
|
@ -417,7 +417,7 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
@ -770,6 +770,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
);
|
||||
|
@ -773,6 +773,17 @@ return array(
|
||||
'military_time' => '24 Hour Time',
|
||||
'last_sent' => 'Last Sent',
|
||||
|
||||
'reminder_emails' => 'Reminder Emails',
|
||||
'templates_and_reminders' => 'Templates & Reminders',
|
||||
'subject' => 'Subject',
|
||||
'body' => 'Body',
|
||||
'first_reminder' => 'First Reminder',
|
||||
'second_reminder' => 'Second Reminder',
|
||||
'third_reminder' => 'Third Reminder',
|
||||
'num_days_reminder' => 'Days after due date',
|
||||
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
|
||||
'reset' => 'Reset',
|
||||
'invoice_not_found' => 'The requested invoice is not available',
|
||||
|
||||
|
||||
|
||||
|
@ -21,49 +21,107 @@
|
||||
{!! Former::populateField('email_template_payment', $paymentEmail) !!}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.invoice_email') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_invoice')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="invoice_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.quote_email') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_quote')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="quote_preview"></div>
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.email_templates') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
|
||||
<div role="tabpanel">
|
||||
<ul class="nav nav-tabs" role="tablist" style="border: none">
|
||||
<li role="presentation" class="active"><a href="#invoice" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.invoice_email') }}</a></li>
|
||||
<li role="presentation"><a href="#quote" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.quote_email') }}</a></li>
|
||||
<li role="presentation"><a href="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="invoice">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_invoice')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="invoice_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="quote">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_quote')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="quote_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="payment">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_payment')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="payment_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.payment_email') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_payment')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="payment_preview"></div>
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.reminder_emails') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
|
||||
<div role="tabpanel">
|
||||
<ul class="nav nav-tabs" role="tablist" style="border: none">
|
||||
<li role="presentation" class="active"><a href="#invoice" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.invoice_email') }}</a></li>
|
||||
<li role="presentation"><a href="#quote" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.quote_email') }}</a></li>
|
||||
<li role="presentation"><a href="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="invoice">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_invoice')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="invoice_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="quote">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_quote')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="quote_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="payment">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_payment')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="payment_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@if (Auth::user()->isPro())
|
||||
<center>
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<ul class="nav nav-tabs nav nav-justified">
|
||||
{!! HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/invoice_settings', 'invoice_settings') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/email_templates', 'email_templates') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/templates_and_reminders', 'templates_and_reminders') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/charts_and_reports', 'charts_and_reports') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/user_management', 'users_and_tokens') !!}
|
||||
</ul>
|
||||
|
32
resources/views/accounts/template.blade.php
Normal file
32
resources/views/accounts/template.blade.php
Normal file
@ -0,0 +1,32 @@
|
||||
<div role="tabpanel" class="tab-pane {{ isset($active) && $active ? 'active' : '' }}" id="{{ $field }}">
|
||||
<div class="panel-body">
|
||||
@if (isset($isReminder) && $isReminder)
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::checkbox('enable_' . $field)->text(trans('texts.enable'))->label('') !!}
|
||||
{!! Former::input('num_days_' . $field)->label(trans('texts.num_days_reminder')) !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('email_subject_' . $field)->label(trans('texts.subject')) !!}
|
||||
<div class="pull-right"><a href="#" onclick="return resetText('{{ 'subject' }}', '{{ $field }}')">{{ trans("texts.reset") }}</a></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p> <p/>
|
||||
<div id="{{ $field }}_subject_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_' . $field)->label(trans('texts.body')) !!}
|
||||
<div class="pull-right"><a href="#" onclick="return resetText('{{ 'template' }}', '{{ $field }}')">{{ trans("texts.reset") }}</a></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p> <p/>
|
||||
<div id="{{ $field }}_template_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
178
resources/views/accounts/templates_and_reminders.blade.php
Normal file
178
resources/views/accounts/templates_and_reminders.blade.php
Normal file
@ -0,0 +1,178 @@
|
||||
@extends('accounts.nav')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<style type="text/css">
|
||||
textarea {
|
||||
min-height: 150px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
@include('accounts.nav_advanced')
|
||||
|
||||
{!! Former::vertical_open()->addClass('col-md-10 col-md-offset-1 warn-on-exit') !!}
|
||||
{!! Former::populate($account) !!}
|
||||
|
||||
@foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type)
|
||||
@foreach (['subject', 'template'] as $field)
|
||||
{!! Former::populateField("email_{$field}_{$type}", $templates[$type][$field]) !!}
|
||||
@endforeach
|
||||
@endforeach
|
||||
|
||||
{!! Former::populateField("enable_reminder1", intval($account->enable_reminder1)) !!}
|
||||
{!! Former::populateField("enable_reminder2", intval($account->enable_reminder2)) !!}
|
||||
{!! Former::populateField("enable_reminder3", intval($account->enable_reminder3)) !!}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.email_templates') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div role="tabpanel">
|
||||
<ul class="nav nav-tabs" role="tablist" style="border: none">
|
||||
<li role="presentation" class="active"><a href="#invoice" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.invoice_email') }}</a></li>
|
||||
<li role="presentation"><a href="#quote" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.quote_email') }}</a></li>
|
||||
<li role="presentation"><a href="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
@include('accounts.template', ['field' => 'invoice', 'active' => true])
|
||||
@include('accounts.template', ['field' => 'quote'])
|
||||
@include('accounts.template', ['field' => 'payment'])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.reminder_emails') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div role="tabpanel">
|
||||
<ul class="nav nav-tabs" role="tablist" style="border: none">
|
||||
<li role="presentation" class="active"><a href="#reminder1" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.first_reminder') }}</a></li>
|
||||
<li role="presentation"><a href="#reminder2" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.second_reminder') }}</a></li>
|
||||
<li role="presentation"><a href="#reminder3" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.third_reminder') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
@include('accounts.template', ['field' => 'reminder1', 'isReminder' => true, 'active' => true])
|
||||
@include('accounts.template', ['field' => 'reminder2', 'isReminder' => true])
|
||||
@include('accounts.template', ['field' => 'reminder3', 'isReminder' => true])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@if (Auth::user()->isPro())
|
||||
<center>
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
</center>
|
||||
@else
|
||||
<script>
|
||||
$(function() {
|
||||
$('form.warn-on-exit input').prop('disabled', true);
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var entityTypes = ['invoice', 'quote', 'payment', 'reminder1', 'reminder2', 'reminder3'];
|
||||
var stringTypes = ['subject', 'template'];
|
||||
var templates = {!! json_encode($templates) !!};
|
||||
|
||||
function refreshPreview() {
|
||||
for (var i=0; i<entityTypes.length; i++) {
|
||||
var entityType = entityTypes[i];
|
||||
for (var j=0; j<stringTypes.length; j++) {
|
||||
var stringType = stringTypes[j];
|
||||
var idName = '#email_' + stringType + '_' + entityType;
|
||||
var value = $(idName).val();
|
||||
var previewName = '#' + entityType + '_' + stringType + '_preview';
|
||||
$(previewName).html(processVariables(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
for (var i=0; i<entityTypes.length; i++) {
|
||||
var entityType = entityTypes[i];
|
||||
for (var j=0; j<stringTypes.length; j++) {
|
||||
var stringType = stringTypes[j];
|
||||
var idName = '#email_' + stringType + '_' + entityType;
|
||||
$(idName).keyup(refreshPreview);
|
||||
}
|
||||
}
|
||||
refreshPreview();
|
||||
});
|
||||
|
||||
function processVariables(str) {
|
||||
if (!str) {
|
||||
return '';
|
||||
}
|
||||
|
||||
keys = [
|
||||
'footer',
|
||||
'account',
|
||||
'client',
|
||||
'amount',
|
||||
'link',
|
||||
'contact',
|
||||
'invoice',
|
||||
'quote'
|
||||
];
|
||||
|
||||
vals = [
|
||||
{!! json_encode($emailFooter) !!},
|
||||
"{{ Auth::user()->account->getDisplayName() }}",
|
||||
"Client Name",
|
||||
formatMoney(100),
|
||||
"{{ Auth::user()->account->getSiteUrl() . '...' }}",
|
||||
"Contact Name",
|
||||
"0001",
|
||||
"0001"
|
||||
];
|
||||
|
||||
// Add any available payment method links
|
||||
@foreach (\App\Models\Gateway::getPaymentTypeLinks() as $type)
|
||||
{!! "keys.push('" . $type.'_link' . "');" !!}
|
||||
{!! "vals.push('" . URL::to("/payment/xxxxxx/{$type}") . "');" !!}
|
||||
@endforeach
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var regExp = new RegExp('\\$'+keys[i], 'g');
|
||||
str = str.replace(regExp, vals[i]);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function resetText(section, field) {
|
||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||
var fieldName = 'email_' + section + '_' + field;
|
||||
var value = templates[field][section];
|
||||
$('#' + fieldName).val(value);
|
||||
refreshPreview();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
@ -250,7 +250,7 @@
|
||||
|
||||
window.setTimeout(function() {
|
||||
$(".alert-hide").fadeOut(500);
|
||||
}, 2000);
|
||||
}, 3000);
|
||||
|
||||
$('#search').blur(function(){
|
||||
$('#search').css('width', '{{ Utils::isEnglish() ? 150 : 110 }}px');
|
||||
|
@ -106,7 +106,7 @@
|
||||
<div class="col-lg-8 col-sm-8">
|
||||
<div style="padding-top:10px">
|
||||
<a href="#" data-bind="click: $root.clickLastSentDate">{{ Utils::dateToString($invoice->last_sent_date) }}</a> -
|
||||
{!! link_to('/invoices/'.$lastSent->public_id, trans('texts.view_invoice'), ['id' => 'lastInvoiceSent', 'target' => '_blank']) !!}
|
||||
{!! link_to('/invoices/'.$lastSent->public_id, trans('texts.view_invoice'), ['id' => 'lastInvoiceSent']) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -783,14 +783,18 @@
|
||||
}
|
||||
|
||||
function resetTerms() {
|
||||
model.invoice().terms(model.invoice().default_terms());
|
||||
refreshPDF();
|
||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||
model.invoice().terms(model.invoice().default_terms());
|
||||
refreshPDF();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function resetFooter() {
|
||||
model.invoice().invoice_footer(model.invoice().default_footer());
|
||||
refreshPDF();
|
||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||
model.invoice().invoice_footer(model.invoice().default_footer());
|
||||
refreshPDF();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -806,7 +810,7 @@
|
||||
|
||||
function onEmailClick() {
|
||||
if (!NINJA.isRegistered) {
|
||||
alert("{{ trans('texts.registration_required') }}");
|
||||
alert("{!! trans('texts.registration_required') !!}");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -817,11 +821,6 @@
|
||||
|
||||
function onSaveClick() {
|
||||
if (model.invoice().is_recurring()) {
|
||||
if (!NINJA.isRegistered) {
|
||||
alert("{{ trans('texts.registration_required') }}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm("{!! trans("texts.confirm_recurring_email_$entityType") !!}" + '\n\n' + getSendToEmails() + '\n' + "{!! trans("texts.confirm_recurring_timing") !!}")) {
|
||||
submitAction('');
|
||||
}
|
||||
@ -851,9 +850,9 @@
|
||||
|
||||
doc = generatePDF(invoice, design, true);
|
||||
doc.getDataUrl( function(pdfString){
|
||||
$('#pdfupload').val(pdfString);
|
||||
submitAction(action);
|
||||
});
|
||||
$('#pdfupload').val(pdfString);
|
||||
submitAction(action);
|
||||
});
|
||||
}
|
||||
|
||||
function submitAction(value) {
|
||||
|
@ -88,7 +88,7 @@
|
||||
var needsRefresh = false;
|
||||
|
||||
function refreshPDF(force) {
|
||||
getPDFString(refreshPDFCB, force);
|
||||
return getPDFString(refreshPDFCB, force);
|
||||
}
|
||||
|
||||
function refreshPDFCB(string) {
|
||||
|
@ -50,11 +50,19 @@
|
||||
invoice.contact = {!! $contact->toJson() !!};
|
||||
|
||||
function getPDFString(cb) {
|
||||
generatePDF(invoice, invoice.invoice_design.javascript, true, cb);
|
||||
return generatePDF(invoice, invoice.invoice_design.javascript, true, cb);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
refreshPDF();
|
||||
@if (Input::has('phantomjs'))
|
||||
doc = getPDFString();
|
||||
doc.getDataUrl(function(pdfString) {
|
||||
document.write(pdfString);
|
||||
document.close();
|
||||
});
|
||||
@else
|
||||
refreshPDF();
|
||||
@endif
|
||||
});
|
||||
|
||||
function onDownloadClick() {
|
||||
@ -63,7 +71,6 @@
|
||||
doc.save(fileName + '-' + invoice.invoice_number + '.pdf');
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@include('invoices.pdf', ['account' => $invoice->client->account])
|
||||
|
@ -133,13 +133,12 @@
|
||||
})
|
||||
|
||||
window.onDatatableReady = function() {
|
||||
$(':checkbox').unbind('click').click(function() {
|
||||
$(':checkbox').click(function() {
|
||||
setBulkActionsEnabled();
|
||||
});
|
||||
|
||||
$('tbody tr').unbind('click').click(function(event) {
|
||||
$('tbody tr').unbind('click').click(function(event) {
|
||||
if (event.target.type !== 'checkbox' && event.target.type !== 'button' && event.target.tagName.toLowerCase() !== 'a') {
|
||||
console.log('click');
|
||||
$checkbox = $(this).closest('tr').find(':checkbox:not(:disabled)');
|
||||
var checked = $checkbox.prop('checked');
|
||||
$checkbox.prop('checked', !checked);
|
||||
|
@ -182,7 +182,7 @@
|
||||
}
|
||||
@if ($task && !$task->is_running)
|
||||
if (!timeLog.isStartValid() || !timeLog.isEndValid()) {
|
||||
alert("{{ trans('texts.task_errors') }}");
|
||||
alert("{!! trans('texts.task_errors') !!}");
|
||||
showTimeDetails();
|
||||
return;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ $I->see('Invoice Fields');
|
||||
$I->amOnPage('/company/advanced_settings/invoice_design');
|
||||
$I->see('Invoice Design');
|
||||
|
||||
$I->amOnPage('/company/advanced_settings/email_templates');
|
||||
$I->amOnPage('/company/advanced_settings/templates_and_reminders');
|
||||
$I->see('Invoice Email');
|
||||
|
||||
$I->amOnPage('/company/advanced_settings/charts_and_reports');
|
||||
|
@ -26,7 +26,7 @@ class OnlinePaymentCest
|
||||
$I->wantTo('create a gateway');
|
||||
$I->amOnPage('/company/payments');
|
||||
|
||||
if (strpos($I->grabFromCurrentUrl(), 'create') > 0) {
|
||||
if (strpos($I->grabFromCurrentUrl(), 'create') !== false) {
|
||||
$I->fillField(['name' =>'23_apiKey'], Fixtures::get('gateway_key'));
|
||||
$I->selectOption('#token_billing_type_id', 4);
|
||||
$I->click('Save');
|
||||
@ -86,6 +86,7 @@ class OnlinePaymentCest
|
||||
$I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey);
|
||||
$I->checkOption('#auto_bill');
|
||||
$I->executeJS('preparePdfData(\'email\')');
|
||||
$I->wait(2);
|
||||
$I->see("$0.00");
|
||||
|
||||
}
|
||||
|
@ -170,16 +170,16 @@ class SettingsCest
|
||||
public function updateEmailTemplates(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update email templates');
|
||||
$I->amOnPage('/company/advanced_settings/email_templates');
|
||||
$I->amOnPage('/company/advanced_settings/templates_and_reminders');
|
||||
|
||||
$string = $this->faker->text(100);
|
||||
|
||||
$I->fillField(['name' => 'email_template_payment'], $string);
|
||||
$I->fillField(['name' => 'email_template_invoice'], $string);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
$I->seeRecord('accounts', array('email_template_payment' => $string));
|
||||
$I->seeRecord('accounts', array('email_template_invoice' => $string));
|
||||
}
|
||||
|
||||
public function runReport(FunctionalTester $I)
|
||||
|
Loading…
Reference in New Issue
Block a user