mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 14:04:40 -04:00
Proposals
This commit is contained in:
parent
4229b8ad57
commit
d280383cfa
@ -458,6 +458,7 @@ if (! defined('APP_NAME')) {
|
||||
|
||||
define('TEMPLATE_INVOICE', 'invoice');
|
||||
define('TEMPLATE_QUOTE', 'quote');
|
||||
define('TEMPLATE_PROPOSAL', 'proposal');
|
||||
define('TEMPLATE_PARTIAL', 'partial');
|
||||
define('TEMPLATE_PAYMENT', 'payment');
|
||||
define('TEMPLATE_REMINDER1', 'reminder1');
|
||||
|
@ -8,6 +8,7 @@ use App\Http\Requests\UpdateProposalRequest;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Proposal;
|
||||
use App\Models\ProposalTemplate;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use App\Ninja\Datatables\ProposalDatatable;
|
||||
use App\Ninja\Repositories\ProposalRepository;
|
||||
use App\Services\ProposalService;
|
||||
@ -21,12 +22,15 @@ class ProposalController extends BaseController
|
||||
{
|
||||
protected $proposalRepo;
|
||||
protected $proposalService;
|
||||
protected $contactMailer;
|
||||
|
||||
protected $entityType = ENTITY_PROPOSAL;
|
||||
|
||||
public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService)
|
||||
public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService, ContactMailer $contactMailer)
|
||||
{
|
||||
$this->proposalRepo = $proposalRepo;
|
||||
$this->proposalService = $proposalService;
|
||||
$this->contactMailer = $contactMailer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,14 +113,18 @@ class ProposalController extends BaseController
|
||||
public function update(UpdateProposalRequest $request)
|
||||
{
|
||||
$proposal = $this->proposalService->save($request->input(), $request->entity());
|
||||
|
||||
Session::flash('message', trans('texts.updated_proposal'));
|
||||
|
||||
$action = Input::get('action');
|
||||
|
||||
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||
return self::bulk();
|
||||
}
|
||||
|
||||
if ($action == 'email') {
|
||||
$this->contactMailer->sendProposal($proposal);
|
||||
} else {
|
||||
Session::flash('message', trans('texts.updated_proposal'));
|
||||
}
|
||||
|
||||
return redirect()->to($proposal->getRoute());
|
||||
}
|
||||
|
||||
@ -142,8 +150,6 @@ class ProposalController extends BaseController
|
||||
|
||||
$mpdf = new mPDF();
|
||||
$mpdf->WriteHTML($proposal->present()->htmlDocument);
|
||||
|
||||
$mpdf->Output();
|
||||
$mpdf->Output($proposal->present()->filename, 'D');
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ class AccountEmailSettings extends Eloquent
|
||||
public static $templates = [
|
||||
TEMPLATE_INVOICE,
|
||||
TEMPLATE_QUOTE,
|
||||
TEMPLATE_PROPOSAL,
|
||||
//TEMPLATE_PARTIAL,
|
||||
TEMPLATE_PAYMENT,
|
||||
TEMPLATE_REMINDER1,
|
||||
|
@ -5,7 +5,9 @@ namespace App\Ninja\Mailers;
|
||||
use App\Events\InvoiceWasEmailed;
|
||||
use App\Events\QuoteWasEmailed;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\ProposalInvitation;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Proposal;
|
||||
use App\Models\Payment;
|
||||
use App\Services\TemplateService;
|
||||
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
|
||||
*
|
||||
|
@ -21,6 +21,11 @@ class AddSubscriptionFormat extends Migration
|
||||
$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) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('account_id');
|
||||
@ -153,6 +158,11 @@ class AddSubscriptionFormat extends Migration
|
||||
$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('proposal_templates');
|
||||
Schema::dropIfExists('proposal_snippets');
|
||||
|
File diff suppressed because one or more lines are too long
@ -1022,6 +1022,7 @@ $LANG = array(
|
||||
'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_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_unconfirmed' => 'Please confirm your account to send emails',
|
||||
'email_error_invalid_contact_email' => 'Invalid contact email',
|
||||
@ -2728,6 +2729,9 @@ $LANG = array(
|
||||
'proposal_not_found' => 'The requested proposal is not available',
|
||||
'create_proposal_category' => 'Create category',
|
||||
'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.',
|
||||
|
||||
);
|
||||
|
||||
|
@ -48,11 +48,13 @@
|
||||
<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="#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>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
@include('accounts.template', ['field' => 'invoice', 'active' => true])
|
||||
@include('accounts.template', ['field' => 'quote'])
|
||||
@include('accounts.template', ['field' => 'proposal'])
|
||||
@include('accounts.template', ['field' => 'payment'])
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,6 +13,7 @@
|
||||
{!! Former::open($url)
|
||||
->method($method)
|
||||
->onsubmit('return onFormSubmit(event)')
|
||||
->id('mainForm')
|
||||
->addClass('warn-on-exit')
|
||||
->rules([
|
||||
'invoice_id' => 'required',
|
||||
@ -24,6 +25,7 @@
|
||||
|
||||
<span style="display:none">
|
||||
{!! Former::text('public_id') !!}
|
||||
{!! Former::text('action') !!}
|
||||
{!! Former::text('html') !!}
|
||||
{!! Former::text('css') !!}
|
||||
</span>
|
||||
@ -66,6 +68,10 @@
|
||||
->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
|
||||
@if ($proposal)
|
||||
{!! Button::info(trans('texts.email'))
|
||||
->withAttributes(['onclick' => 'onEmailClick()'])
|
||||
->appendIcon(Icon::create('send')) !!}
|
||||
|
||||
{!! DropdownButton::normal(trans('texts.more_actions'))
|
||||
->withContents($proposal->present()->moreActions()) !!}
|
||||
@endif
|
||||
@ -94,6 +100,10 @@
|
||||
function onDownloadClick() {
|
||||
location.href = "{{ url("/proposals/{$proposal->public_id}/download") }}";
|
||||
}
|
||||
function onEmailClick() {
|
||||
$('#action').val('email');
|
||||
$('#mainForm').submit();
|
||||
}
|
||||
@endif
|
||||
|
||||
function loadTemplate() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user