invoiceRepo = $invoiceRepo;
$this->paymentRepo = $paymentRepo;
$this->activityRepo = $activityRepo;
$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)
&& (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
event(new QuoteInvitationWasViewed($invoice, $invitation));
} else {
event(new InvoiceInvitationWasViewed($invoice, $invitation));
}
}
Session::put($invitationKey, true); // track this invitation has been seen
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',
]);
$data = array();
$paymentTypes = $this->getPaymentTypes($account, $client, $invitation);
$paymentURL = '';
if (count($paymentTypes) == 1) {
$paymentURL = $paymentTypes[0]['url'];
if (!$account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) {
$paymentURL = URL::to($paymentURL);
}
}
$paymentDriver = $account->paymentDriver($invitation, GATEWAY_TYPE_CREDIT_CARD);
if ($wepayGateway = $account->getGatewayConfig(GATEWAY_WEPAY)){
$data['enableWePayACH'] = $wepayGateway->getAchEnabled();
}
$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;
}
$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,
'transactionToken' => $paymentDriver->createTransactionToken(),
'partialView' => $paymentDriver->partialView(),
'accountGateway' => $paymentDriver->accountGateway,
'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);
}
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($account, $client, $invitation)
{
$links = [];
foreach ($account->account_gateways as $accountGateway) {
$paymentDriver = $accountGateway->paymentDriver($invitation);
$links = array_merge($links, $paymentDriver->tokenLinks());
$links = array_merge($links, $paymentDriver->paymentLinks());
}
return $links;
}
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();
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;
}
public function dashboard()
{
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$client = $contact->client;
$account = $client->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
if (!$account->enable_client_portal || !$account->enable_client_portal_dashboard) {
return $this->returnError();
}
$paymentDriver = $account->paymentDriver(false, GATEWAY_TYPE_TOKEN);
$customer = $paymentDriver->customer($client->id);
$data = [
'color' => $color,
'contact' => $contact,
'account' => $account,
'client' => $client,
'clientFontUrl' => $account->getFontsUrl(),
'gateway' => $account->getTokenGateway(),
'paymentMethods' => $customer ? $customer->payment_methods : false,
'transactionToken' => $paymentDriver->createTransactionToken(),
];
return response()->view('invited.dashboard', $data);
}
public function activityDatatable()
{
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$client = $contact->client;
$query = $this->activityRepo->findByClientId($client->id);
$query->where('activities.adjustment', '!=', 0);
return Datatable::query($query)
->addColumn('activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); })
->addColumn('activity_type_id', function ($model) {
$data = [
'client' => Utils::getClientDisplayName($model),
'user' => $model->is_system ? ('' . trans('texts.system') . '') : ($model->account_name),
'invoice' => $model->invoice,
'contact' => Utils::getClientDisplayName($model),
'payment' => $model->payment ? ' ' . $model->payment : '',
'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,
];
return trans("texts.activity_{$model->activity_type_id}", $data);
})
->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id, $model->country_id); })
->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id, $model->country_id) : ''; })
->make();
}
public function recurringInvoiceIndex()
{
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'account' => $account,
'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()
{
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'account' => $account,
'client' => $contact->client,
'clientFontUrl' => $account->getFontsUrl(),
'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()
{
if (!$contact = $this->getContact()) {
return '';
}
return $this->invoiceRepo->getClientDatatable($contact->id, ENTITY_INVOICE, Input::get('sSearch'));
}
public function recurringInvoiceDatatable()
{
if (!$contact = $this->getContact()) {
return '';
}
return $this->invoiceRepo->getClientRecurringDatatable($contact->id);
}
public function paymentIndex()
{
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
}
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'account' => $account,
'clientFontUrl' => $account->getFontsUrl(),
'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'),
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'source', 'payment_amount', 'payment_date', 'status'])
];
return response()->view('public_list', $data);
}
public function paymentDatatable()
{
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$payments = $this->paymentRepo->findForContact($contact->id, Input::get('sSearch'));
return Datatable::query($payments)
->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number)->toHtml() : $model->invoice_number; })
->addColumn('transaction_reference', function ($model) { return $model->transaction_reference ? $model->transaction_reference : 'Manual entry'; })
->addColumn('payment_type', function ($model) { return ($model->payment_type && !$model->last4) ? $model->payment_type : ($model->account_gateway_id ? 'Online payment' : ''); })
->addColumn('payment_source', function ($model) {
$code = str_replace(' ', '', strtolower($model->payment_type));
$card_type = trans("texts.card_" . $code);
if ($model->payment_type_id != PAYMENT_TYPE_ACH) {
if($model->last4) {
$expiration = trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($model->expiration, false)->format('m/y')));
return ' •••' . $model->last4 . ' ' . $expiration;
} elseif ($model->email) {
return $model->email;
}
} elseif ($model->last4) {
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.' •••' . $model->last4;
} elseif($model->last4) {
return '
•••' . $model->last4;
}
}
})
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); })
->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); })
->addColumn('status', function ($model) { return $this->getPaymentStatusLabel($model); })
->orderColumns( 'invoice_number', 'transaction_reference', 'payment_type', 'amount', 'payment_date')
->make();
}
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 "