1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 13:12:50 +01:00
invoiceninja/app/Http/Controllers/ClientPortalController.php

1015 lines
39 KiB
PHP
Raw Normal View History

2015-10-13 17:44:01 +02:00
<?php namespace App\Http\Controllers;
use Auth;
use View;
2015-10-13 17:44:01 +02:00
use DB;
use URL;
2015-10-13 17:44:01 +02:00
use Input;
use Utils;
use Request;
2016-03-23 23:40:42 +01:00
use Response;
use Session;
2015-10-13 17:44:01 +02:00
use Datatable;
2016-04-29 23:50:21 +02:00
use Validator;
use Cache;
2016-04-30 23:54:56 +02:00
use Redirect;
use App\Models\Gateway;
2015-10-13 17:44:01 +02:00
use App\Models\Invitation;
2016-03-23 23:40:42 +01:00
use App\Models\Document;
2016-05-15 22:27:56 +02:00
use App\Models\PaymentMethod;
2016-05-24 23:02:28 +02:00
use App\Models\Contact;
2015-10-13 17:44:01 +02:00
use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\PaymentRepository;
2015-10-28 20:22:07 +01:00
use App\Ninja\Repositories\ActivityRepository;
2016-03-24 16:58:30 +01:00
use App\Ninja\Repositories\DocumentRepository;
use App\Events\InvoiceInvitationWasViewed;
use App\Events\QuoteInvitationWasViewed;
use App\Services\PaymentService;
use Barracuda\ArchiveStream\ZipArchive;
2015-10-13 17:44:01 +02:00
class ClientPortalController extends BaseController
2015-10-13 17:44:01 +02:00
{
private $invoiceRepo;
private $paymentRepo;
2016-03-23 23:40:42 +01:00
private $documentRepo;
2015-10-13 17:44:01 +02:00
2016-03-24 16:58:30 +01:00
public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo, DocumentRepository $documentRepo, PaymentService $paymentService)
2015-10-13 17:44:01 +02:00
{
$this->invoiceRepo = $invoiceRepo;
$this->paymentRepo = $paymentRepo;
2015-10-28 20:22:07 +01:00
$this->activityRepo = $activityRepo;
2016-03-24 16:58:30 +01:00
$this->documentRepo = $documentRepo;
$this->paymentService = $paymentService;
}
public function view($invitationKey)
{
if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
return $this->returnError();
}
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $invoice->account;
if (!$account->checkSubdomain(Request::server('HTTP_HOST'))) {
return response()->view('error', [
'error' => trans('texts.invoice_not_found'),
'hideHeader' => true,
'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(),
]);
}
if (!Input::has('phantomjs') && !Input::has('silent') && !Session::has($invitationKey)
2016-03-14 19:23:20 +01:00
&& (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
if ($invoice->is_quote) {
event(new QuoteInvitationWasViewed($invoice, $invitation));
} else {
event(new InvoiceInvitationWasViewed($invoice, $invitation));
}
}
Session::put($invitationKey, true); // track this invitation has been seen
2016-05-24 23:02:28 +02:00
Session::put('contact_key', $invitation->contact->contact_key);// track current contact
$account->loadLocalizationSettings($client);
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
$invoice->features = [
'customize_invoice_design' => $account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN),
'remove_created_by' => $account->hasFeature(FEATURE_REMOVE_CREATED_BY),
'invoice_settings' => $account->hasFeature(FEATURE_INVOICE_SETTINGS),
];
$invoice->invoice_fonts = $account->getFontsData();
if ($invoice->invoice_design_id == CUSTOM_DESIGN) {
$invoice->invoice_design->javascript = $account->custom_design;
} else {
$invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake;
}
$contact = $invitation->contact;
$contact->setVisible([
'first_name',
'last_name',
'email',
'phone',
]);
2016-05-07 04:33:03 +02:00
$data = array();
$paymentTypes = $this->getPaymentTypes($client, $invitation);
$paymentURL = '';
2016-04-29 23:50:21 +02:00
if (count($paymentTypes) == 1) {
$paymentURL = $paymentTypes[0]['url'];
if (!$account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) {
$paymentURL = URL::to($paymentURL);
}
}
2016-05-07 04:33:03 +02:00
if ($braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)){
if($braintreeGateway->getPayPalEnabled()) {
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
}
$showApprove = $invoice->quote_invoice_id ? false : true;
if ($invoice->due_date) {
$showApprove = time() < strtotime($invoice->due_date);
}
if ($invoice->invoice_status_id >= INVOICE_STATUS_APPROVED) {
$showApprove = false;
}
// Checkout.com requires first getting a payment token
$checkoutComToken = false;
$checkoutComKey = false;
2016-03-09 15:58:21 +01:00
$checkoutComDebug = false;
if ($accountGateway = $account->getGatewayConfig(GATEWAY_CHECKOUT_COM)) {
2016-03-09 15:58:21 +01:00
$checkoutComDebug = $accountGateway->getConfigField('testMode');
if ($checkoutComToken = $this->paymentService->getCheckoutComToken($invitation)) {
$checkoutComKey = $accountGateway->getConfigField('publicApiKey');
$invitation->transaction_reference = $checkoutComToken;
$invitation->save();
}
}
2016-05-07 04:33:03 +02:00
$data += array(
'account' => $account,
'showApprove' => $showApprove,
'showBreadcrumbs' => false,
'clientFontUrl' => $account->getFontsUrl(),
'invoice' => $invoice->hidePrivateFields(),
'invitation' => $invitation,
'invoiceLabels' => $account->getInvoiceLabels(),
'contact' => $contact,
'paymentTypes' => $paymentTypes,
'paymentURL' => $paymentURL,
'checkoutComToken' => $checkoutComToken,
'checkoutComKey' => $checkoutComKey,
2016-03-09 15:58:21 +01:00
'checkoutComDebug' => $checkoutComDebug,
'phantomjs' => Input::has('phantomjs'),
);
if($account->hasFeature(FEATURE_DOCUMENTS) && $this->canCreateZip()){
$zipDocs = $this->getInvoiceZipDocuments($invoice, $size);
if(count($zipDocs) > 1){
$data['documentsZipURL'] = URL::to("client/documents/{$invitation->invitation_key}");
$data['documentsZipSize'] = $size;
}
}
return View::make('invoices.view', $data);
}
2016-05-24 23:02:28 +02:00
public function contactIndex($contactKey) {
if (!$contact = Contact::where('contact_key', '=', $contactKey)->first()) {
return $this->returnError();
}
$client = $contact->client;
Session::put('contact_key', $contactKey);// track current contact
return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/invoices/');
}
private function getPaymentTypes($client, $invitation)
{
$paymentTypes = [];
$account = $client->account;
2016-04-29 23:50:21 +02:00
$paymentMethods = $this->paymentService->getClientPaymentMethods($client);
if ($paymentMethods) {
foreach ($paymentMethods as $paymentMethod) {
2016-05-11 00:46:32 +02:00
if ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH || $paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFIED) {
$code = htmlentities(str_replace(' ', '', strtolower($paymentMethod->payment_type->name)));
$html = '';
2016-04-29 23:50:21 +02:00
2016-05-11 00:46:32 +02:00
if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
2016-05-25 21:04:58 +02:00
if ($paymentMethod->bank_name) {
$html = '<div>' . htmlentities($paymentMethod->bank_name) . '</div>';
2016-05-15 22:27:56 +02:00
} else {
$html = '<img height="22" src="'.URL::to('/images/credit_cards/ach.png').'" style="float:left" alt="'.trans("texts.direct_debit").'">';
2016-05-11 00:46:32 +02:00
}
} elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
2016-05-07 04:33:03 +02:00
$html = '<img height="22" src="'.URL::to('/images/credit_cards/paypal.png').'" alt="'.trans("texts.card_".$code).'">';
2016-04-29 23:50:21 +02:00
} else {
$html = '<img height="22" src="'.URL::to('/images/credit_cards/'.$code.'.png').'" alt="'.trans("texts.card_".$code).'">';
}
2016-05-11 00:46:32 +02:00
$url = URL::to("/payment/{$invitation->invitation_key}/token/".$paymentMethod->public_id);
2016-05-07 04:33:03 +02:00
2016-05-11 00:46:32 +02:00
if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
$html .= '&nbsp;&nbsp;<span>'.$paymentMethod->email.'</span>';
2016-05-07 04:33:03 +02:00
$url .= '#braintree_paypal';
2016-05-11 00:46:32 +02:00
} elseif ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH) {
$html .= '<div class="pull-right" style="text-align:right">'.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod->expiration, false)->format('m/y'))).'<br>';
$html .= '&bull;&bull;&bull;'.$paymentMethod->last4.'</div>';
2016-04-29 23:50:21 +02:00
} else {
$html .= '<div style="text-align:right">';
2016-05-11 00:46:32 +02:00
$html .= '&bull;&bull;&bull;'.$paymentMethod->last4.'</div>';
2016-04-29 23:50:21 +02:00
}
$paymentTypes[] = [
2016-05-07 04:33:03 +02:00
'url' => $url,
2016-04-29 23:50:21 +02:00
'label' => $html,
];
}
}
}
2016-04-29 23:50:21 +02:00
foreach(Gateway::$paymentTypes as $type) {
2016-04-29 23:50:21 +02:00
if ($gateway = $account->getGatewayByType($type)) {
$types = array($type);
2016-04-29 23:50:21 +02:00
if ($type == PAYMENT_TYPE_STRIPE) {
$types = array(PAYMENT_TYPE_STRIPE_CREDIT_CARD);
if ($gateway->getAchEnabled()) {
$types[] = PAYMENT_TYPE_STRIPE_ACH;
}
}
foreach($types as $type) {
$typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type));
$url = URL::to("/payment/{$invitation->invitation_key}/{$typeLink}");
// PayPal doesn't allow being run in an iframe so we need to open in new tab
if ($type === PAYMENT_TYPE_PAYPAL && $account->iframe_url) {
$url = 'javascript:window.open("' . $url . '", "_blank")';
}
if ($type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
$label = trans('texts.' . strtolower(PAYMENT_TYPE_CREDIT_CARD));
} elseif ($type == PAYMENT_TYPE_STRIPE_ACH) {
$label = trans('texts.' . strtolower(PAYMENT_TYPE_DIRECT_DEBIT));
} else {
$label = trans('texts.' . strtolower($type));
}
$paymentTypes[] = [
'url' => $url, 'label' => $label
];
2016-05-07 04:33:03 +02:00
if($gateway->getPayPalEnabled()) {
$paymentTypes[] = [
'label' => trans('texts.paypal'),
'url' => $url = URL::to("/payment/{$invitation->invitation_key}/braintree_paypal"),
];
}
}
}
}
return $paymentTypes;
2015-10-13 17:44:01 +02:00
}
2016-02-17 16:50:01 +01:00
public function download($invitationKey)
{
if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
return response()->view('error', [
'error' => trans('texts.invoice_not_found'),
'hideHeader' => true,
]);
}
$invoice = $invitation->invoice;
$pdfString = $invoice->getPDFString();
2016-02-17 20:13:55 +01:00
header('Content-Type: application/pdf');
header('Content-Length: ' . strlen($pdfString));
header('Content-disposition: attachment; filename="' . $invoice->getFileName() . '"');
header('Cache-Control: public, must-revalidate, max-age=0');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
return $pdfString;
2016-02-17 16:50:01 +01:00
}
2015-10-13 17:44:01 +02:00
public function dashboard()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2015-10-20 19:12:34 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
$account = $client->account;
2015-10-13 17:44:01 +02:00
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
if (!$account->enable_client_portal || !$account->enable_client_portal_dashboard) {
return $this->returnError();
}
2015-10-13 17:44:01 +02:00
$data = [
'color' => $color,
2016-05-25 21:04:58 +02:00
'contact' => $contact,
2015-10-13 17:44:01 +02:00
'account' => $account,
'client' => $client,
2016-01-07 08:08:30 +01:00
'clientFontUrl' => $account->getFontsUrl(),
'gateway' => $account->getTokenGateway(),
'paymentMethods' => $this->paymentService->getClientPaymentMethods($client),
2015-10-13 17:44:01 +02:00
];
2016-05-07 04:33:03 +02:00
if ($braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)){
if($braintreeGateway->getPayPalEnabled()) {
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
}
2015-10-13 17:44:01 +02:00
return response()->view('invited.dashboard', $data);
}
public function activityDatatable()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
return $this->returnError();
2015-10-20 19:12:34 +02:00
}
2015-10-13 17:44:01 +02:00
2016-05-24 23:02:28 +02:00
$client = $contact->client;
$query = $this->activityRepo->findByClientId($client->id);
2015-10-28 20:22:07 +01:00
$query->where('activities.adjustment', '!=', 0);
2015-10-13 17:44:01 +02:00
return Datatable::query($query)
->addColumn('activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); })
2015-12-31 12:31:50 +01:00
->addColumn('activity_type_id', function ($model) {
2015-10-28 20:22:07 +01:00
$data = [
'client' => Utils::getClientDisplayName($model),
'user' => $model->is_system ? ('<i>' . trans('texts.system') . '</i>') : ($model->account_name),
'invoice' => $model->invoice,
2015-10-28 20:22:07 +01:00
'contact' => Utils::getClientDisplayName($model),
'payment' => $model->payment ? ' ' . $model->payment : '',
2016-04-23 22:40:19 +02:00
'credit' => $model->payment_amount ? Utils::formatMoney($model->credit, $model->currency_id, $model->country_id) : '',
'payment_amount' => $model->payment_amount ? Utils::formatMoney($model->payment_amount, $model->currency_id, $model->country_id) : null,
'adjustment' => $model->adjustment ? Utils::formatMoney($model->adjustment, $model->currency_id, $model->country_id) : null,
2015-10-28 20:22:07 +01:00
];
return trans("texts.activity_{$model->activity_type_id}", $data);
})
->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id, $model->country_id); })
2015-12-14 22:05:17 +01:00
->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id, $model->country_id) : ''; })
2015-10-13 17:44:01 +02:00
->make();
}
public function recurringInvoiceIndex()
2015-10-13 17:44:01 +02:00
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2015-10-20 19:12:34 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
2015-10-13 17:44:01 +02:00
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
2015-10-13 17:44:01 +02:00
$data = [
'color' => $color,
2016-04-29 23:50:21 +02:00
'account' => $account,
2016-05-24 23:02:28 +02:00
'client' => $contact->client,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.recurring_invoices'),
'entityType' => ENTITY_RECURRING_INVOICE,
'columns' => Utils::trans(['frequency', 'start_date', 'end_date', 'invoice_total', 'auto_bill']),
];
return response()->view('public_list', $data);
}
public function invoiceIndex()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'account' => $account,
2016-05-24 23:02:28 +02:00
'client' => $contact->client,
2016-01-07 08:08:30 +01:00
'clientFontUrl' => $account->getFontsUrl(),
2015-10-13 17:44:01 +02:00
'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE,
'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']),
];
return response()->view('public_list', $data);
}
public function invoiceDatatable()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2015-12-27 12:08:58 +01:00
return '';
2015-10-20 19:12:34 +02:00
}
2015-10-13 17:44:01 +02:00
2016-05-24 23:02:28 +02:00
return $this->invoiceRepo->getClientDatatable($contact->id, ENTITY_INVOICE, Input::get('sSearch'));
2015-10-13 17:44:01 +02:00
}
public function recurringInvoiceDatatable()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
return '';
}
2016-05-24 23:02:28 +02:00
return $this->invoiceRepo->getClientRecurringDatatable($contact->id);
}
2015-10-13 17:44:01 +02:00
public function paymentIndex()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2015-10-20 19:12:34 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
2015-10-13 17:44:01 +02:00
$data = [
'color' => $color,
2016-04-29 23:50:21 +02:00
'account' => $account,
2016-01-07 08:08:30 +01:00
'clientFontUrl' => $account->getFontsUrl(),
2015-10-13 17:44:01 +02:00
'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'),
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'source', 'payment_amount', 'payment_date', 'status'])
2015-10-13 17:44:01 +02:00
];
return response()->view('public_list', $data);
}
public function paymentDatatable()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
return $this->returnError();
2015-10-20 19:12:34 +02:00
}
2016-05-24 23:02:28 +02:00
$payments = $this->paymentRepo->findForContact($contact->id, Input::get('sSearch'));
2015-10-13 17:44:01 +02:00
return Datatable::query($payments)
2016-04-10 21:54:02 +02:00
->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number)->toHtml() : $model->invoice_number; })
2015-10-13 17:44:01 +02:00
->addColumn('transaction_reference', function ($model) { return $model->transaction_reference ? $model->transaction_reference : '<i>Manual entry</i>'; })
->addColumn('payment_type', function ($model) { return ($model->payment_type && !$model->last4) ? $model->payment_type : ($model->account_gateway_id ? '<i>Online payment</i>' : ''); })
2016-05-07 04:33:03 +02:00
->addColumn('payment_source', function ($model) {
$code = str_replace(' ', '', strtolower($model->payment_type));
$card_type = trans("texts.card_" . $code);
2016-04-29 23:50:21 +02:00
if ($model->payment_type_id != PAYMENT_TYPE_ACH) {
2016-05-07 04:33:03 +02:00
if($model->last4) {
$expiration = trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($model->expiration, false)->format('m/y')));
return '<img height="22" src="' . URL::to('/images/credit_cards/' . $code . '.png') . '" alt="' . htmlentities($card_type) . '">&nbsp; &bull;&bull;&bull;' . $model->last4 . ' ' . $expiration;
} elseif ($model->email) {
return $model->email;
}
} elseif ($model->last4) {
2016-05-25 21:04:58 +02:00
if($model->bank_name) {
$bankName = $model->bank_name;
} else {
$bankData = PaymentMethod::lookupBankData($model->routing_number);
if($bankData) {
$bankName = $bankData->name;
}
}
if (!empty($bankName)) {
return $bankName.'&nbsp; &bull;&bull;&bull;' . $model->last4;
2016-05-07 04:33:03 +02:00
} elseif($model->last4) {
2016-04-29 23:50:21 +02:00
return '<img height="22" src="' . URL::to('/images/credit_cards/ach.png') . '" alt="' . htmlentities($card_type) . '">&nbsp; &bull;&bull;&bull;' . $model->last4;
}
}
})
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); })
2015-10-13 17:44:01 +02:00
->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); })
2016-04-23 22:40:19 +02:00
->addColumn('status', function ($model) { return $this->getPaymentStatusLabel($model); })
->orderColumns( 'invoice_number', 'transaction_reference', 'payment_type', 'amount', 'payment_date')
2015-10-13 17:44:01 +02:00
->make();
}
2016-04-23 22:40:19 +02:00
private function getPaymentStatusLabel($model)
{
$label = trans("texts.status_" . strtolower($model->payment_status_name));
$class = 'default';
switch ($model->payment_status_id) {
case PAYMENT_STATUS_PENDING:
$class = 'info';
break;
case PAYMENT_STATUS_COMPLETED:
$class = 'success';
break;
case PAYMENT_STATUS_FAILED:
$class = 'danger';
break;
case PAYMENT_STATUS_PARTIALLY_REFUNDED:
$label = trans('texts.status_partially_refunded_amount', [
'amount' => Utils::formatMoney($model->refunded, $model->currency_id, $model->country_id),
]);
$class = 'primary';
break;
case PAYMENT_STATUS_REFUNDED:
$class = 'default';
break;
}
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
}
2015-10-13 17:44:01 +02:00
public function quoteIndex()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2015-10-20 19:12:34 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
2015-10-13 17:44:01 +02:00
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
2016-04-29 23:50:21 +02:00
'account' => $account,
2016-01-07 08:08:30 +01:00
'clientFontUrl' => $account->getFontsUrl(),
2015-10-13 17:44:01 +02:00
'title' => trans('texts.quotes'),
'entityType' => ENTITY_QUOTE,
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']),
];
return response()->view('public_list', $data);
}
public function quoteDatatable()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2015-10-20 19:12:34 +02:00
return false;
}
2015-10-13 17:44:01 +02:00
2016-05-24 23:02:28 +02:00
return $this->invoiceRepo->getClientDatatable($contact->id, ENTITY_QUOTE, Input::get('sSearch'));
2015-10-13 17:44:01 +02:00
}
2016-03-24 16:58:30 +01:00
public function documentIndex()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-03-24 16:58:30 +01:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
2016-03-24 16:58:30 +01:00
$data = [
'color' => $color,
2016-04-29 23:50:21 +02:00
'account' => $account,
2016-03-24 16:58:30 +01:00
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.documents'),
'entityType' => ENTITY_DOCUMENT,
'columns' => Utils::trans(['invoice_number', 'name', 'document_date', 'document_size']),
];
return response()->view('public_list', $data);
}
public function documentDatatable()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-03-24 16:58:30 +01:00
return false;
}
2016-05-24 23:02:28 +02:00
return $this->documentRepo->getClientDatatable($contact->id, ENTITY_DOCUMENT, Input::get('sSearch'));
2016-03-24 16:58:30 +01:00
}
private function returnError($error = false)
2015-10-20 19:12:34 +02:00
{
return response()->view('error', [
'error' => $error ?: trans('texts.invoice_not_found'),
2015-10-20 19:12:34 +02:00
'hideHeader' => true,
]);
}
2016-05-24 23:02:28 +02:00
private function getContact() {
$contactKey = session('contact_key');
2015-10-13 17:44:01 +02:00
2016-05-24 23:02:28 +02:00
if (!$contactKey) {
2015-10-20 19:12:34 +02:00
return false;
2015-10-13 17:44:01 +02:00
}
2016-05-24 23:02:28 +02:00
$contact = Contact::where('contact_key', '=', $contactKey)->first();
2015-10-13 17:44:01 +02:00
2016-05-24 23:02:28 +02:00
if (!$contact || $contact->is_deleted) {
2015-10-20 19:12:34 +02:00
return false;
2015-10-13 17:44:01 +02:00
}
2016-05-24 23:02:28 +02:00
return $contact;
2015-10-13 17:44:01 +02:00
}
2016-03-23 23:40:42 +01:00
public function getDocumentVFSJS($publicId, $name){
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-03-23 23:40:42 +01:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$document = Document::scope($publicId, $contact->account_id)->first();
if(!$document->isPDFEmbeddable()){
2016-03-23 23:40:42 +01:00
return Response::view('error', array('error'=>'Image does not exist!'), 404);
}
2016-03-23 23:40:42 +01:00
$authorized = false;
2016-05-24 23:02:28 +02:00
if($document->expense && $document->expense->client_id == $contact->client_id){
2016-03-23 23:40:42 +01:00
$authorized = true;
2016-05-24 23:02:28 +02:00
} else if($document->invoice && $document->invoice->client_id ==$contact->client_id){
2016-03-23 23:40:42 +01:00
$authorized = true;
}
2016-03-23 23:40:42 +01:00
if(!$authorized){
return Response::view('error', array('error'=>'Not authorized'), 403);
}
2016-03-23 23:40:42 +01:00
if(substr($name, -3)=='.js'){
$name = substr($name, 0, -3);
}
2016-03-23 23:40:42 +01:00
$content = $document->preview?$document->getRawPreview():$document->getRaw();
$content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")';
$response = Response::make($content, 200);
$response->header('content-type', 'text/javascript');
$response->header('cache-control', 'max-age=31536000');
2016-03-23 23:40:42 +01:00
return $response;
}
protected function canCreateZip(){
return function_exists('gmp_init');
}
protected function getInvoiceZipDocuments($invoice, &$size=0){
$documents = $invoice->documents;
foreach($invoice->expenses as $expense){
$documents = $documents->merge($expense->documents);
}
$documents = $documents->sortBy('size');
$size = 0;
$maxSize = MAX_ZIP_DOCUMENTS_SIZE * 1000;
$toZip = array();
foreach($documents as $document){
if($size + $document->size > $maxSize)break;
if(!empty($toZip[$document->name])){
// This name is taken
if($toZip[$document->name]->hash != $document->hash){
// 2 different files with the same name
$nameInfo = pathinfo($document->name);
for($i = 1;; $i++){
$name = $nameInfo['filename'].' ('.$i.').'.$nameInfo['extension'];
if(empty($toZip[$name])){
$toZip[$name] = $document;
$size += $document->size;
break;
} else if ($toZip[$name]->hash == $document->hash){
// We're not adding this after all
break;
}
}
}
}
else{
$toZip[$document->name] = $document;
$size += $document->size;
}
}
return $toZip;
}
public function getInvoiceDocumentsZip($invitationKey){
if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
Session::put('contact_key', $invitation->contact->contact_key);// track current contact
$invoice = $invitation->invoice;
$toZip = $this->getInvoiceZipDocuments($invoice);
if(!count($toZip)){
return Response::view('error', array('error'=>'No documents small enough'), 404);
}
$zip = new ZipArchive($invitation->account->name.' Invoice '.$invoice->invoice_number.'.zip');
return Response::stream(function() use ($toZip, $zip) {
foreach($toZip as $name=>$document){
$fileStream = $document->getStream();
if($fileStream){
$zip->init_file_stream_transfer($name, $document->size, array('time'=>$document->created_at->timestamp));
while ($buffer = fread($fileStream, 256000))$zip->stream_file_part($buffer);
fclose($fileStream);
$zip->complete_file_stream();
}
else{
$zip->add_file($name, $document->getRaw());
}
}
$zip->finish();
}, 200);
}
2016-03-24 02:25:33 +01:00
public function getDocument($invitationKey, $publicId){
if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
Session::put('contact_key', $invitation->contact->contact_key);// track current contact
2016-03-24 02:25:33 +01:00
$clientId = $invitation->invoice->client_id;
$document = Document::scope($publicId, $invitation->account_id)->firstOrFail();
2016-03-24 02:25:33 +01:00
$authorized = false;
if($document->expense && $document->expense->client_id == $invitation->invoice->client_id){
$authorized = true;
} else if($document->invoice && $document->invoice->client_id == $invitation->invoice->client_id){
$authorized = true;
}
2016-03-24 02:25:33 +01:00
if(!$authorized){
return Response::view('error', array('error'=>'Not authorized'), 403);
}
2016-03-24 02:25:33 +01:00
return DocumentController::getDownloadResponse($document);
}
2015-10-13 17:44:01 +02:00
2016-04-29 23:50:21 +02:00
public function paymentMethods()
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-04-29 23:50:21 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
2016-04-29 23:50:21 +02:00
$account = $client->account;
$paymentMethods = $this->paymentService->getClientPaymentMethods($client);
$data = array(
'account' => $account,
2016-05-25 21:04:58 +02:00
'contact' => $contact,
2016-04-29 23:50:21 +02:00
'color' => $account->primary_color ? $account->primary_color : '#0b4d78',
'client' => $client,
'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(),
'paymentMethods' => $paymentMethods,
'gateway' => $account->getTokenGateway(),
'title' => trans('texts.payment_methods')
);
2016-05-07 04:33:03 +02:00
if ($braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)){
if($braintreeGateway->getPayPalEnabled()) {
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
}
2016-04-29 23:50:21 +02:00
return response()->view('payments.paymentmethods', $data);
}
public function verifyPaymentMethod()
{
2016-05-11 00:46:32 +02:00
$publicId = Input::get('source_id');
2016-04-29 23:50:21 +02:00
$amount1 = Input::get('verification1');
$amount2 = Input::get('verification2');
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-04-29 23:50:21 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
2016-05-11 00:46:32 +02:00
$result = $this->paymentService->verifyClientPaymentMethod($client, $publicId, $amount1, $amount2);
2016-04-29 23:50:21 +02:00
if (is_string($result)) {
Session::flash('error', $result);
} else {
Session::flash('message', trans('texts.payment_method_verified'));
}
return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
2016-04-29 23:50:21 +02:00
}
2016-05-11 00:46:32 +02:00
public function removePaymentMethod($publicId)
2016-04-29 23:50:21 +02:00
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-04-29 23:50:21 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
2016-05-11 00:46:32 +02:00
$result = $this->paymentService->removeClientPaymentMethod($client, $publicId);
2016-04-29 23:50:21 +02:00
if (is_string($result)) {
Session::flash('error', $result);
} else {
Session::flash('message', trans('texts.payment_method_removed'));
}
return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
2016-04-29 23:50:21 +02:00
}
2016-05-07 04:33:03 +02:00
public function addPaymentMethod($paymentType, $token=false)
2016-04-29 23:50:21 +02:00
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-04-29 23:50:21 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
2016-04-29 23:50:21 +02:00
$account = $client->account;
$typeLink = $paymentType;
$paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
2016-05-24 23:02:28 +02:00
$accountGateway = $client->account->getTokenGateway();
2016-04-29 23:50:21 +02:00
$gateway = $accountGateway->gateway;
2016-05-07 04:33:03 +02:00
if ($token && $paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
2016-05-25 21:04:58 +02:00
$sourceReference = $this->paymentService->createToken($paymentType, $this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $contact->id);
2016-05-07 04:33:03 +02:00
2016-05-11 00:46:32 +02:00
if(empty($sourceReference)) {
2016-05-07 04:33:03 +02:00
$this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
} else {
Session::flash('message', trans('texts.payment_method_added'));
}
return redirect()->to($account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
}
2016-04-29 23:50:21 +02:00
$acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
$data = [
'showBreadcrumbs' => false,
'client' => $client,
2016-05-24 23:02:28 +02:00
'contact' => $contact,
2016-04-29 23:50:21 +02:00
'gateway' => $gateway,
'accountGateway' => $accountGateway,
'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
'paymentType' => $paymentType,
'countries' => Cache::get('countries'),
'currencyId' => $client->getCurrencyId(),
'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
'account' => $account,
'url' => URL::to('client/paymentmethods/add/'.$typeLink),
'clientFontUrl' => $account->getFontsUrl(),
'showAddress' => $accountGateway->show_address,
2016-05-06 16:50:50 +02:00
'paymentTitle' => trans('texts.add_payment_method'),
2016-05-25 21:04:58 +02:00
'sourceId' => $token,
2016-04-29 23:50:21 +02:00
];
2016-05-25 21:04:58 +02:00
$details = json_decode(Input::get('details'));
$data['details'] = $details;
2016-04-29 23:50:21 +02:00
if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
$data['currencies'] = Cache::get('currencies');
}
if ($gateway->id == GATEWAY_BRAINTREE) {
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
2016-05-25 21:04:58 +02:00
if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| ($accountGateway->gateway_id == GATEWAY_WEPAY && $paymentType != PAYMENT_TYPE_WEPAY_ACH)) {
$data['tokenize'] = true;
}
2016-04-29 23:50:21 +02:00
return View::make('payments.add_paymentmethod', $data);
}
public function postAddPaymentMethod($paymentType)
{
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-04-29 23:50:21 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
2016-04-30 23:54:56 +02:00
$typeLink = $paymentType;
2016-04-29 23:50:21 +02:00
$paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
$account = $client->account;
$accountGateway = $account->getGatewayByType($paymentType);
$sourceToken = Input::get('sourceToken');
2016-04-29 23:50:21 +02:00
2016-05-15 22:27:56 +02:00
if (($validator = PaymentController::processPaymentClientDetails($client, $accountGateway, $paymentType)) !== true) {
2016-05-25 21:04:58 +02:00
return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)
2016-05-15 22:27:56 +02:00
->withErrors($validator)
->withInput(Request::except('cvv'));
}
2016-04-29 23:50:21 +02:00
if ($sourceToken) {
$details = array('token' => $sourceToken);
2016-05-01 04:45:51 +02:00
} elseif (Input::get('plaidPublicToken')) {
$usingPlaid = true;
$details = array('plaidPublicToken' => Input::get('plaidPublicToken'), 'plaidAccountId' => Input::get('plaidAccountId'));
}
2016-05-25 21:04:58 +02:00
if (($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) && !Input::get('authorize_ach')) {
2016-05-06 17:21:06 +02:00
Session::flash('error', trans('texts.ach_authorization_required'));
2016-05-25 21:04:58 +02:00
return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
}
if ($paymentType == PAYMENT_TYPE_WEPAY_ACH && !Input::get('tos_agree')) {
Session::flash('error', trans('texts.wepay_payment_tos_agree_required'));
return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
2016-05-06 17:21:06 +02:00
}
2016-05-01 04:45:51 +02:00
if (!empty($details)) {
2016-04-29 23:50:21 +02:00
$gateway = $this->paymentService->createGateway($accountGateway);
2016-05-25 21:04:58 +02:00
$sourceReference = $this->paymentService->createToken($paymentType, $gateway, $details, $accountGateway, $client, $contact->id);
2016-04-29 23:50:21 +02:00
} else {
2016-05-25 21:04:58 +02:00
return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
2016-04-29 23:50:21 +02:00
}
2016-05-11 00:46:32 +02:00
if(empty($sourceReference)) {
2016-04-30 23:54:56 +02:00
$this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
2016-05-25 21:04:58 +02:00
return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
2016-04-29 23:50:21 +02:00
} else if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty($usingPlaid) ) {
// The user needs to complete verification
Session::flash('message', trans('texts.bank_account_verification_next_steps'));
return Redirect::to($account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
2016-04-29 23:50:21 +02:00
} else {
Session::flash('message', trans('texts.payment_method_added'));
return redirect()->to($account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
2016-04-29 23:50:21 +02:00
}
}
public function setDefaultPaymentMethod(){
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
2016-04-29 23:50:21 +02:00
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
2016-04-29 23:50:21 +02:00
$validator = Validator::make(Input::all(), array('source' => 'required'));
if ($validator->fails()) {
return Redirect::to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
2016-04-29 23:50:21 +02:00
}
$result = $this->paymentService->setClientDefaultPaymentMethod($client, Input::get('source'));
if (is_string($result)) {
Session::flash('error', $result);
} else {
Session::flash('message', trans('texts.payment_method_set_as_default'));
}
return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
2016-04-29 23:50:21 +02:00
}
2016-04-30 23:54:56 +02:00
private function paymentMethodError($type, $error, $accountGateway = false, $exception = false)
{
$message = '';
if ($accountGateway && $accountGateway->gateway) {
$message = $accountGateway->gateway->name . ': ';
}
$message .= $error ?: trans('texts.payment_method_error');
Session::flash('error', $message);
Utils::logError("Payment Method Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
}
public function setAutoBill(){
2016-05-24 23:02:28 +02:00
if (!$contact = $this->getContact()) {
return $this->returnError();
}
2016-05-24 23:02:28 +02:00
$client = $contact->client;
$validator = Validator::make(Input::all(), array('public_id' => 'required'));
if ($validator->fails()) {
return Redirect::to('client/invoices/recurring');
}
$publicId = Input::get('public_id');
$enable = Input::get('enable');
$invoice = $client->invoices->where('public_id', intval($publicId))->first();
2016-05-09 22:29:02 +02:00
if ($invoice && $invoice->is_recurring && ($invoice->auto_bill == AUTO_BILL_OPT_IN || $invoice->auto_bill == AUTO_BILL_OPT_OUT)) {
$invoice->client_enable_auto_bill = $enable ? true : false;
$invoice->save();
}
return Redirect::to('client/invoices/recurring');
}
}