mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Working on manual send reminders #1234
This commit is contained in:
parent
3e4528e340
commit
d38101545f
@ -4,12 +4,20 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Auth;
|
use Auth;
|
||||||
use Eloquent;
|
use Eloquent;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Activity.
|
* Class Activity.
|
||||||
*/
|
*/
|
||||||
class Activity extends Eloquent
|
class Activity extends Eloquent
|
||||||
{
|
{
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $presenter = 'App\Ninja\Presenters\ActivityPresenter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,7 @@ use App\Events\QuoteInvitationWasEmailed;
|
|||||||
use App\Events\QuoteWasCreated;
|
use App\Events\QuoteWasCreated;
|
||||||
use App\Events\QuoteWasUpdated;
|
use App\Events\QuoteWasUpdated;
|
||||||
use App\Libraries\CurlUtils;
|
use App\Libraries\CurlUtils;
|
||||||
|
use App\Models\Activity;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Laracasts\Presenter\PresentableTrait;
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
@ -1441,6 +1442,16 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
return $statuses;
|
return $statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function emailHistory()
|
||||||
|
{
|
||||||
|
return Activity::scope()
|
||||||
|
->with(['contact'])
|
||||||
|
->whereInvoiceId($this->id)
|
||||||
|
->whereIn('activity_type_id', [ACTIVITY_TYPE_EMAIL_INVOICE, ACTIVITY_TYPE_EMAIL_QUOTE])
|
||||||
|
->orderBy('id', 'desc')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoice::creating(function ($invoice) {
|
Invoice::creating(function ($invoice) {
|
||||||
|
39
app/Ninja/Presenters/ActivityPresenter.php
Normal file
39
app/Ninja/Presenters/ActivityPresenter.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Presenters;
|
||||||
|
|
||||||
|
use Laracasts\Presenter\Presenter;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ActivityPresenter extends Presenter
|
||||||
|
{
|
||||||
|
public function createdAt()
|
||||||
|
{
|
||||||
|
return Utils::timestampToDateTimeString(strtotime($this->entity->created_at));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createdAtDate()
|
||||||
|
{
|
||||||
|
return Utils::dateToString($this->entity->created_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
if ($this->entity->is_system) {
|
||||||
|
return '<i>' . trans('texts.system') . '</i>';
|
||||||
|
} else {
|
||||||
|
return $this->entity->user->getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notes()
|
||||||
|
{
|
||||||
|
if ($this->entity->notes) {
|
||||||
|
return trans('texts.notes_' . $this->entity->notes);
|
||||||
|
} elseif (in_array($this->entity->activity_type_id, [ACTIVITY_TYPE_EMAIL_INVOICE, ACTIVITY_TYPE_EMAIL_QUOTE])) {
|
||||||
|
return trans('texts.initial_email');
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Ninja\Presenters;
|
namespace App\Ninja\Presenters;
|
||||||
|
|
||||||
use App\Libraries\Skype\InvoiceCard;
|
use App\Libraries\Skype\InvoiceCard;
|
||||||
|
use App\Models\Activity;
|
||||||
use Carbon;
|
use Carbon;
|
||||||
use DropdownButton;
|
use DropdownButton;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
@ -494,7 +494,7 @@ $LANG = array(
|
|||||||
'email_address' => 'Email address',
|
'email_address' => 'Email address',
|
||||||
'lets_go' => 'Let\'s go',
|
'lets_go' => 'Let\'s go',
|
||||||
'password_recovery' => 'Password Recovery',
|
'password_recovery' => 'Password Recovery',
|
||||||
'send_email' => 'Send email',
|
'send_email' => 'Send Email',
|
||||||
'set_password' => 'Set Password',
|
'set_password' => 'Set Password',
|
||||||
'converted' => 'Converted',
|
'converted' => 'Converted',
|
||||||
'email_approved' => 'Email me when a quote is <b>approved</b>',
|
'email_approved' => 'Email me when a quote is <b>approved</b>',
|
||||||
@ -2347,6 +2347,13 @@ $LANG = array(
|
|||||||
'inactivity_logout' => 'Due to inactivity, you have been automatically logged out.',
|
'inactivity_logout' => 'Due to inactivity, you have been automatically logged out.',
|
||||||
'mark_active' => 'Mark Active',
|
'mark_active' => 'Mark Active',
|
||||||
'send_automatically' => 'Send Automatically',
|
'send_automatically' => 'Send Automatically',
|
||||||
|
'template' => 'Template',
|
||||||
|
'initial_email' => 'Initial Email',
|
||||||
|
'invoice_not_emailed' => 'This invoice hasn\'t been emailed.',
|
||||||
|
'quote_not_emailed' => 'This quote hasn\'t been emailed.',
|
||||||
|
'days_ago' => ':count day ago|:count days ago',
|
||||||
|
'sent_by' => 'Sent by :user',
|
||||||
|
'recipients' => 'Recipients',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -799,6 +799,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@include('invoices.email')
|
||||||
|
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@ -1284,24 +1286,33 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sweetConfirm(function() {
|
$('#recipients').html(getSendToEmails());
|
||||||
model.invoice().is_public(true);
|
$('#emailModal').modal('show');
|
||||||
var accountLanguageId = parseInt({{ $account->language_id ?: '0' }});
|
|
||||||
var clientLanguageId = parseInt(model.invoice().client().language_id()) || 0;
|
|
||||||
var attachPDF = {{ $account->attachPDF() ? 'true' : 'false' }};
|
|
||||||
|
|
||||||
// if they aren't attaching the pdf no need to generate it
|
|
||||||
if ( ! attachPDF) {
|
|
||||||
submitAction('email');
|
|
||||||
// if the client's language is different then we can't use the browser version of the PDF
|
|
||||||
} else if (clientLanguageId && clientLanguageId != accountLanguageId) {
|
|
||||||
submitAction('email');
|
|
||||||
} else {
|
|
||||||
preparePdfData('email');
|
|
||||||
}
|
|
||||||
}, getSendToEmails());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onConfirmEmailClick() {
|
||||||
|
model.invoice().is_public(true);
|
||||||
|
var accountLanguageId = parseInt({{ $account->language_id ?: '0' }});
|
||||||
|
var clientLanguageId = parseInt(model.invoice().client().language_id()) || 0;
|
||||||
|
var attachPDF = {{ $account->attachPDF() ? 'true' : 'false' }};
|
||||||
|
|
||||||
|
// if they aren't attaching the pdf no need to generate it
|
||||||
|
if ( ! attachPDF) {
|
||||||
|
submitAction('email');
|
||||||
|
// if the client's language is different then we can't use the browser version of the PDF
|
||||||
|
} else if (clientLanguageId && clientLanguageId != accountLanguageId) {
|
||||||
|
submitAction('email');
|
||||||
|
} else {
|
||||||
|
preparePdfData('email');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Utils::isNinjaDev())
|
||||||
|
$(function() {
|
||||||
|
onEmailClick();
|
||||||
|
})
|
||||||
|
@endif
|
||||||
|
|
||||||
function onSaveDraftClick() {
|
function onSaveDraftClick() {
|
||||||
model.invoice().is_public(false);
|
model.invoice().is_public(false);
|
||||||
onSaveClick();
|
onSaveClick();
|
||||||
@ -1310,7 +1321,7 @@
|
|||||||
function onMarkSentClick() {
|
function onMarkSentClick() {
|
||||||
if (model.invoice().is_recurring()) {
|
if (model.invoice().is_recurring()) {
|
||||||
// warn invoice will be emailed when saving new recurring invoice
|
// warn invoice will be emailed when saving new recurring invoice
|
||||||
var text = getSendToEmails() + '\n' + "{!! trans("texts.confirm_recurring_timing") !!}";
|
var text = '\n' + getSendToEmails() + '\n\n' + "{!! trans("texts.confirm_recurring_timing") !!}";
|
||||||
var title = "{!! trans("texts.confirm_recurring_email_$entityType") !!}";
|
var title = "{!! trans("texts.confirm_recurring_email_$entityType") !!}";
|
||||||
sweetConfirm(function() {
|
sweetConfirm(function() {
|
||||||
model.invoice().is_public(true);
|
model.invoice().is_public(true);
|
||||||
|
93
resources/views/invoices/email.blade.php
Normal file
93
resources/views/invoices/email.blade.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<div class="modal fade" id="emailModal" tabindex="-1" role="dialog" aria-labelledby="emailModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" style="min-width:150px">
|
||||||
|
<div class="modal-content">
|
||||||
|
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title" id="emailModalLabel">{{ trans('texts.email_invoice') }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
{!! Former::plaintext('recipients')
|
||||||
|
->value('') !!}
|
||||||
|
|
||||||
|
{!! Former::select('template')
|
||||||
|
->options([
|
||||||
|
'initial' => trans('texts.initial_email'),
|
||||||
|
'reminder1' => trans('texts.first_reminder'),
|
||||||
|
'reminder2' => trans('texts.second_reminder'),
|
||||||
|
'reminder3' => trans('texts.third_reminder'),
|
||||||
|
]) !!}
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<div role="tabpanel">
|
||||||
|
<ul class="nav nav-tabs" role="tablist" style="border: none">
|
||||||
|
<li role="presentation" class="active">
|
||||||
|
<a href="#preview" aria-controls="preview" role="tab" data-toggle="tab">{{ trans('texts.preview') }}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#customize" aria-controls="customize" role="tab" data-toggle="tab">{{ trans('texts.customize') }}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#history" aria-controls="history" role="tab" data-toggle="tab">{{ trans('texts.history') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="preview">
|
||||||
|
<div id="emailSubject"></div>
|
||||||
|
<div id="emailBody"></div>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="customize">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="history">
|
||||||
|
<br/>
|
||||||
|
@if (count($activities = $invoice->emailHistory()))
|
||||||
|
<table class="table table-striped data-table">
|
||||||
|
<tr>
|
||||||
|
<th>{{ trans('texts.template')}}</th>
|
||||||
|
<th>{{ trans('texts.contact')}}</th>
|
||||||
|
<th>{{ trans('texts.date')}}</th>
|
||||||
|
</tr>
|
||||||
|
@foreach ($activities as $activity)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $activity->present()->notes }}</td>
|
||||||
|
<td>
|
||||||
|
<span title="{{ trans('texts.sent_by', ['user' => $activity->present()->user]) }}">
|
||||||
|
{{ $activity->contact->getDisplayName() }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span title="{{ $activity->present()->createdAt }}">
|
||||||
|
{{ $activity->present()->createdAtDate }} - {{ trans_choice('texts.days_ago', $activity->created_at->diffInDays()) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</table>
|
||||||
|
@else
|
||||||
|
<center style="font-size:16px;color:#888888;padding-top:20px;">
|
||||||
|
{{ trans("texts.{$invoice->getEntityType()}_not_emailed") }}
|
||||||
|
</center>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer" style="margin-top: 0px">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
|
||||||
|
<button type="button" class="btn btn-info" onclick="onConfirmEmailClick()">{{ trans('texts.send_email') }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
</script>
|
@ -641,13 +641,13 @@ function ContactModel(data) {
|
|||||||
self.displayName = ko.computed(function() {
|
self.displayName = ko.computed(function() {
|
||||||
var str = '';
|
var str = '';
|
||||||
if (self.first_name() || self.last_name()) {
|
if (self.first_name() || self.last_name()) {
|
||||||
str += (self.first_name() || '') + ' ' + (self.last_name() || '') + '\n';
|
str += (self.first_name() || '') + ' ' + (self.last_name() || '') + ' ';
|
||||||
}
|
}
|
||||||
if (self.email()) {
|
if (self.email()) {
|
||||||
str += self.email() + '\n';
|
str += '<' + self.email() + '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str + '<br/>';
|
||||||
});
|
});
|
||||||
|
|
||||||
self.email.display = ko.computed(function() {
|
self.email.display = ko.computed(function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user