Proposals

This commit is contained in:
Hillel Coren 2018-02-12 10:43:31 +02:00
parent 4229b8ad57
commit d280383cfa
9 changed files with 420 additions and 14 deletions

View File

@ -458,6 +458,7 @@ if (! defined('APP_NAME')) {
define('TEMPLATE_INVOICE', 'invoice'); define('TEMPLATE_INVOICE', 'invoice');
define('TEMPLATE_QUOTE', 'quote'); define('TEMPLATE_QUOTE', 'quote');
define('TEMPLATE_PROPOSAL', 'proposal');
define('TEMPLATE_PARTIAL', 'partial'); define('TEMPLATE_PARTIAL', 'partial');
define('TEMPLATE_PAYMENT', 'payment'); define('TEMPLATE_PAYMENT', 'payment');
define('TEMPLATE_REMINDER1', 'reminder1'); define('TEMPLATE_REMINDER1', 'reminder1');

View File

@ -8,6 +8,7 @@ use App\Http\Requests\UpdateProposalRequest;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Proposal; use App\Models\Proposal;
use App\Models\ProposalTemplate; use App\Models\ProposalTemplate;
use App\Ninja\Mailers\ContactMailer;
use App\Ninja\Datatables\ProposalDatatable; use App\Ninja\Datatables\ProposalDatatable;
use App\Ninja\Repositories\ProposalRepository; use App\Ninja\Repositories\ProposalRepository;
use App\Services\ProposalService; use App\Services\ProposalService;
@ -21,12 +22,15 @@ class ProposalController extends BaseController
{ {
protected $proposalRepo; protected $proposalRepo;
protected $proposalService; protected $proposalService;
protected $contactMailer;
protected $entityType = ENTITY_PROPOSAL; protected $entityType = ENTITY_PROPOSAL;
public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService) public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService, ContactMailer $contactMailer)
{ {
$this->proposalRepo = $proposalRepo; $this->proposalRepo = $proposalRepo;
$this->proposalService = $proposalService; $this->proposalService = $proposalService;
$this->contactMailer = $contactMailer;
} }
/** /**
@ -109,14 +113,18 @@ class ProposalController extends BaseController
public function update(UpdateProposalRequest $request) public function update(UpdateProposalRequest $request)
{ {
$proposal = $this->proposalService->save($request->input(), $request->entity()); $proposal = $this->proposalService->save($request->input(), $request->entity());
Session::flash('message', trans('texts.updated_proposal'));
$action = Input::get('action'); $action = Input::get('action');
if (in_array($action, ['archive', 'delete', 'restore'])) { if (in_array($action, ['archive', 'delete', 'restore'])) {
return self::bulk(); return self::bulk();
} }
if ($action == 'email') {
$this->contactMailer->sendProposal($proposal);
} else {
Session::flash('message', trans('texts.updated_proposal'));
}
return redirect()->to($proposal->getRoute()); return redirect()->to($proposal->getRoute());
} }
@ -142,8 +150,6 @@ class ProposalController extends BaseController
$mpdf = new mPDF(); $mpdf = new mPDF();
$mpdf->WriteHTML($proposal->present()->htmlDocument); $mpdf->WriteHTML($proposal->present()->htmlDocument);
$mpdf->Output();
$mpdf->Output($proposal->present()->filename, 'D'); $mpdf->Output($proposal->present()->filename, 'D');
} }
} }

View File

@ -38,6 +38,7 @@ class AccountEmailSettings extends Eloquent
public static $templates = [ public static $templates = [
TEMPLATE_INVOICE, TEMPLATE_INVOICE,
TEMPLATE_QUOTE, TEMPLATE_QUOTE,
TEMPLATE_PROPOSAL,
//TEMPLATE_PARTIAL, //TEMPLATE_PARTIAL,
TEMPLATE_PAYMENT, TEMPLATE_PAYMENT,
TEMPLATE_REMINDER1, TEMPLATE_REMINDER1,

View File

@ -5,7 +5,9 @@ namespace App\Ninja\Mailers;
use App\Events\InvoiceWasEmailed; use App\Events\InvoiceWasEmailed;
use App\Events\QuoteWasEmailed; use App\Events\QuoteWasEmailed;
use App\Models\Invitation; use App\Models\Invitation;
use App\Models\ProposalInvitation;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Proposal;
use App\Models\Payment; use App\Models\Payment;
use App\Services\TemplateService; use App\Services\TemplateService;
use App\Jobs\ConvertInvoiceToUbl; use App\Jobs\ConvertInvoiceToUbl;
@ -216,6 +218,150 @@ class ContactMailer extends Mailer
} }
} }
/**
* @param Invoice $invoice
* @param bool $reminder
* @param bool $pdfString
*
* @return bool|null|string
*/
public function sendProposal(Proposal $proposal, $template = false)
{
$proposal->load('invitations', 'invoice.client.language', 'account');
$account = $proposal->account;
$invoice = $proposal->invoice;
$client = $invoice->client;
$response = null;
if ($proposal->trashed()) {
return trans('texts.email_error_inactive_proposal');
} elseif ($client->trashed()) {
return trans('texts.email_error_inactive_client');
} elseif ($invoice->trashed()) {
return trans('texts.email_error_inactive_invoice');
}
$account->loadLocalizationSettings($client);
$emailTemplate = !empty($template['body']) ? $template['body'] : $account->getEmailTemplate(ENTITY_PROPOSAL);
$emailSubject = !empty($template['subject']) ? $template['subject'] : $account->getEmailSubject(ENTITY_PROPOSAL);
$sent = false;
$pdfString = false;
/*
if ($account->attachPDF()) {
$pdfString = $invoice->getPDFString();
}
*/
$isFirst = true;
foreach ($proposal->invitations as $invitation) {
$data = [
//'pdfString' => $pdfString,
];
$response = $this->sendProposalInvitation($invitation, $proposal, $emailTemplate, $emailSubject, $isFirst, $data);
$isFirst = false;
if ($response === true) {
$sent = true;
}
}
$account->loadLocalizationSettings();
/*
if ($sent === true) {
event(new QuoteWasEmailed($invoice, $reminder));
}
*/
return $response;
}
/**
* @param Invitation $invitation
* @param Invoice $invoice
* @param $body
* @param $subject
* @param $pdfString
* @param $documentStrings
* @param mixed $reminder
*
* @throws \Laracasts\Presenter\Exceptions\PresenterException
*
* @return bool|string
*/
private function sendProposalInvitation(
ProposalInvitation $invitation,
Proposal $proposal,
$body,
$subject,
$isFirst,
$attachments
) {
$account = $proposal->account;
$invoice = $proposal->invoice;
$client = $invoice->client;
$user = $invitation->user;
if ($user->trashed()) {
$user = $account->users()->orderBy('id')->first();
}
if (! $user->email || ! $user->registered) {
return trans('texts.email_error_user_unregistered');
} elseif (! $user->confirmed || $this->isThrottled($account)) {
return trans('texts.email_error_user_unconfirmed');
} elseif (! $invitation->contact->email) {
return trans('texts.email_error_invalid_contact_email');
} elseif ($invitation->contact->trashed()) {
return trans('texts.email_error_inactive_contact');
}
$variables = [
'account' => $account,
'client' => $client,
'invitation' => $invitation,
'amount' => $invoice->getRequestedAmount(),
];
$data = [
'body' => $this->templateService->processVariables($body, $variables),
'link' => $invitation->getLink(),
'entityType' => $invoice->getEntityType(),
'invoiceId' => $invoice->id,
'invitation' => $invitation,
'account' => $account,
'client' => $client,
'invoice' => $invoice,
'documents' => $attachments['documentStrings'],
'notes' => $reminder,
'bccEmail' => $isFirst ? $account->getBccEmail() : false,
'fromEmail' => $account->getFromEmail(),
];
/*
if ($account->attachPDF()) {
$data['pdfString'] = $attachments['pdfString'];
$data['pdfFileName'] = $invoice->getFileName();
}
*/
$subject = $this->templateService->processVariables($subject, $variables);
$fromEmail = $account->getReplyToEmail() ?: $user->email;
$view = $account->getTemplateView(ENTITY_INVOICE);
$response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data);
if ($response === true) {
return true;
} else {
return $response;
}
}
/** /**
* @param int $length * @param int $length
* *

View File

@ -21,6 +21,11 @@ class AddSubscriptionFormat extends Migration
$table->boolean('ubl_email_attachment')->default(false); $table->boolean('ubl_email_attachment')->default(false);
}); });
Schema::table('account_email_settings', function ($table) {
$table->string('email_subject_proposal')->nullable();
$table->text('email_template_proposal')->nullable();
});
Schema::create('proposal_categories', function ($table) { Schema::create('proposal_categories', function ($table) {
$table->increments('id'); $table->increments('id');
$table->unsignedInteger('account_id'); $table->unsignedInteger('account_id');
@ -153,6 +158,11 @@ class AddSubscriptionFormat extends Migration
$table->dropColumn('ubl_email_attachment'); $table->dropColumn('ubl_email_attachment');
}); });
Schema::table('account_email_settings', function ($table) {
$table->dropColumn('email_subject_proposal');
$table->dropColumn('email_template_proposal');
});
Schema::dropIfExists('proposals'); Schema::dropIfExists('proposals');
Schema::dropIfExists('proposal_templates'); Schema::dropIfExists('proposal_templates');
Schema::dropIfExists('proposal_snippets'); Schema::dropIfExists('proposal_snippets');

File diff suppressed because one or more lines are too long

View File

@ -1022,6 +1022,7 @@ $LANG = array(
'email_error_inactive_client' => 'Emails can not be sent to inactive clients', 'email_error_inactive_client' => 'Emails can not be sent to inactive clients',
'email_error_inactive_contact' => 'Emails can not be sent to inactive contacts', 'email_error_inactive_contact' => 'Emails can not be sent to inactive contacts',
'email_error_inactive_invoice' => 'Emails can not be sent to inactive invoices', 'email_error_inactive_invoice' => 'Emails can not be sent to inactive invoices',
'email_error_inactive_proposal' => 'Emails can not be sent to inactive proposals',
'email_error_user_unregistered' => 'Please register your account to send emails', 'email_error_user_unregistered' => 'Please register your account to send emails',
'email_error_user_unconfirmed' => 'Please confirm your account to send emails', 'email_error_user_unconfirmed' => 'Please confirm your account to send emails',
'email_error_invalid_contact_email' => 'Invalid contact email', 'email_error_invalid_contact_email' => 'Invalid contact email',
@ -2728,6 +2729,9 @@ $LANG = array(
'proposal_not_found' => 'The requested proposal is not available', 'proposal_not_found' => 'The requested proposal is not available',
'create_proposal_category' => 'Create category', 'create_proposal_category' => 'Create category',
'clone_proposal_template' => 'Clone Template', 'clone_proposal_template' => 'Clone Template',
'proposal_email' => 'Proposal Email',
'proposal_subject' => 'New proposal :number from :account',
'proposal_message' => 'To view your proposal for :amount, click the link below.',
); );

View File

@ -48,11 +48,13 @@
<ul class="nav nav-tabs" role="tablist" style="border: none"> <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" 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="#quote" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.quote_email') }}</a></li>
<li role="presentation"><a href="#proposal" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.proposal_email') }}</a></li>
<li role="presentation"><a href="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li> <li role="presentation"><a href="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
@include('accounts.template', ['field' => 'invoice', 'active' => true]) @include('accounts.template', ['field' => 'invoice', 'active' => true])
@include('accounts.template', ['field' => 'quote']) @include('accounts.template', ['field' => 'quote'])
@include('accounts.template', ['field' => 'proposal'])
@include('accounts.template', ['field' => 'payment']) @include('accounts.template', ['field' => 'payment'])
</div> </div>
</div> </div>

View File

@ -13,6 +13,7 @@
{!! Former::open($url) {!! Former::open($url)
->method($method) ->method($method)
->onsubmit('return onFormSubmit(event)') ->onsubmit('return onFormSubmit(event)')
->id('mainForm')
->addClass('warn-on-exit') ->addClass('warn-on-exit')
->rules([ ->rules([
'invoice_id' => 'required', 'invoice_id' => 'required',
@ -24,6 +25,7 @@
<span style="display:none"> <span style="display:none">
{!! Former::text('public_id') !!} {!! Former::text('public_id') !!}
{!! Former::text('action') !!}
{!! Former::text('html') !!} {!! Former::text('html') !!}
{!! Former::text('css') !!} {!! Former::text('css') !!}
</span> </span>
@ -66,6 +68,10 @@
->appendIcon(Icon::create('floppy-disk')) !!} ->appendIcon(Icon::create('floppy-disk')) !!}
@if ($proposal) @if ($proposal)
{!! Button::info(trans('texts.email'))
->withAttributes(['onclick' => 'onEmailClick()'])
->appendIcon(Icon::create('send')) !!}
{!! DropdownButton::normal(trans('texts.more_actions')) {!! DropdownButton::normal(trans('texts.more_actions'))
->withContents($proposal->present()->moreActions()) !!} ->withContents($proposal->present()->moreActions()) !!}
@endif @endif
@ -94,6 +100,10 @@
function onDownloadClick() { function onDownloadClick() {
location.href = "{{ url("/proposals/{$proposal->public_id}/download") }}"; location.href = "{{ url("/proposals/{$proposal->public_id}/download") }}";
} }
function onEmailClick() {
$('#action').val('email');
$('#mainForm').submit();
}
@endif @endif
function loadTemplate() { function loadTemplate() {