mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
52b4f3f26a
@ -20,6 +20,6 @@ MAIL_FROM_ADDRESS
|
||||
MAIL_FROM_NAME
|
||||
MAIL_PASSWORD
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
#PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
|
||||
LOG=single
|
||||
|
@ -120,13 +120,10 @@ module.exports = function(grunt) {
|
||||
src: [
|
||||
'public/vendor/bootstrap/dist/css/bootstrap.min.css',
|
||||
'public/vendor/font-awesome/css/font-awesome.min.css',
|
||||
/*
|
||||
'public/css/bootstrap.splash.css',
|
||||
'public/css/splash.css',
|
||||
*/
|
||||
'public/css/bootstrap-combobox.css',
|
||||
'public/vendor/datatables/media/css/jquery.dataTables.css',
|
||||
'public/vendor/datatables-bootstrap3/BS3/assets/css/datatables.css',
|
||||
'public/css/public.style.css',
|
||||
],
|
||||
dest: 'public/css/built.public.css',
|
||||
nonull: true,
|
||||
|
@ -36,6 +36,10 @@ class SendReminders extends Command
|
||||
$this->info(count($accounts).' accounts found');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (!$account->isPro()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account);
|
||||
$this->info($account->name . ': ' . count($invoices).' invoices found');
|
||||
|
||||
|
@ -8,14 +8,16 @@ class UserSettingsChanged extends Event {
|
||||
|
||||
use SerializesModels;
|
||||
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct($user = false)
|
||||
{
|
||||
//
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -373,8 +373,10 @@ class AccountController extends BaseController
|
||||
$rules = [];
|
||||
$user = Auth::user();
|
||||
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
|
||||
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH));
|
||||
if (!$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) {
|
||||
$iframeURL = rtrim($iframeURL, "/");
|
||||
|
||||
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH));
|
||||
if ($iframeURL || !$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) {
|
||||
$subdomain = null;
|
||||
}
|
||||
if ($subdomain) {
|
||||
|
@ -20,14 +20,7 @@ class ActivityController extends BaseController
|
||||
->addColumn('activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); })
|
||||
->addColumn('message', function ($model) { return Utils::decodeActivity($model->message); })
|
||||
->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id); })
|
||||
->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? self::wrapAdjustment($model->adjustment, $model->currency_id) : ''; })
|
||||
->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id) : ''; })
|
||||
->make();
|
||||
}
|
||||
|
||||
private function wrapAdjustment($adjustment, $currencyId)
|
||||
{
|
||||
$class = $adjustment <= 0 ? 'success' : 'default';
|
||||
$adjustment = Utils::formatMoney($adjustment, $currencyId);
|
||||
return "<h4><div class=\"label label-{$class}\">$adjustment</div></h4>";
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ class AppController extends BaseController
|
||||
"MAIL_USERNAME={$mail['username']}\n".
|
||||
"MAIL_FROM_NAME={$mail['from']['name']}\n".
|
||||
"MAIL_PASSWORD={$mail['password']}\n\n".
|
||||
"PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'";
|
||||
"#PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'";
|
||||
|
||||
// Write Config Settings
|
||||
$fp = fopen(base_path()."/.env", 'w');
|
||||
|
@ -181,6 +181,10 @@ class InvoiceApiController extends Controller
|
||||
// initialize the line items
|
||||
if (isset($data['product_key']) || isset($data['cost']) || isset($data['notes']) || isset($data['qty'])) {
|
||||
$data['invoice_items'] = [self::prepareItem($data)];
|
||||
|
||||
// make sure the tax isn't applied twice (for the invoice and the line item)
|
||||
unset($data['invoice_items'][0]['tax_name']);
|
||||
unset($data['invoice_items'][0]['tax_rate']);
|
||||
} else {
|
||||
foreach ($data['invoice_items'] as $index => $item) {
|
||||
$data['invoice_items'][$index] = self::prepareItem($item);
|
||||
|
@ -76,28 +76,6 @@ class InvoiceController extends BaseController
|
||||
return View::make('list', $data);
|
||||
}
|
||||
|
||||
public function clientIndex()
|
||||
{
|
||||
$invitationKey = Session::get('invitation_key');
|
||||
if (!$invitationKey) {
|
||||
app()->abort(404);
|
||||
}
|
||||
|
||||
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
|
||||
$account = $invitation->account;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'title' => trans('texts.invoices'),
|
||||
'entityType' => ENTITY_INVOICE,
|
||||
'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']),
|
||||
];
|
||||
|
||||
return View::make('public_list', $data);
|
||||
}
|
||||
|
||||
public function getDatatable($clientPublicId = null)
|
||||
{
|
||||
$accountId = Auth::user()->account_id;
|
||||
@ -106,25 +84,6 @@ class InvoiceController extends BaseController
|
||||
return $this->invoiceRepo->getDatatable($accountId, $clientPublicId, ENTITY_INVOICE, $search);
|
||||
}
|
||||
|
||||
public function getClientDatatable()
|
||||
{
|
||||
$search = Input::get('sSearch');
|
||||
$invitationKey = Session::get('invitation_key');
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
|
||||
|
||||
if (!$invitation || $invitation->is_deleted) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, $search);
|
||||
}
|
||||
|
||||
public function getRecurringDatatable($clientPublicId = null)
|
||||
{
|
||||
$query = $this->invoiceRepo->getRecurringInvoices(Auth::user()->account_id, $clientPublicId, Input::get('sSearch'));
|
||||
@ -172,42 +131,22 @@ class InvoiceController extends BaseController
|
||||
|
||||
public function view($invitationKey)
|
||||
{
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
|
||||
|
||||
if (!$invitation) {
|
||||
app()->abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey);
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
app()->abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
$invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country');
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
$account = $invoice->account;
|
||||
|
||||
if (!$client || $client->is_deleted) {
|
||||
if (!$account->checkSubdomain(Request::server('HTTP_HOST'))) {
|
||||
app()->abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
if ($account->subdomain) {
|
||||
$server = explode('.', Request::server('HTTP_HOST'));
|
||||
$subdomain = $server[0];
|
||||
|
||||
if (!in_array($subdomain, ['app', 'www']) && $subdomain != $account->subdomain) {
|
||||
return View::make('invoices.deleted');
|
||||
}
|
||||
}
|
||||
|
||||
if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||
Activity::viewInvoice($invitation);
|
||||
Event::fire(new InvoiceViewed($invoice));
|
||||
}
|
||||
|
||||
Session::set($invitationKey, true);
|
||||
Session::set('invitation_key', $invitationKey);
|
||||
Session::set($invitationKey, true); // track this invitation has been seen
|
||||
Session::set('invitation_key', $invitationKey); // track current invitation
|
||||
|
||||
$account->loadLocalizationSettings($client);
|
||||
|
||||
@ -226,27 +165,16 @@ class InvoiceController extends BaseController
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'phone', ]);
|
||||
|
||||
// Determine payment options
|
||||
$paymentTypes = [];
|
||||
if ($client->getGatewayToken()) {
|
||||
$paymentTypes[] = [
|
||||
'url' => URL::to("payment/{$invitation->invitation_key}/token"), 'label' => trans('texts.use_card_on_file')
|
||||
];
|
||||
}
|
||||
foreach(Gateway::$paymentTypes as $type) {
|
||||
if ($account->getGatewayByType($type)) {
|
||||
$typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type));
|
||||
$paymentTypes[] = [
|
||||
'url' => URL::to("/payment/{$invitation->invitation_key}/{$typeLink}"), 'label' => trans('texts.'.strtolower($type))
|
||||
];
|
||||
}
|
||||
}
|
||||
'phone',
|
||||
]);
|
||||
|
||||
$paymentTypes = $this->getPaymentTypes($client, $invitation);
|
||||
$paymentURL = '';
|
||||
if (count($paymentTypes)) {
|
||||
$paymentURL = $paymentTypes[0]['url'];
|
||||
if (!$account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) {
|
||||
$paymentURL = URL::to($paymentURL);
|
||||
}
|
||||
}
|
||||
|
||||
$showApprove = $invoice->quote_invoice_id ? false : true;
|
||||
@ -271,6 +199,34 @@ class InvoiceController extends BaseController
|
||||
return View::make('invoices.view', $data);
|
||||
}
|
||||
|
||||
private function getPaymentTypes($client, $invitation)
|
||||
{
|
||||
$paymentTypes = [];
|
||||
$account = $client->account;
|
||||
|
||||
if ($client->getGatewayToken()) {
|
||||
$paymentTypes[] = [
|
||||
'url' => URL::to("payment/{$invitation->invitation_key}/token"), 'label' => trans('texts.use_card_on_file')
|
||||
];
|
||||
}
|
||||
foreach(Gateway::$paymentTypes as $type) {
|
||||
if ($account->getGatewayByType($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")';
|
||||
}
|
||||
$paymentTypes[] = [
|
||||
'url' => $url, 'label' => trans('texts.'.strtolower($type))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $paymentTypes;
|
||||
}
|
||||
|
||||
public function edit($publicId, $clone = false)
|
||||
{
|
||||
$invoice = Invoice::scope($publicId)->withTrashed()->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')->firstOrFail();
|
||||
@ -468,121 +424,138 @@ class InvoiceController extends BaseController
|
||||
{
|
||||
$action = Input::get('action');
|
||||
$entityType = Input::get('entityType');
|
||||
$input = json_decode(Input::get('data'));
|
||||
|
||||
if (in_array($action, ['archive', 'delete', 'mark', 'restore'])) {
|
||||
return InvoiceController::bulk($entityType);
|
||||
}
|
||||
|
||||
$input = json_decode(Input::get('data'));
|
||||
$invoice = $input->invoice;
|
||||
|
||||
if ($errors = $this->invoiceRepo->getErrors($invoice)) {
|
||||
if ($errors = $this->invoiceRepo->getErrors($input->invoice)) {
|
||||
Session::flash('error', trans('texts.invoice_error'));
|
||||
|
||||
return Redirect::to("{$entityType}s/create")
|
||||
->withInput()->withErrors($errors);
|
||||
} else {
|
||||
$this->taxRateRepo->save($input->tax_rates);
|
||||
|
||||
$clientData = (array) $invoice->client;
|
||||
$client = $this->clientRepo->save($invoice->client->public_id, $clientData);
|
||||
|
||||
$invoiceData = (array) $invoice;
|
||||
$invoiceData['client_id'] = $client->id;
|
||||
$invoice = $this->invoiceRepo->save($publicId, $invoiceData, $entityType);
|
||||
|
||||
$account = Auth::user()->account;
|
||||
if ($account->invoice_taxes != $input->invoice_taxes
|
||||
|| $account->invoice_item_taxes != $input->invoice_item_taxes
|
||||
|| $account->invoice_design_id != $input->invoice->invoice_design_id
|
||||
|| $account->show_item_taxes != $input->show_item_taxes) {
|
||||
$account->invoice_taxes = $input->invoice_taxes;
|
||||
$account->invoice_item_taxes = $input->invoice_item_taxes;
|
||||
$account->invoice_design_id = $input->invoice->invoice_design_id;
|
||||
$account->show_item_taxes = $input->show_item_taxes;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
$client->load('contacts');
|
||||
$sendInvoiceIds = [];
|
||||
|
||||
foreach ($client->contacts as $contact) {
|
||||
if ($contact->send_invoice || count($client->contacts) == 1) {
|
||||
$sendInvoiceIds[] = $contact->id;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($client->contacts as $contact) {
|
||||
$invitation = Invitation::scope()->whereContactId($contact->id)->whereInvoiceId($invoice->id)->first();
|
||||
|
||||
if (in_array($contact->id, $sendInvoiceIds) && !$invitation) {
|
||||
$invitation = Invitation::createNew();
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
$invitation->contact_id = $contact->id;
|
||||
$invitation->invitation_key = str_random(RANDOM_KEY_LENGTH);
|
||||
$invitation->save();
|
||||
} elseif (!in_array($contact->id, $sendInvoiceIds) && $invitation) {
|
||||
$invitation->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$invoice = $this->saveInvoice($publicId, $input, $entityType);
|
||||
$url = "{$entityType}s/".$invoice->public_id.'/edit';
|
||||
$message = trans($publicId ? "texts.updated_{$entityType}" : "texts.created_{$entityType}");
|
||||
|
||||
// check if we created a new client with the invoice
|
||||
if ($input->invoice->client->public_id == '-1') {
|
||||
$message = $message.' '.trans('texts.and_created_client');
|
||||
|
||||
$url = URL::to('clients/'.$client->public_id);
|
||||
$url = URL::to('clients/'.$input->invoice->client->public_id);
|
||||
Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT, $url);
|
||||
}
|
||||
|
||||
if ($invoice->account->pdf_email_attachment && !$invoice->is_recurring) {
|
||||
$pdfUpload = Input::get('pdfupload');
|
||||
if (!empty($pdfUpload) && strpos($pdfUpload, 'data:application/pdf;base64,') === 0) {
|
||||
$invoice->updateCachedPDF($pdfUpload);
|
||||
}
|
||||
}
|
||||
|
||||
if ($action == 'clone') {
|
||||
return $this->cloneInvoice($publicId);
|
||||
} elseif ($action == 'convert') {
|
||||
return $this->convertQuote($publicId);
|
||||
} elseif ($action == 'email') {
|
||||
if (Auth::user()->confirmed && !Auth::user()->isDemo()) {
|
||||
if ($invoice->is_recurring) {
|
||||
if ($invoice->shouldSendToday()) {
|
||||
$invoice = $this->invoiceRepo->createRecurringInvoice($invoice);
|
||||
// in case auto-bill is enabled
|
||||
if ($invoice->isPaid()) {
|
||||
$response = true;
|
||||
} else {
|
||||
$response = $this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
} else {
|
||||
$response = trans('texts.recurring_too_soon');
|
||||
}
|
||||
} else {
|
||||
$response = $this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
if ($response === true) {
|
||||
$message = trans("texts.emailed_{$entityType}");
|
||||
Session::flash('message', $message);
|
||||
} else {
|
||||
Session::flash('error', $response);
|
||||
}
|
||||
} else {
|
||||
$errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required');
|
||||
Session::flash('error', $errorMessage);
|
||||
Session::flash('message', $message);
|
||||
}
|
||||
} else {
|
||||
Session::flash('message', $message);
|
||||
return $this->emailInvoice($invoice, Input::get('pdfupload'));
|
||||
}
|
||||
|
||||
$url = "{$entityType}s/".$invoice->public_id.'/edit';
|
||||
|
||||
|
||||
Session::flash('message', $message);
|
||||
return Redirect::to($url);
|
||||
}
|
||||
}
|
||||
|
||||
private function emailInvoice($invoice, $pdfUpload)
|
||||
{
|
||||
$entityType = $invoice->getEntityType();
|
||||
$pdfUpload = Utils::decodePDF($pdfUpload);
|
||||
|
||||
if (!Auth::user()->confirmed) {
|
||||
$errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required');
|
||||
Session::flash('error', $errorMessage);
|
||||
Session::flash('message', $message);
|
||||
return Redirect::to($url);
|
||||
}
|
||||
|
||||
if ($invoice->is_recurring) {
|
||||
$response = $this->emailRecurringInvoice($invoice);
|
||||
} else {
|
||||
$response = $this->mailer->sendInvoice($invoice, false, $pdfUpload);
|
||||
}
|
||||
|
||||
if ($response === true) {
|
||||
$message = trans("texts.emailed_{$entityType}");
|
||||
Session::flash('message', $message);
|
||||
} else {
|
||||
Session::flash('error', $response);
|
||||
}
|
||||
|
||||
return Redirect::to("{$entityType}s/{$invoice->public_id}/edit");
|
||||
}
|
||||
|
||||
private function emailRecurringInvoice(&$invoice)
|
||||
{
|
||||
if (!$invoice->shouldSendToday()) {
|
||||
return trans('texts.recurring_too_soon');
|
||||
}
|
||||
|
||||
// switch from the recurring invoice to the generated invoice
|
||||
$invoice = $this->invoiceRepo->createRecurringInvoice($invoice);
|
||||
|
||||
// in case auto-bill is enabled then a receipt has been sent
|
||||
if ($invoice->isPaid()) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
}
|
||||
|
||||
private function saveInvoice($publicId, $input, $entityType)
|
||||
{
|
||||
$invoice = $input->invoice;
|
||||
|
||||
$this->taxRateRepo->save($input->tax_rates);
|
||||
|
||||
$clientData = (array) $invoice->client;
|
||||
$client = $this->clientRepo->save($invoice->client->public_id, $clientData);
|
||||
|
||||
$invoiceData = (array) $invoice;
|
||||
$invoiceData['client_id'] = $client->id;
|
||||
$invoice = $this->invoiceRepo->save($publicId, $invoiceData, $entityType);
|
||||
|
||||
$account = Auth::user()->account;
|
||||
if ($account->invoice_taxes != $input->invoice_taxes
|
||||
|| $account->invoice_item_taxes != $input->invoice_item_taxes
|
||||
|| $account->invoice_design_id != $input->invoice->invoice_design_id
|
||||
|| $account->show_item_taxes != $input->show_item_taxes) {
|
||||
$account->invoice_taxes = $input->invoice_taxes;
|
||||
$account->invoice_item_taxes = $input->invoice_item_taxes;
|
||||
$account->invoice_design_id = $input->invoice->invoice_design_id;
|
||||
$account->show_item_taxes = $input->show_item_taxes;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
$client->load('contacts');
|
||||
$sendInvoiceIds = [];
|
||||
|
||||
foreach ($client->contacts as $contact) {
|
||||
if ($contact->send_invoice || count($client->contacts) == 1) {
|
||||
$sendInvoiceIds[] = $contact->id;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($client->contacts as $contact) {
|
||||
$invitation = Invitation::scope()->whereContactId($contact->id)->whereInvoiceId($invoice->id)->first();
|
||||
|
||||
if (in_array($contact->id, $sendInvoiceIds) && !$invitation) {
|
||||
$invitation = Invitation::createNew();
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
$invitation->contact_id = $contact->id;
|
||||
$invitation->invitation_key = str_random(RANDOM_KEY_LENGTH);
|
||||
$invitation->save();
|
||||
} elseif (!in_array($contact->id, $sendInvoiceIds) && $invitation) {
|
||||
$invitation->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
|
@ -47,28 +47,6 @@ class PaymentController extends BaseController
|
||||
));
|
||||
}
|
||||
|
||||
public function clientIndex()
|
||||
{
|
||||
$invitationKey = Session::get('invitation_key');
|
||||
if (!$invitationKey) {
|
||||
app()->abort(404);
|
||||
}
|
||||
|
||||
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
|
||||
$account = $invitation->account;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'entityType' => ENTITY_PAYMENT,
|
||||
'title' => trans('texts.payments'),
|
||||
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date'])
|
||||
];
|
||||
|
||||
return View::make('public_list', $data);
|
||||
}
|
||||
|
||||
public function getDatatable($clientPublicId = null)
|
||||
{
|
||||
$payments = $this->paymentRepo->find($clientPublicId, Input::get('sSearch'));
|
||||
@ -114,33 +92,6 @@ class PaymentController extends BaseController
|
||||
->make();
|
||||
}
|
||||
|
||||
public function getClientDatatable()
|
||||
{
|
||||
$search = Input::get('sSearch');
|
||||
$invitationKey = Session::get('invitation_key');
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->with('contact.client')->first();
|
||||
|
||||
if (!$invitation) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$payments = $this->paymentRepo->findForContact($invitation->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) : $model->invoice_number; })
|
||||
->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->payment_type : ($model->account_gateway_id ? '<i>Online payment</i>' : ''); })
|
||||
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id); })
|
||||
->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); })
|
||||
->make();
|
||||
}
|
||||
|
||||
public function create($clientPublicId = 0, $invoicePublicId = 0)
|
||||
{
|
||||
$invoices = Invoice::scope()
|
||||
@ -549,14 +500,16 @@ class PaymentController extends BaseController
|
||||
|
||||
$invitation = Invitation::with('invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('transaction_reference', '=', $token)->firstOrFail();
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
|
||||
$accountGateway = $account->getGatewayByType(Session::get('payment_type'));
|
||||
$gateway = $this->paymentService->createGateway($accountGateway);
|
||||
|
||||
// Check for Dwolla payment error
|
||||
if ($accountGateway->isGateway(GATEWAY_DWOLLA) && Input::get('error')) {
|
||||
$this->error('Dwolla', Input::get('error_description'), $accountGateway);
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
return Redirect::to($invitation->getLink());
|
||||
}
|
||||
|
||||
try {
|
||||
@ -569,20 +522,20 @@ class PaymentController extends BaseController
|
||||
$payment = $this->paymentService->createPayment($invitation, $ref, $payerId);
|
||||
Session::flash('message', trans('texts.applied_payment'));
|
||||
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
return Redirect::to($invitation->getLink());
|
||||
} else {
|
||||
$this->error('offsite', $response->getMessage(), $accountGateway);
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
return Redirect::to($invitation->getLink());
|
||||
}
|
||||
} else {
|
||||
$payment = $this->paymentService->createPayment($invitation, $token, $payerId);
|
||||
Session::flash('message', trans('texts.applied_payment'));
|
||||
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
return Redirect::to($invitation->getLink());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Offsite-uncaught', false, $accountGateway, $e);
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
return Redirect::to($invitation->getLink());
|
||||
}
|
||||
}
|
||||
|
||||
|
163
app/Http/Controllers/PublicClientController.php
Normal file
163
app/Http/Controllers/PublicClientController.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use DB;
|
||||
use Input;
|
||||
use Utils;
|
||||
use Datatable;
|
||||
use App\Models\Invitation;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
|
||||
class PublicClientController extends BaseController
|
||||
{
|
||||
private $invoiceRepo;
|
||||
private $paymentRepo;
|
||||
|
||||
public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo)
|
||||
{
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->paymentRepo = $paymentRepo;
|
||||
}
|
||||
|
||||
public function dashboard()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
$account = $invitation->account;
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'account' => $account,
|
||||
'client' => $client,
|
||||
];
|
||||
|
||||
return response()->view('invited.dashboard', $data);
|
||||
}
|
||||
|
||||
public function activityDatatable()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
$query = DB::table('activities')
|
||||
->join('clients', 'clients.id', '=', 'activities.client_id')
|
||||
->where('activities.client_id', '=', $invoice->client_id)
|
||||
->where('activities.adjustment', '!=', 0)
|
||||
->select('activities.id', 'activities.message', 'activities.created_at', 'clients.currency_id', 'activities.balance', 'activities.adjustment');
|
||||
|
||||
return Datatable::query($query)
|
||||
->addColumn('activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); })
|
||||
->addColumn('message', function ($model) { return strip_tags(Utils::decodeActivity($model->message)); })
|
||||
->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id); })
|
||||
->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id) : ''; })
|
||||
->make();
|
||||
}
|
||||
|
||||
public function invoiceIndex()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
$account = $invitation->account;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'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()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
|
||||
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch'));
|
||||
}
|
||||
|
||||
|
||||
public function paymentIndex()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
$account = $invitation->account;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'entityType' => ENTITY_PAYMENT,
|
||||
'title' => trans('texts.payments'),
|
||||
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date'])
|
||||
];
|
||||
|
||||
return response()->view('public_list', $data);
|
||||
}
|
||||
|
||||
public function paymentDatatable()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
$payments = $this->paymentRepo->findForContact($invitation->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) : $model->invoice_number; })
|
||||
->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->payment_type : ($model->account_gateway_id ? '<i>Online payment</i>' : ''); })
|
||||
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id); })
|
||||
->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); })
|
||||
->make();
|
||||
}
|
||||
|
||||
public function quoteIndex()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
$account = $invitation->account;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'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()
|
||||
{
|
||||
$invitation = $this->getInvitation();
|
||||
|
||||
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, Input::get('sSearch'));
|
||||
}
|
||||
|
||||
private function getInvitation()
|
||||
{
|
||||
$invitationKey = session('invitation_key');
|
||||
|
||||
if (!$invitationKey) {
|
||||
app()->abort(404);
|
||||
}
|
||||
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
|
||||
|
||||
if (!$invitation || $invitation->is_deleted) {
|
||||
app()->abort(404);
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
app()->abort(404);
|
||||
}
|
||||
|
||||
return $invitation;
|
||||
}
|
||||
|
||||
}
|
@ -67,28 +67,6 @@ class QuoteController extends BaseController
|
||||
return View::make('list', $data);
|
||||
}
|
||||
|
||||
public function clientIndex()
|
||||
{
|
||||
$invitationKey = Session::get('invitation_key');
|
||||
if (!$invitationKey) {
|
||||
app()->abort(404);
|
||||
}
|
||||
|
||||
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
|
||||
$account = $invitation->account;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'title' => trans('texts.quotes'),
|
||||
'entityType' => ENTITY_QUOTE,
|
||||
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']),
|
||||
];
|
||||
|
||||
return View::make('public_list', $data);
|
||||
}
|
||||
|
||||
public function getDatatable($clientPublicId = null)
|
||||
{
|
||||
$accountId = Auth::user()->account_id;
|
||||
@ -97,25 +75,6 @@ class QuoteController extends BaseController
|
||||
return $this->invoiceRepo->getDatatable($accountId, $clientPublicId, ENTITY_QUOTE, $search);
|
||||
}
|
||||
|
||||
public function getClientDatatable()
|
||||
{
|
||||
$search = Input::get('sSearch');
|
||||
$invitationKey = Session::get('invitation_key');
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
|
||||
|
||||
if (!$invitation || $invitation->is_deleted) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, $search);
|
||||
}
|
||||
|
||||
public function create($clientPublicId = 0)
|
||||
{
|
||||
if (!Utils::isPro()) {
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php namespace InvoiceNinja\Http\Controllers;
|
||||
|
||||
class HomeController extends Controller {
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Home Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller renders your application's "dashboard" for users that
|
||||
| are authenticated. Of course, you are free to change or remove the
|
||||
| controller as you wish. It is just here to get your app started!
|
||||
|
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application dashboard to the user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('home');
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
<?php namespace InvoiceNinja\Http\Controllers;
|
||||
|
||||
class WelcomeController extends Controller {
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Welcome Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller renders the "marketing page" for the application and
|
||||
| is configured to only allow guests. Like most of the other sample
|
||||
| controllers, you are free to modify or remove it as you desire.
|
||||
|
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application welcome screen to the user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('welcome');
|
||||
}
|
||||
|
||||
}
|
@ -48,12 +48,14 @@ Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||
Route::get('complete', 'PaymentController@offsite_payment');
|
||||
Route::get('client/quotes', 'QuoteController@clientIndex');
|
||||
Route::get('client/invoices', 'InvoiceController@clientIndex');
|
||||
Route::get('client/payments', 'PaymentController@clientIndex');
|
||||
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'QuoteController@getClientDatatable'));
|
||||
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'InvoiceController@getClientDatatable'));
|
||||
Route::get('api/client.payments', array('as'=>'api.client.payments', 'uses'=>'PaymentController@getClientDatatable'));
|
||||
Route::get('client/quotes', 'PublicClientController@quoteIndex');
|
||||
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
|
||||
Route::get('client/payments', 'PublicClientController@paymentIndex');
|
||||
Route::get('client/dashboard', 'PublicClientController@dashboard');
|
||||
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable'));
|
||||
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'PublicClientController@invoiceDatatable'));
|
||||
Route::get('api/client.payments', array('as'=>'api.client.payments', 'uses'=>'PublicClientController@paymentDatatable'));
|
||||
Route::get('api/client.activity', array('as'=>'api.client.activity', 'uses'=>'PublicClientController@activityDatatable'));
|
||||
|
||||
Route::get('license', 'PaymentController@show_license_payment');
|
||||
Route::post('license', 'PaymentController@do_license_payment');
|
||||
|
@ -94,11 +94,6 @@ class Utils
|
||||
return isset($_ENV[DEMO_ACCOUNT_ID]) ? $_ENV[DEMO_ACCOUNT_ID] : false;
|
||||
}
|
||||
|
||||
public static function isDemo()
|
||||
{
|
||||
return Auth::check() && Auth::user()->isDemo();
|
||||
}
|
||||
|
||||
public static function getNewsFeedResponse($userType = false)
|
||||
{
|
||||
if (!$userType) {
|
||||
@ -634,6 +629,11 @@ class Utils
|
||||
];
|
||||
}
|
||||
|
||||
public static function isEmpty($value)
|
||||
{
|
||||
return !$value || $value == '0.00' || $value == '0,00';
|
||||
}
|
||||
|
||||
public static function startsWith($haystack, $needle)
|
||||
{
|
||||
return $needle === "" || strpos($haystack, $needle) === 0;
|
||||
@ -672,7 +672,8 @@ class Utils
|
||||
fwrite($output, "\n");
|
||||
}
|
||||
|
||||
public static function getFirst($values) {
|
||||
public static function getFirst($values)
|
||||
{
|
||||
if (is_array($values)) {
|
||||
return count($values) ? $values[0] : false;
|
||||
} else {
|
||||
@ -681,7 +682,8 @@ class Utils
|
||||
}
|
||||
|
||||
// nouns in German and French should be uppercase
|
||||
public static function transFlowText($key) {
|
||||
public static function transFlowText($key)
|
||||
{
|
||||
$str = trans("texts.$key");
|
||||
if (!in_array(App::getLocale(), ['de', 'fr'])) {
|
||||
$str = strtolower($str);
|
||||
@ -689,7 +691,8 @@ class Utils
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function getSubdomainPlaceholder() {
|
||||
public static function getSubdomainPlaceholder()
|
||||
{
|
||||
$parts = parse_url(SITE_URL);
|
||||
$subdomain = '';
|
||||
if (isset($parts['host'])) {
|
||||
@ -701,7 +704,8 @@ class Utils
|
||||
return $subdomain;
|
||||
}
|
||||
|
||||
public static function getDomainPlaceholder() {
|
||||
public static function getDomainPlaceholder()
|
||||
{
|
||||
$parts = parse_url(SITE_URL);
|
||||
$domain = '';
|
||||
if (isset($parts['host'])) {
|
||||
@ -719,7 +723,8 @@ class Utils
|
||||
return $domain;
|
||||
}
|
||||
|
||||
public static function replaceSubdomain($domain, $subdomain) {
|
||||
public static function replaceSubdomain($domain, $subdomain)
|
||||
{
|
||||
$parsedUrl = parse_url($domain);
|
||||
$host = explode('.', $parsedUrl['host']);
|
||||
if (count($host) > 0) {
|
||||
@ -729,11 +734,61 @@ class Utils
|
||||
return $domain;
|
||||
}
|
||||
|
||||
public static function splitName($name) {
|
||||
public static function splitName($name)
|
||||
{
|
||||
$name = trim($name);
|
||||
$lastName = (strpos($name, ' ') === false) ? '' : preg_replace('#.*\s([\w-]*)$#', '$1', $name);
|
||||
$firstName = trim( preg_replace('#'.$lastName.'#', '', $name ) );
|
||||
$firstName = trim(preg_replace('#'.$lastName.'#', '', $name));
|
||||
return array($firstName, $lastName);
|
||||
}
|
||||
|
||||
public static function decodePDF($string)
|
||||
{
|
||||
$string = str_replace('data:application/pdf;base64,', '', $string);
|
||||
return base64_decode($string);
|
||||
}
|
||||
|
||||
public static function cityStateZip($city, $state, $postalCode, $swap)
|
||||
{
|
||||
$str = $city;
|
||||
|
||||
if ($state) {
|
||||
if ($str) {
|
||||
$str .= ', ';
|
||||
}
|
||||
$str .= $state;
|
||||
}
|
||||
|
||||
if ($swap) {
|
||||
return $postalCode . ' ' . $str;
|
||||
} else {
|
||||
return $str . ' ' . $postalCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static function formatWebsite($website)
|
||||
{
|
||||
if (!$website) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$link = $website;
|
||||
$title = $website;
|
||||
$prefix = 'http://';
|
||||
|
||||
if (strlen($link) > 7 && substr($link, 0, 7) === $prefix) {
|
||||
$title = substr($title, 7);
|
||||
} else {
|
||||
$link = $prefix.$link;
|
||||
}
|
||||
|
||||
return link_to($link, $title, array('target' => '_blank'));
|
||||
}
|
||||
|
||||
public static function wrapAdjustment($adjustment, $currencyId)
|
||||
{
|
||||
$class = $adjustment <= 0 ? 'success' : 'default';
|
||||
$adjustment = Utils::formatMoney($adjustment, $currencyId);
|
||||
return "<h4><div class=\"label label-{$class}\">$adjustment</div></h4>";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use App\Events\UserSettingsChanged;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldBeQueued;
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
|
||||
class HandleUserSettingsChanged {
|
||||
|
||||
@ -14,9 +15,10 @@ class HandleUserSettingsChanged {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AccountRepository $accountRepo)
|
||||
public function __construct(AccountRepository $accountRepo, UserMailer $userMailer)
|
||||
{
|
||||
$this->accountRepo = $accountRepo;
|
||||
$this->userMailer = $userMailer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,12 +29,19 @@ class HandleUserSettingsChanged {
|
||||
*/
|
||||
public function handle(UserSettingsChanged $event)
|
||||
{
|
||||
if (Auth::check()) {
|
||||
$account = Auth::user()->account;
|
||||
$account->loadLocalizationSettings();
|
||||
if (!Auth::check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
$account = Auth::user()->account;
|
||||
$account->loadLocalizationSettings();
|
||||
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
|
||||
if ($event->user && $event->user->isEmailBeingChanged()) {
|
||||
$this->userMailer->sendConfirmation($event->user);
|
||||
Session::flash('warning', trans('texts.verify_email'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,12 @@ class Account extends Eloquent
|
||||
return $user->getDisplayName();
|
||||
}
|
||||
|
||||
public function getCityState()
|
||||
{
|
||||
$swap = $this->country && $this->country->swap_postal_code;
|
||||
return Utils::cityStateZip($this->city, $this->state, $this->postal_code, $swap);
|
||||
}
|
||||
|
||||
public function getMomentDateTimeFormat()
|
||||
{
|
||||
$format = $this->datetime_format ? $this->datetime_format->format_moment : DEFAULT_DATETIME_MOMENT_FORMAT;
|
||||
@ -158,12 +164,10 @@ class Account extends Eloquent
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public function hasLogo()
|
||||
{
|
||||
file_exists($this->getLogoPath());
|
||||
return file_exists($this->getLogoPath());
|
||||
}
|
||||
*/
|
||||
|
||||
public function getLogoPath()
|
||||
{
|
||||
@ -426,11 +430,13 @@ class Account extends Eloquent
|
||||
|
||||
public function getEmailSubject($entityType)
|
||||
{
|
||||
$field = "email_subject_{$entityType}";
|
||||
$value = $this->$field;
|
||||
if ($this->isPro()) {
|
||||
$field = "email_subject_{$entityType}";
|
||||
$value = $this->$field;
|
||||
|
||||
if ($value) {
|
||||
return $value;
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getDefaultEmailSubject($entityType);
|
||||
@ -455,13 +461,15 @@ class Account extends Eloquent
|
||||
|
||||
public function getEmailTemplate($entityType, $message = false)
|
||||
{
|
||||
$field = "email_template_{$entityType}";
|
||||
$template = $this->$field;
|
||||
if ($this->isPro()) {
|
||||
$field = "email_template_{$entityType}";
|
||||
$template = $this->$field;
|
||||
|
||||
if ($template) {
|
||||
return $template;
|
||||
if ($template) {
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $this->getDefaultEmailTemplate($entityType, $message);
|
||||
}
|
||||
|
||||
@ -503,6 +511,43 @@ class Account extends Eloquent
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function checkSubdomain($host)
|
||||
{
|
||||
if (!$this->subdomain) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$server = explode('.', $host);
|
||||
$subdomain = $server[0];
|
||||
|
||||
if (!in_array($subdomain, ['app', 'www']) && $subdomain != $this->subdomain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function showCustomField($field, $entity)
|
||||
{
|
||||
if ($this->isPro()) {
|
||||
return $this->$field ? true : false;
|
||||
}
|
||||
|
||||
if (!$entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert (for example) 'custom_invoice_label1' to 'invoice.custom_value1'
|
||||
$field = str_replace(['invoice_', 'label'], ['', 'value'], $field);
|
||||
|
||||
return Utils::isEmpty($entity->$field) ? false : true;
|
||||
}
|
||||
|
||||
public function attatchPDF()
|
||||
{
|
||||
return $this->isPro() && $this->pdf_email_attachment;
|
||||
}
|
||||
}
|
||||
|
||||
Account::updated(function ($account) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Models;
|
||||
|
||||
use Utils;
|
||||
use DB;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -88,6 +89,12 @@ class Client extends EntityModel
|
||||
return $contact->getDisplayName();
|
||||
}
|
||||
|
||||
public function getCityState()
|
||||
{
|
||||
$swap = $this->country && $this->country->swap_postal_code;
|
||||
return Utils::cityStateZip($this->city, $this->state, $this->postal_code, $swap);
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return ENTITY_CLIENT;
|
||||
@ -113,25 +120,6 @@ class Client extends EntityModel
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getWebsite()
|
||||
{
|
||||
if (!$this->website) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$link = $this->website;
|
||||
$title = $this->website;
|
||||
$prefix = 'http://';
|
||||
|
||||
if (strlen($link) > 7 && substr($link, 0, 7) === $prefix) {
|
||||
$title = substr($title, 7);
|
||||
} else {
|
||||
$link = $prefix.$link;
|
||||
}
|
||||
|
||||
return link_to($link, $title, array('target' => '_blank'));
|
||||
}
|
||||
|
||||
public function getDateCreated()
|
||||
{
|
||||
if ($this->created_at == '0000-00-00 00:00:00') {
|
||||
|
@ -36,13 +36,15 @@ class Invitation extends EntityModel
|
||||
|
||||
$url = SITE_URL;
|
||||
$iframe_url = $this->account->iframe_url;
|
||||
|
||||
if ($iframe_url) {
|
||||
return "{$iframe_url}/?{$this->invitation_key}";
|
||||
} elseif ($this->account->subdomain) {
|
||||
$url = Utils::replaceSubdomain($url, $this->account->subdomain);
|
||||
|
||||
if ($this->account->isPro()) {
|
||||
if ($iframe_url) {
|
||||
return "{$iframe_url}/?{$this->invitation_key}";
|
||||
} elseif ($this->account->subdomain) {
|
||||
$url = Utils::replaceSubdomain($url, $this->account->subdomain);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "{$url}/view/{$this->invitation_key}";
|
||||
}
|
||||
|
||||
|
@ -285,37 +285,36 @@ class Invoice extends EntityModel
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateCachedPDF($encodedString = false)
|
||||
public function getPDFString()
|
||||
{
|
||||
if (!$encodedString && env('PHANTOMJS_CLOUD_KEY')) {
|
||||
$invitation = $this->invitations[0];
|
||||
$link = $invitation->getLink();
|
||||
|
||||
$curl = curl_init();
|
||||
$jsonEncodedData = json_encode([
|
||||
'targetUrl' => "{$link}?phantomjs=true",
|
||||
'requestType' => 'raw',
|
||||
'delayTime' => 3000,
|
||||
]);
|
||||
|
||||
$opts = [
|
||||
CURLOPT_URL => PHANTOMJS_CLOUD . env('PHANTOMJS_CLOUD_KEY'),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $jsonEncodedData,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: '.strlen($jsonEncodedData)],
|
||||
];
|
||||
|
||||
curl_setopt_array($curl, $opts);
|
||||
$encodedString = strip_tags(curl_exec($curl));
|
||||
curl_close($curl);
|
||||
}
|
||||
|
||||
$encodedString = str_replace('data:application/pdf;base64,', '', $encodedString);
|
||||
if ($encodedString = base64_decode($encodedString)) {
|
||||
file_put_contents($this->getPDFPath(), $encodedString);
|
||||
if (!env('PHANTOMJS_CLOUD_KEY')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$invitation = $this->invitations[0];
|
||||
$link = $invitation->getLink();
|
||||
|
||||
$curl = curl_init();
|
||||
$jsonEncodedData = json_encode([
|
||||
'targetUrl' => "{$link}?phantomjs=true",
|
||||
'requestType' => 'raw',
|
||||
'delayTime' => 1000,
|
||||
]);
|
||||
|
||||
$opts = [
|
||||
CURLOPT_URL => PHANTOMJS_CLOUD . env('PHANTOMJS_CLOUD_KEY'),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $jsonEncodedData,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: '.strlen($jsonEncodedData)],
|
||||
];
|
||||
|
||||
curl_setopt_array($curl, $opts);
|
||||
$encodedString = strip_tags(curl_exec($curl));
|
||||
curl_close($curl);
|
||||
|
||||
return Utils::decodePDF($encodedString);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,11 +96,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
return $this->account->isPro();
|
||||
}
|
||||
|
||||
public function isDemo()
|
||||
{
|
||||
return $this->account->id == Utils::getDemoAccountId();
|
||||
}
|
||||
|
||||
public function maxInvoiceDesignId()
|
||||
{
|
||||
return $this->isPro() ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST);
|
||||
@ -135,27 +130,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
{
|
||||
return Session::get(SESSION_COUNTER, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
public function getPopOverText()
|
||||
{
|
||||
if (!Utils::isNinja() || !Auth::check() || Session::has('error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$count = self::getRequestsCount();
|
||||
|
||||
if ($count == 1 || $count % 5 == 0) {
|
||||
if (!Utils::isRegistered()) {
|
||||
return trans('texts.sign_up_to_save');
|
||||
} elseif (!Auth::user()->account->name) {
|
||||
return trans('texts.set_name');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
public function afterSave($success = true, $forced = false)
|
||||
{
|
||||
@ -209,6 +183,12 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
if ($user->password != $user->getOriginal('password')) {
|
||||
$user->failed_logins = 0;
|
||||
}
|
||||
|
||||
// if the user changes their email then they need to reconfirm it
|
||||
if ($user->isEmailBeingChanged()) {
|
||||
$user->confirmed = 0;
|
||||
$user->confirmation_code = str_random(RANDOM_KEY_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
public static function onUpdatedUser($user)
|
||||
@ -219,7 +199,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
event(new UserSignedUp());
|
||||
}
|
||||
|
||||
event(new UserSettingsChanged());
|
||||
event(new UserSettingsChanged($user));
|
||||
}
|
||||
|
||||
public function isEmailBeingChanged()
|
||||
{
|
||||
return Utils::isNinjaProd()
|
||||
&& $this->email != $this->getOriginal('email')
|
||||
&& $this->getOriginal('confirmed');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use App\Events\InvoiceSent;
|
||||
|
||||
class ContactMailer extends Mailer
|
||||
{
|
||||
public function sendInvoice(Invoice $invoice, $reminder = false)
|
||||
public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false)
|
||||
{
|
||||
$invoice->load('invitations', 'client.language', 'account');
|
||||
$entityType = $invoice->getEntityType();
|
||||
@ -26,18 +26,17 @@ class ContactMailer extends Mailer
|
||||
}
|
||||
|
||||
$account->loadLocalizationSettings($client);
|
||||
|
||||
if ($account->pdf_email_attachment) {
|
||||
$invoice->updateCachedPDF();
|
||||
}
|
||||
|
||||
$emailTemplate = $account->getEmailTemplate($reminder ?: $entityType);
|
||||
$emailSubject = $account->getEmailSubject($reminder ?: $entityType);
|
||||
|
||||
$sent = false;
|
||||
|
||||
if ($account->attatchPDF() && !$pdfString) {
|
||||
$pdfString = $invoice->getPDFString();
|
||||
}
|
||||
|
||||
foreach ($invoice->invitations as $invitation) {
|
||||
if ($this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject)) {
|
||||
if ($this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject, $pdfString)) {
|
||||
$sent = true;
|
||||
}
|
||||
}
|
||||
@ -51,7 +50,7 @@ class ContactMailer extends Mailer
|
||||
return $sent ?: trans('texts.email_error');
|
||||
}
|
||||
|
||||
private function sendInvitation($invitation, $invoice, $body, $subject)
|
||||
private function sendInvitation($invitation, $invoice, $body, $subject, $pdfString)
|
||||
{
|
||||
$client = $invoice->client;
|
||||
$account = $invoice->account;
|
||||
@ -80,11 +79,18 @@ class ContactMailer extends Mailer
|
||||
'amount' => $invoice->getRequestedAmount()
|
||||
];
|
||||
|
||||
$data['body'] = $this->processVariables($body, $variables);
|
||||
$data['link'] = $invitation->getLink();
|
||||
$data['entityType'] = $invoice->getEntityType();
|
||||
$data['invoiceId'] = $invoice->id;
|
||||
$data['invitation'] = $invitation;
|
||||
$data = [
|
||||
'body' => $this->processVariables($body, $variables),
|
||||
'link' => $invitation->getLink(),
|
||||
'entityType' => $invoice->getEntityType(),
|
||||
'invoiceId' => $invoice->id,
|
||||
'invitation' => $invitation,
|
||||
];
|
||||
|
||||
if ($account->attatchPDF()) {
|
||||
$data['pdfString'] = $pdfString;
|
||||
$data['pdfFileName'] = $invoice->getFileName();
|
||||
}
|
||||
|
||||
$subject = $this->processVariables($subject, $variables);
|
||||
$fromEmail = $user->email;
|
||||
@ -131,13 +137,16 @@ class ContactMailer extends Mailer
|
||||
$data = [
|
||||
'body' => $this->processVariables($emailTemplate, $variables)
|
||||
];
|
||||
$subject = $this->processVariables($emailSubject, $variables);
|
||||
|
||||
$data['invoice_id'] = $payment->invoice->id;
|
||||
if ($invoice->account->pdf_email_attachment) {
|
||||
$invoice->updateCachedPDF();
|
||||
if ($account->attatchPDF()) {
|
||||
$data['pdfString'] = $invoice->getPDFString();
|
||||
$data['pdfFileName'] = $invoice->getFileName();
|
||||
}
|
||||
|
||||
$subject = $this->processVariables($emailSubject, $variables);
|
||||
$data['invoice_id'] = $payment->invoice->id;
|
||||
$invoice->updateCachedPDF();
|
||||
|
||||
if ($user->email && $contact->email) {
|
||||
$this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data);
|
||||
}
|
||||
|
@ -31,14 +31,8 @@ class Mailer
|
||||
->subject($subject);
|
||||
|
||||
// Attach the PDF to the email
|
||||
if (isset($data['invoiceId'])) {
|
||||
$invoice = Invoice::with('account')->where('id', '=', $data['invoiceId'])->first();
|
||||
if ($invoice->account->pdf_email_attachment && file_exists($invoice->getPDFPath())) {
|
||||
$message->attach(
|
||||
$invoice->getPDFPath(),
|
||||
array('as' => $invoice->getFileName(), 'mime' => 'application/pdf')
|
||||
);
|
||||
}
|
||||
if (!empty($data['pdfString']) && !empty($data['pdfFileName'])) {
|
||||
$message->attachData($data['pdfString'], $data['pdfFileName']);
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +48,7 @@ class Mailer
|
||||
$invitation = $data['invitation'];
|
||||
|
||||
// Track the Postmark message id
|
||||
if (isset($_ENV['POSTMARK_API_TOKEN'])) {
|
||||
if (isset($_ENV['POSTMARK_API_TOKEN']) && $response) {
|
||||
$json = $response->json();
|
||||
$invitation->message_id = $json['MessageID'];
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ class InvoiceRepository
|
||||
|
||||
if (!$publicId) {
|
||||
$invoice->client_id = $data['client_id'];
|
||||
$invoice->is_recurring = $data['is_recurring'] && !Utils::isDemo() ? true : false;
|
||||
$invoice->is_recurring = $data['is_recurring'] ? true : false;
|
||||
}
|
||||
|
||||
if ($invoice->is_recurring) {
|
||||
@ -576,6 +576,28 @@ class InvoiceRepository
|
||||
return count($invoices);
|
||||
}
|
||||
|
||||
public function findInvoiceByInvitation($invitationKey)
|
||||
{
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
|
||||
if (!$invitation) {
|
||||
app()->abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
if (!$invoice || $invoice->is_deleted) {
|
||||
app()->abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
$invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country');
|
||||
$client = $invoice->client;
|
||||
|
||||
if (!$client || $client->is_deleted) {
|
||||
app()->abort(404, trans('texts.invoice_not_found'));
|
||||
}
|
||||
|
||||
return $invitation;
|
||||
}
|
||||
|
||||
public function findOpenInvoices($clientId)
|
||||
{
|
||||
return Invoice::scope()
|
||||
@ -666,10 +688,6 @@ class InvoiceRepository
|
||||
}
|
||||
}
|
||||
|
||||
if ($recurInvoice->account->pdf_email_attachment) {
|
||||
$invoice->updateCachedPDF();
|
||||
}
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
|
@ -52,4 +52,10 @@ $app->singleton(
|
||||
|
|
||||
*/
|
||||
|
||||
/*
|
||||
if (strstr($_SERVER['HTTP_USER_AGENT'], 'PhantomJS') && Utils::isNinjaDev()) {
|
||||
$app->loadEnvironmentFrom('.env.testing');
|
||||
}
|
||||
*/
|
||||
|
||||
return $app;
|
||||
|
46
composer.lock
generated
46
composer.lock
generated
@ -1646,12 +1646,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Intervention/image.git",
|
||||
"reference": "44c9a6bb292e50cf8a1e4b5030c7954c2709c089"
|
||||
"reference": "e6c9cd03d6b2a870e74da03332feeb97d477fc87"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Intervention/image/zipball/44c9a6bb292e50cf8a1e4b5030c7954c2709c089",
|
||||
"reference": "44c9a6bb292e50cf8a1e4b5030c7954c2709c089",
|
||||
"url": "https://api.github.com/repos/Intervention/image/zipball/e6c9cd03d6b2a870e74da03332feeb97d477fc87",
|
||||
"reference": "e6c9cd03d6b2a870e74da03332feeb97d477fc87",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1700,7 +1700,7 @@
|
||||
"thumbnail",
|
||||
"watermark"
|
||||
],
|
||||
"time": "2015-08-30 15:37:50"
|
||||
"time": "2015-10-12 08:42:50"
|
||||
},
|
||||
{
|
||||
"name": "ircmaxell/password-compat",
|
||||
@ -2312,12 +2312,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lokielse/omnipay-alipay.git",
|
||||
"reference": "87622e8549b50773a8db83c93c3ad9a22e618991"
|
||||
"reference": "cbfbee089e0a84a58c73e9d3794894b81a6a82d6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lokielse/omnipay-alipay/zipball/87622e8549b50773a8db83c93c3ad9a22e618991",
|
||||
"reference": "87622e8549b50773a8db83c93c3ad9a22e618991",
|
||||
"url": "https://api.github.com/repos/lokielse/omnipay-alipay/zipball/cbfbee089e0a84a58c73e9d3794894b81a6a82d6",
|
||||
"reference": "cbfbee089e0a84a58c73e9d3794894b81a6a82d6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2353,7 +2353,7 @@
|
||||
"payment",
|
||||
"purchase"
|
||||
],
|
||||
"time": "2015-09-15 16:43:43"
|
||||
"time": "2015-10-07 09:33:48"
|
||||
},
|
||||
{
|
||||
"name": "maximebf/debugbar",
|
||||
@ -6199,16 +6199,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "2.2.3",
|
||||
"version": "2.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "ef1ca6835468857944d5c3b48fa503d5554cff2f"
|
||||
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef1ca6835468857944d5c3b48fa503d5554cff2f",
|
||||
"reference": "ef1ca6835468857944d5c3b48fa503d5554cff2f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
|
||||
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6257,7 +6257,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2015-09-14 06:51:16"
|
||||
"time": "2015-10-06 15:47:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -6439,16 +6439,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "4.8.10",
|
||||
"version": "4.8.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "463163747474815c5ccd4ae12b5b355ec12158e8"
|
||||
"reference": "00194eb95989190a73198390ceca081ad3441a7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/463163747474815c5ccd4ae12b5b355ec12158e8",
|
||||
"reference": "463163747474815c5ccd4ae12b5b355ec12158e8",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/00194eb95989190a73198390ceca081ad3441a7f",
|
||||
"reference": "00194eb95989190a73198390ceca081ad3441a7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6507,7 +6507,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2015-10-01 09:14:30"
|
||||
"time": "2015-10-12 03:36:47"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
@ -6799,16 +6799,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/global-state.git",
|
||||
"reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01"
|
||||
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
|
||||
"reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
|
||||
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6846,7 +6846,7 @@
|
||||
"keywords": [
|
||||
"global state"
|
||||
],
|
||||
"time": "2014-10-06 09:23:50"
|
||||
"time": "2015-10-12 03:26:01"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/recursion-context",
|
||||
|
@ -59,10 +59,10 @@ return [
|
||||
|
||||
'iron' => [
|
||||
'driver' => 'iron',
|
||||
'host' => 'mq-aws-us-east-1.iron.io',
|
||||
'token' => 'your-token',
|
||||
'project' => 'your-project-id',
|
||||
'queue' => 'your-queue-name',
|
||||
'host' => env('QUEUE_HOST', 'mq-aws-us-east-1.iron.io'),
|
||||
'token' => env('QUEUE_TOKEN'),
|
||||
'project' => env('QUEUE_PROJECT'),
|
||||
'queue' => env('QUEUE_NAME'),
|
||||
'encrypt' => true,
|
||||
],
|
||||
|
||||
|
11
public/css/bootstrap.splash.css
vendored
11
public/css/bootstrap.splash.css
vendored
File diff suppressed because one or more lines are too long
5
public/css/built.css
vendored
5
public/css/built.css
vendored
@ -2464,6 +2464,11 @@ table.dataTable tbody th, table.dataTable tbody td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
table.data-table tr {
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
border-top: 1px solid #d0d0d0;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
padding: 4px !important;
|
||||
margin-top: 1px;
|
||||
|
179
public/css/built.public.css
vendored
179
public/css/built.public.css
vendored
@ -779,4 +779,181 @@ div.DTFC_LeftBodyWrapper tbody tr:first-child td {
|
||||
|
||||
div.DTFC_LeftFootWrapper table {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
.navbar-header {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.navbar li a {
|
||||
padding: 31px 20px 31px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#footer {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
#footer .top {
|
||||
background: #2e2b2b;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
padding: 40px 0 27px;
|
||||
}
|
||||
|
||||
#footer .top li {
|
||||
display: inline-block;
|
||||
margin: 0 30px 10px;
|
||||
}
|
||||
|
||||
#footer .top a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#footer .bottom {
|
||||
border-top: 1px solid #5f5d5d;
|
||||
background: #211f1f;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: #636262;
|
||||
padding: 28px 0;
|
||||
}
|
||||
|
||||
#footer .bottom a {
|
||||
color: #636262;
|
||||
}
|
||||
|
||||
#footer .menu-item-31 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 9px;
|
||||
height: 15px;
|
||||
background: url({{ asset('images/social/facebook.svg') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#footer .menu-item-32 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
background: url({{ asset('images/social/twitter.svg') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#footer .menu-item-33 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
background: url({{ asset('images/social/github.png') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
/* Hide bootstrap sort header icons */
|
||||
table.data-table thead .sorting:after { content: '' !important }
|
||||
table.data-table thead .sorting_asc:after { content: '' !important }
|
||||
table.data-table thead .sorting_desc:after { content: '' !important}
|
||||
table.data-table thead .sorting_asc_disabled:after { content: '' !important }
|
||||
table.data-table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
|
||||
.dataTables_length {
|
||||
padding-left: 20px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.dataTables_length label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
#footer .top {
|
||||
padding: 27px 0;
|
||||
}
|
||||
|
||||
#footer .bottom {
|
||||
padding: 25px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
table.dataTable { border-radius: 3px; border-collapse: collapse;
|
||||
/*border-spacing: 0;*/}
|
||||
table.dataTable thead > tr > th, table.invoice-table thead > tr > th {
|
||||
color:#fff;
|
||||
}
|
||||
th:first-child {
|
||||
border-radius: 3px 0 0 0;
|
||||
border-left: none;
|
||||
}
|
||||
th:last-child {
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
|
||||
tr {border: none;}
|
||||
td {
|
||||
padding-top: 16px !important;
|
||||
padding-bottom: 16px !important;
|
||||
}
|
||||
|
||||
/*th {border-left: 1px solid #d26b26; }*/
|
||||
th {border-left: 1px solid #FFFFFF; }
|
||||
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
|
||||
vertical-align: middle;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #dfe0e1;
|
||||
}
|
||||
table.dataTable.no-footer {
|
||||
border-bottom: none;
|
||||
}
|
||||
.table-striped>tbody>tr:nth-child(odd)>td,
|
||||
.table-striped>tbody>tr:nth-child(odd)>th {
|
||||
background-color: #FDFDFD;
|
||||
}
|
||||
table.table thead .sorting_asc {
|
||||
background: url('../images/sort_asc.png') no-repeat 90% 50%;
|
||||
}
|
||||
table.table thead .sorting_desc {
|
||||
background: url('../images/sort_desc.png') no-repeat 90% 50%;
|
||||
}
|
||||
table.dataTable thead th, table.dataTable thead td, table.invoice-table thead th, table.invoice-table thead td {
|
||||
padding: 12px 10px;
|
||||
}
|
||||
table.dataTable tbody th, table.dataTable tbody td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dataTables_wrapper {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
table.table thead > tr > th {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
table td {
|
||||
max-width: 250px;
|
||||
}
|
||||
.pagination>li:first-child>a, .pagination>li:first-child>span {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
/* hide table sorting indicators */
|
||||
table.data-table thead .sorting { background: url('') no-repeat center right; }
|
177
public/css/public.style.css
vendored
Normal file
177
public/css/public.style.css
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
.navbar-header {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.navbar li a {
|
||||
padding: 31px 20px 31px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#footer {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
#footer .top {
|
||||
background: #2e2b2b;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
padding: 40px 0 27px;
|
||||
}
|
||||
|
||||
#footer .top li {
|
||||
display: inline-block;
|
||||
margin: 0 30px 10px;
|
||||
}
|
||||
|
||||
#footer .top a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#footer .bottom {
|
||||
border-top: 1px solid #5f5d5d;
|
||||
background: #211f1f;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: #636262;
|
||||
padding: 28px 0;
|
||||
}
|
||||
|
||||
#footer .bottom a {
|
||||
color: #636262;
|
||||
}
|
||||
|
||||
#footer .menu-item-31 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 9px;
|
||||
height: 15px;
|
||||
background: url({{ asset('images/social/facebook.svg') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#footer .menu-item-32 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
background: url({{ asset('images/social/twitter.svg') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#footer .menu-item-33 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
background: url({{ asset('images/social/github.png') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
/* Hide bootstrap sort header icons */
|
||||
table.table thead .sorting:after { content: '' !important }
|
||||
table.table thead .sorting_asc:after { content: '' !important }
|
||||
table.table thead .sorting_desc:after { content: '' !important }
|
||||
table.table thead .sorting_asc_disabled:after { content: '' !important }
|
||||
table.table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
|
||||
.dataTables_length {
|
||||
padding-left: 20px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.dataTables_length label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
#footer .top {
|
||||
padding: 27px 0;
|
||||
}
|
||||
|
||||
#footer .bottom {
|
||||
padding: 25px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
table.dataTable { border-radius: 3px; border-collapse: collapse;
|
||||
/*border-spacing: 0;*/}
|
||||
table.dataTable thead > tr > th, table.invoice-table thead > tr > th {
|
||||
color:#fff;
|
||||
}
|
||||
th:first-child {
|
||||
border-radius: 3px 0 0 0;
|
||||
border-left: none;
|
||||
}
|
||||
th:last-child {
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
|
||||
tr {border: none;}
|
||||
td {
|
||||
padding-top: 16px !important;
|
||||
padding-bottom: 16px !important;
|
||||
}
|
||||
|
||||
/*th {border-left: 1px solid #d26b26; }*/
|
||||
th {border-left: 1px solid #FFFFFF; }
|
||||
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
|
||||
vertical-align: middle;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #dfe0e1;
|
||||
}
|
||||
table.dataTable.no-footer {
|
||||
border-bottom: none;
|
||||
}
|
||||
.table-striped>tbody>tr:nth-child(odd)>td,
|
||||
.table-striped>tbody>tr:nth-child(odd)>th {
|
||||
background-color: #FDFDFD;
|
||||
}
|
||||
table.table thead .sorting_asc {
|
||||
background: url('../images/sort_asc.png') no-repeat 90% 50%;
|
||||
}
|
||||
table.table thead .sorting_desc {
|
||||
background: url('../images/sort_desc.png') no-repeat 90% 50%;
|
||||
}
|
||||
table.dataTable thead th, table.dataTable thead td, table.invoice-table thead th, table.invoice-table thead td {
|
||||
padding: 12px 10px;
|
||||
}
|
||||
table.dataTable tbody th, table.dataTable tbody td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dataTables_wrapper {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
table.table thead > tr > th {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
table td {
|
||||
max-width: 250px;
|
||||
}
|
||||
.pagination>li:first-child>a, .pagination>li:first-child>span {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
/* hide table sorting indicators */
|
||||
table.data-table thead .sorting { background: url('') no-repeat center right; }
|
1378
public/css/splash.css
vendored
1378
public/css/splash.css
vendored
File diff suppressed because it is too large
Load Diff
5
public/css/style.css
vendored
5
public/css/style.css
vendored
@ -114,6 +114,11 @@ table.dataTable tbody th, table.dataTable tbody td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
table.data-table tr {
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
border-top: 1px solid #d0d0d0;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
padding: 4px !important;
|
||||
margin-top: 1px;
|
||||
|
@ -31988,9 +31988,13 @@ NINJA.accountAddress = function(invoice) {
|
||||
{text: account.address2},
|
||||
{text: cityStatePostal},
|
||||
{text: account.country ? account.country.name : ''},
|
||||
{text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false},
|
||||
{text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false}
|
||||
];
|
||||
|
||||
if (invoice.is_pro) {
|
||||
data.push({text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false});
|
||||
data.push({text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false});
|
||||
}
|
||||
|
||||
return NINJA.prepareDataList(data, 'accountAddress');
|
||||
}
|
||||
|
||||
|
@ -415,9 +415,13 @@ NINJA.accountAddress = function(invoice) {
|
||||
{text: account.address2},
|
||||
{text: cityStatePostal},
|
||||
{text: account.country ? account.country.name : ''},
|
||||
{text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false},
|
||||
{text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false}
|
||||
];
|
||||
|
||||
if (invoice.is_pro) {
|
||||
data.push({text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false});
|
||||
data.push({text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false});
|
||||
}
|
||||
|
||||
return NINJA.prepareDataList(data, 'accountAddress');
|
||||
}
|
||||
|
||||
|
@ -815,5 +815,9 @@
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
|
||||
);
|
@ -814,5 +814,9 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
|
||||
);
|
||||
|
@ -769,7 +769,7 @@ return array(
|
||||
|
||||
'iframe_url' => 'Website',
|
||||
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||
'iframe_url_help2' => 'Currently only supported with on-site gateways (ie, Stripe and Authorize.net). You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||
|
||||
'auto_bill' => 'Auto Bill',
|
||||
'military_time' => '24 Hour Time',
|
||||
@ -814,5 +814,10 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
|
||||
|
||||
);
|
||||
|
||||
|
@ -792,5 +792,9 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
|
||||
);
|
||||
|
@ -814,5 +814,9 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
|
||||
);
|
@ -806,5 +806,9 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
|
||||
);
|
||||
|
@ -807,5 +807,9 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
|
||||
);
|
||||
|
@ -808,5 +808,9 @@ return array(
|
||||
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
);
|
||||
|
@ -815,6 +815,10 @@ return array(
|
||||
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
);
|
||||
|
||||
|
@ -814,4 +814,8 @@ return array(
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
);
|
@ -808,5 +808,9 @@ return array(
|
||||
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
);
|
||||
|
@ -808,5 +808,9 @@ return array(
|
||||
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
);
|
||||
|
@ -811,5 +811,9 @@ return array(
|
||||
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
|
||||
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
|
||||
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice',
|
||||
|
||||
'custom_invoice_link' => 'Custom Invoice Link',
|
||||
'total_invoiced' => 'Total Invoiced',
|
||||
'open_balance' => 'Open Balance',
|
||||
|
||||
);
|
||||
|
@ -42,7 +42,7 @@
|
||||
{!! Former::text('work_phone') !!}
|
||||
{!! Former::file('logo')->max(2, 'MB')->accept('image')->inlineHelp(trans('texts.logo_help')) !!}
|
||||
|
||||
@if (file_exists($account->getLogoPath()))
|
||||
@if ($account->hasLogo())
|
||||
<center>
|
||||
{!! HTML::image($account->getLogoPath().'?no_cache='.time(), 'Logo', ['width' => 200]) !!}
|
||||
<a href="#" onclick="deleteLogo()">{{ trans('texts.remove_logo') }}</a>
|
||||
|
@ -1,187 +0,0 @@
|
||||
@extends('accounts.nav')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<style type="text/css">
|
||||
textarea {
|
||||
min-height: 150px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
@include('accounts.nav_advanced')
|
||||
|
||||
{!! Former::open()->addClass('col-md-10 col-md-offset-1 warn-on-exit') !!}
|
||||
{!! Former::populateField('email_template_invoice', $invoiceEmail) !!}
|
||||
{!! Former::populateField('email_template_quote', $quoteEmail) !!}
|
||||
{!! Former::populateField('email_template_payment', $paymentEmail) !!}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.email_templates') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
|
||||
<div role="tabpanel">
|
||||
<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="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="invoice">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_invoice')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="invoice_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="quote">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_quote')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="quote_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="payment">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_payment')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="payment_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.reminder_emails') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
|
||||
<div role="tabpanel">
|
||||
<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="#payment" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.payment_email') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="invoice">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_invoice')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="invoice_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="quote">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_quote')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="quote_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="payment">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
{!! Former::textarea('email_template_payment')->raw() !!}
|
||||
</div>
|
||||
<div class="col-md-6" id="payment_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@if (Auth::user()->isPro())
|
||||
<center>
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
</center>
|
||||
@else
|
||||
<script>
|
||||
$(function() {
|
||||
$('form.warn-on-exit input').prop('disabled', true);
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
$('#email_template_invoice').keyup(refreshInvoice);
|
||||
$('#email_template_quote').keyup(refreshQuote);
|
||||
$('#email_template_payment').keyup(refreshPayment);
|
||||
|
||||
refreshInvoice();
|
||||
refreshQuote();
|
||||
refreshPayment();
|
||||
});
|
||||
|
||||
function refreshInvoice() {
|
||||
$('#invoice_preview').html(processVariables($('#email_template_invoice').val()));
|
||||
}
|
||||
|
||||
function refreshQuote() {
|
||||
$('#quote_preview').html(processVariables($('#email_template_quote').val()));
|
||||
}
|
||||
|
||||
function refreshPayment() {
|
||||
$('#payment_preview').html(processVariables($('#email_template_payment').val()));
|
||||
}
|
||||
|
||||
function processVariables(str) {
|
||||
if (!str) {
|
||||
return '';
|
||||
}
|
||||
|
||||
keys = ['footer', 'account', 'client', 'amount', 'link', 'contact'];
|
||||
vals = [{!! json_encode($emailFooter) !!}, '{!! Auth::user()->account->getDisplayName() !!}', 'Client Name', formatMoney(100), '{!! SITE_URL . '/view/...' !!}', 'Contact Name'];
|
||||
|
||||
// Add any available payment method links
|
||||
@foreach (\App\Models\Gateway::getPaymentTypeLinks() as $type)
|
||||
{!! "keys.push('" . $type.'_link' . "');" !!}
|
||||
{!! "vals.push('" . URL::to("/payment/xxxxxx/{$type}") . "');" !!}
|
||||
@endforeach
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var regExp = new RegExp('\\$'+keys[i], 'g');
|
||||
str = str.replace(regExp, vals[i]);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
@ -4,6 +4,9 @@
|
||||
@parent
|
||||
|
||||
<style type="text/css">
|
||||
.iframe_url {
|
||||
display: none;
|
||||
}
|
||||
.input-group-addon div.checkbox {
|
||||
display: inline;
|
||||
}
|
||||
@ -30,13 +33,26 @@
|
||||
<h3 class="panel-title">{!! trans('texts.email_settings') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
@if (Utils::isNinja())
|
||||
{{ Former::setOption('capitalize_translations', false) }}
|
||||
{!! Former::text('subdomain')->placeholder(trans('texts.www'))->onchange('onSubdomainChange()') !!}
|
||||
{!! Former::text('iframe_url')->placeholder('http://invoices.example.com/')
|
||||
->onchange('onDomainChange()')->appendIcon('question-sign')->addGroupClass('iframe_url') !!}
|
||||
@endif
|
||||
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
|
||||
@if (Utils::isNinja())
|
||||
{!! Former::inline_radios('custom_invoice_link')
|
||||
->onchange('onCustomLinkChange()')
|
||||
->radios([
|
||||
trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'],
|
||||
trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'],
|
||||
])->check($account->iframe_url ? 'website' : 'subdomain') !!}
|
||||
{{ Former::setOption('capitalize_translations', false) }}
|
||||
{!! Former::text('subdomain')
|
||||
->placeholder(trans('texts.www'))
|
||||
->onchange('onSubdomainChange()')
|
||||
->addGroupClass('subdomain')
|
||||
->label(' ') !!}
|
||||
{!! Former::text('iframe_url')
|
||||
->placeholder('http://www.example.com/invoice')
|
||||
->appendIcon('question-sign')
|
||||
->addGroupClass('iframe_url')
|
||||
->label(' ') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -150,7 +166,9 @@
|
||||
|
||||
<div class="modal-body">
|
||||
<p>{{ trans('texts.iframe_url_help1') }}</p>
|
||||
<pre><iframe id="invoiceIFrame" width="800" height="1000"></iframe>
|
||||
<pre><center>
|
||||
<iframe id="invoiceIFrame" width="1000" height="1200"></iframe>
|
||||
<center>
|
||||
<script language="javascript">
|
||||
var iframe = document.getElementById('invoiceIFrame');
|
||||
iframe.src = '{{ SITE_URL }}/view/'
|
||||
@ -187,8 +205,15 @@
|
||||
input.val(val);
|
||||
}
|
||||
|
||||
function onDomainChange() {
|
||||
|
||||
function onCustomLinkChange() {
|
||||
var val = $('input[name=custom_link]:checked').val()
|
||||
if (val == 'subdomain') {
|
||||
$('.subdomain').show();
|
||||
$('.iframe_url').hide();
|
||||
} else {
|
||||
$('.subdomain').hide();
|
||||
$('.iframe_url').show();
|
||||
}
|
||||
}
|
||||
|
||||
$('.iframe_url .input-group-addon').click(function() {
|
||||
@ -197,6 +222,14 @@
|
||||
|
||||
$(function() {
|
||||
setQuoteNumberEnabled();
|
||||
onCustomLinkChange();
|
||||
|
||||
$('#subdomain').change(function() {
|
||||
$('#iframe_url').val('');
|
||||
});
|
||||
$('#iframe_url').change(function() {
|
||||
$('#subdomain').val('');
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@ -1,21 +0,0 @@
|
||||
<ul class="nav nav-tabs nav nav-justified">
|
||||
{!! HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/invoice_settings', 'invoice_settings') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/templates_and_reminders', 'templates_and_reminders') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/charts_and_reports', 'charts_and_reports') !!}
|
||||
{!! HTML::nav_link('company/advanced_settings/user_management', 'users_and_tokens') !!}
|
||||
</ul>
|
||||
<p> </p>
|
||||
|
||||
@if (!Auth::user()->account->isPro())
|
||||
<center>
|
||||
<div style="font-size:larger;" class="col-md-8 col-md-offset-2">
|
||||
{!! trans('texts.pro_plan_advanced_settings', ['link'=>'<a href="#" onclick="showProPlan(\''.$feature.'\')">'.trans('texts.pro_plan.remove_logo_link').'</a>']) !!}
|
||||
</div>
|
||||
|
||||
<p> <p/>
|
||||
|
||||
</center>
|
||||
@endif
|
||||
|
||||
<br/>
|
@ -78,14 +78,8 @@
|
||||
@if ($client->address2)
|
||||
{{ $client->address2 }}<br/>
|
||||
@endif
|
||||
@if ($client->city)
|
||||
{{ $client->city }},
|
||||
@endif
|
||||
@if ($client->state)
|
||||
{{ $client->state }}
|
||||
@endif
|
||||
@if ($client->postal_code)
|
||||
{{ $client->postal_code }}
|
||||
@if ($client->getCityState())
|
||||
{{ $client->getCityState() }}<br/>
|
||||
@endif
|
||||
@if ($client->country)
|
||||
<br/>{{ $client->country->name }}
|
||||
@ -114,7 +108,7 @@
|
||||
@endif
|
||||
|
||||
@if ($client->website)
|
||||
<p>{!! $client->getWebsite() !!}</p>
|
||||
<p>{!! Utils::formatWebsite($client->website) !!}</p>
|
||||
@endif
|
||||
|
||||
@if ($client->language)
|
||||
|
170
resources/views/invited/dashboard.blade.php
Normal file
170
resources/views/invited/dashboard.blade.php
Normal file
@ -0,0 +1,170 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
div.main-container {
|
||||
min-height: 700px;
|
||||
}
|
||||
|
||||
div.row {
|
||||
padding-top: 2em;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
div.logo img {
|
||||
max-width:300px;
|
||||
max-height:200px;
|
||||
}
|
||||
|
||||
div.address-details {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
div.col-md-4-left {
|
||||
padding-left: 15px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
div.col-md-4-center {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
div.col-md-4-right {
|
||||
padding-left: 6px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
div.well {
|
||||
background-color: white;
|
||||
color: #0b4d78;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
div.well .fa {
|
||||
color: green;
|
||||
font-size: 18px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
div.well .amount {
|
||||
margin-top: 10px;
|
||||
font-size: 32px;
|
||||
font-weight: 300;
|
||||
color: black;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th, table.invoice-table thead > tr > th {
|
||||
background-color: {{ $color }} !important;
|
||||
}
|
||||
|
||||
.pagination>.active>a,
|
||||
.pagination>.active>span,
|
||||
.pagination>.active>a:hover,
|
||||
.pagination>.active>span:hover,
|
||||
.pagination>.active>a:focus,
|
||||
.pagination>.active>span:focus {
|
||||
background-color: {{ $color }};
|
||||
border-color: {{ $color }};
|
||||
}
|
||||
|
||||
table.table thead .sorting:after { content: '' !important }
|
||||
table.table thead .sorting_asc:after { content: '' !important }
|
||||
table.table thead .sorting_desc:after { content: '' !important }
|
||||
table.table thead .sorting_asc_disabled:after { content: '' !important }
|
||||
table.table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
|
||||
</style>
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="container main-container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 logo">
|
||||
@if ($account->hasLogo())
|
||||
{!! HTML::image($account->getLogoPath()) !!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-3 address-details">
|
||||
@if ($account->address1)
|
||||
{{ $account->address1 }}<br/>
|
||||
@endif
|
||||
@if ($account->address2)
|
||||
{{ $account->address2 }}<br/>
|
||||
@endif
|
||||
@if ($account->getCityState())
|
||||
{{ $account->getCityState() }}<br/>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3 address-details">
|
||||
@if ($account->work_phone)
|
||||
<i class="fa fa-phone" style="width: 20px"></i>{{ $account->work_phone }}<br/>
|
||||
@endif
|
||||
@if ($account->work_email)
|
||||
<i class="fa fa-envelope" style="width: 20px"></i>{!! HTML::mailto($account->work_email, $account->work_email) !!}<br/>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-4-left">
|
||||
<div class="well">
|
||||
<div class="fa fa-file-text-o"></div>
|
||||
<div>
|
||||
{{ trans('texts.total_invoiced') }}
|
||||
</div>
|
||||
<div class="amount">
|
||||
{{ Utils::formatMoney($client->paid_to_date + $client->balance, $client->currency_id ?: $account->currency_id) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-4-center">
|
||||
<div class="well">
|
||||
<div class="fa fa-credit-card"></div>
|
||||
<div>
|
||||
{{ trans('texts.paid_to_date') }}
|
||||
</div>
|
||||
<div class="amount">
|
||||
{{ Utils::formatMoney($client->paid_to_date, $client->currency_id ?: $account->currency_id) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-4-right">
|
||||
<div class="well">
|
||||
<div class="fa fa-server"></div>
|
||||
<div>
|
||||
{{ trans('texts.open_balance') }}
|
||||
</div>
|
||||
<div class="amount">
|
||||
{{ Utils::formatMoney($client->balance, $client->currency_id ?: $account->currency_id) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!! Datatable::table()
|
||||
->addColumn(
|
||||
trans('texts.date'),
|
||||
trans('texts.message'),
|
||||
trans('texts.balance'),
|
||||
trans('texts.adjustment'))
|
||||
->setUrl(route('api.client.activity'))
|
||||
->setOptions('bFilter', false)
|
||||
->setOptions('aaSorting', [['0', 'desc']])
|
||||
->setOptions('sPaginationType', 'bootstrap')
|
||||
->render('datatable') !!}
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
@ -104,7 +104,7 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($account->custom_invoice_text_label1 || $invoice && $account->custom_text_value1)
|
||||
@if ($account->showCustomField('custom_invoice_text_label1', $invoice))
|
||||
{!! Former::text('custom_text_value1')->label($account->custom_invoice_text_label1)->data_bind("value: custom_text_value1, valueUpdate: 'afterkeydown'") !!}
|
||||
@endif
|
||||
|
||||
@ -115,7 +115,7 @@
|
||||
</div>
|
||||
@elseif ($invoice && isset($lastSent) && $lastSent)
|
||||
<div class="pull-right" style="padding-top: 6px">
|
||||
{!! trans('texts.last_sent_on', ['date' => link_to('/invoices/'.$lastSent->public_id, Utils::dateToString($invoice->last_sent_date))]) !!}
|
||||
{!! trans('texts.last_sent_on', ['date' => link_to('/invoices/'.$lastSent->public_id, Utils::dateToString($invoice->last_sent_date), ['id' => 'lastSent'])]) !!}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@ -141,7 +141,7 @@
|
||||
->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw()
|
||||
) !!}
|
||||
|
||||
@if ($account->custom_invoice_text_label2 || $invoice && $account->custom_text_value2)
|
||||
@if ($account->showCustomField('custom_invoice_text_label2', $invoice))
|
||||
{!! Former::text('custom_text_value2')->label($account->custom_invoice_text_label2)->data_bind("value: custom_text_value2, valueUpdate: 'afterkeydown'") !!}
|
||||
@endif
|
||||
|
||||
@ -261,7 +261,7 @@
|
||||
<td style="text-align: right"><span data-bind="text: totals.discounted"/></td>
|
||||
</tr>
|
||||
|
||||
@if (($account->custom_invoice_label1 || ($invoice && floatval($invoice->custom_value1)) != 0) && $account->custom_invoice_taxes1)
|
||||
@if ($account->showCustomField('custom_invoice_label1', $invoice) && $account->custom_invoice_taxes1)
|
||||
<tr>
|
||||
<td class="hide-border" colspan="3"/>
|
||||
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
|
||||
@ -270,7 +270,7 @@
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if (($account->custom_invoice_label2 || ($invoice && floatval($invoice->custom_value2)) != 0) && $account->custom_invoice_taxes2)
|
||||
@if ($account->showCustomField('custom_invoice_label2', $invoice) && $account->custom_invoice_taxes2)
|
||||
<tr>
|
||||
<td class="hide-border" colspan="3"/>
|
||||
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
|
||||
@ -298,7 +298,7 @@
|
||||
<td style="text-align: right"><span data-bind="text: totals.taxAmount"/></td>
|
||||
</tr>
|
||||
|
||||
@if (($account->custom_invoice_label1 || ($invoice && floatval($invoice->custom_value1)) != 0) && !$account->custom_invoice_taxes1)
|
||||
@if ($account->showCustomField('custom_invoice_label1', $invoice) && !$account->custom_invoice_taxes1)
|
||||
<tr>
|
||||
<td class="hide-border" colspan="3"/>
|
||||
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
|
||||
@ -307,7 +307,7 @@
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if (($account->custom_invoice_label2 || ($invoice && floatval($invoice->custom_value2)) != 0) && !$account->custom_invoice_taxes2)
|
||||
@if ($account->showCustomField('custom_invoice_label2', $invoice) && !$account->custom_invoice_taxes2)
|
||||
<tr>
|
||||
<td class="hide-border" colspan="3"/>
|
||||
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
|
||||
|
@ -72,10 +72,11 @@
|
||||
@endif
|
||||
|
||||
var NINJA = NINJA || {};
|
||||
NINJA.primaryColor = "{{ $account->primary_color }}";
|
||||
NINJA.secondaryColor = "{{ $account->secondary_color }}";
|
||||
NINJA.fontSize = {{ $account->font_size }};
|
||||
|
||||
@if (Utils::isPro())
|
||||
NINJA.primaryColor = "{{ $account->primary_color }}";
|
||||
NINJA.secondaryColor = "{{ $account->secondary_color }}";
|
||||
NINJA.fontSize = {{ $account->font_size }};
|
||||
@endif
|
||||
var invoiceLabels = {!! json_encode($account->getInvoiceLabels()) !!};
|
||||
|
||||
if (window.invoice) {
|
||||
|
@ -33,7 +33,7 @@
|
||||
@if (count($paymentTypes) > 1)
|
||||
{!! DropdownButton::success(trans('texts.pay_now'))->withContents($paymentTypes)->large() !!}
|
||||
@else
|
||||
{!! link_to(URL::to($paymentURL), trans('texts.pay_now'), ['class' => 'btn btn-success btn-lg']) !!}
|
||||
<a href='{!! $paymentURL !!}' class="btn btn-success btn-lg">{{ trans('texts.pay_now') }}</a>
|
||||
@endif
|
||||
@else
|
||||
{!! Button::normal('Download PDF')->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}
|
||||
|
@ -1,15 +0,0 @@
|
||||
<!-- Facebook Conversion Code for Checkouts -->
|
||||
<script>(function() {
|
||||
var _fbq = window._fbq || (window._fbq = []);
|
||||
if (!_fbq.loaded) {
|
||||
var fbds = document.createElement('script');
|
||||
fbds.async = true;
|
||||
fbds.src = '//connect.facebook.net/en_US/fbds.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(fbds, s);
|
||||
_fbq.loaded = true;
|
||||
}
|
||||
})();
|
||||
window._fbq = window._fbq || [];
|
||||
window._fbq.push(['track', '{{ $pixelId }}', {'value':'{{ $price }}','currency':'USD'}]);
|
||||
</script>
|
@ -1,123 +1,7 @@
|
||||
@extends('master')
|
||||
|
||||
@section('head')
|
||||
|
||||
<link href="{{ asset('css/built.public.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
<style type="text/css">
|
||||
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
.navbar-header {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.navbar li a {
|
||||
padding: 31px 20px 31px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#footer {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
#footer .top {
|
||||
background: #2e2b2b;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
padding: 40px 0 27px;
|
||||
}
|
||||
|
||||
#footer .top li {
|
||||
display: inline-block;
|
||||
margin: 0 30px 10px;
|
||||
}
|
||||
|
||||
#footer .top a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#footer .bottom {
|
||||
border-top: 1px solid #5f5d5d;
|
||||
background: #211f1f;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: #636262;
|
||||
padding: 28px 0;
|
||||
}
|
||||
|
||||
#footer .bottom a {
|
||||
color: #636262;
|
||||
}
|
||||
|
||||
#footer .menu-item-31 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 9px;
|
||||
height: 15px;
|
||||
background: url({{ asset('images/social/facebook.svg') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#footer .menu-item-32 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
background: url({{ asset('images/social/twitter.svg') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#footer .menu-item-33 a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
background: url({{ asset('images/social/github.png') }}) no-repeat;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
/* Hide bootstrap sort header icons */
|
||||
table.table thead .sorting:after { content: '' !important }
|
||||
table.table thead .sorting_asc:after { content: '' !important }
|
||||
table.table thead .sorting_desc:after { content: '' !important}
|
||||
table.table thead .sorting_asc_disabled:after { content: '' !important }
|
||||
table.table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
|
||||
.dataTables_length {
|
||||
padding-left: 20px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.dataTables_length label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
#footer .top {
|
||||
padding: 27px 0;
|
||||
}
|
||||
|
||||
#footer .bottom {
|
||||
padding: 25px 0;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<link href="{{ asset('css/built.public.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
@stop
|
||||
|
||||
@section('body')
|
||||
@ -184,6 +68,9 @@ table.table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
@if (!isset($hideHeader) || !$hideHeader)
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li {{ Request::is('*client/dashboard') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/dashboard', trans('texts.dashboard') ) !!}
|
||||
</li>
|
||||
<li {{ Request::is('*client/quotes') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/quotes', trans('texts.quotes') ) !!}
|
||||
</li>
|
||||
|
@ -3,80 +3,25 @@
|
||||
@section('content')
|
||||
|
||||
<style type="text/css">
|
||||
table.dataTable thead > tr > th, table.invoice-table thead > tr > th {
|
||||
background-color: {{ $color }} !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.pagination>.active>a,
|
||||
.pagination>.active>span,
|
||||
.pagination>.active>a:hover,
|
||||
.pagination>.active>span:hover,
|
||||
.pagination>.active>a:focus,
|
||||
.pagination>.active>span:focus {
|
||||
background-color: {{ $color }};
|
||||
border-color: {{ $color }};
|
||||
}
|
||||
|
||||
table.dataTable { border-radius: 3px; border-collapse: collapse;
|
||||
/*border-spacing: 0;*/}
|
||||
table.dataTable thead > tr > th, table.invoice-table thead > tr > th {
|
||||
background-color: {{ $color }} !important;
|
||||
color:#fff;
|
||||
}
|
||||
th:first-child {
|
||||
border-radius: 3px 0 0 0;
|
||||
border-left: none;
|
||||
}
|
||||
th:last-child {
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
|
||||
tr {border: none;}
|
||||
td {
|
||||
padding-top: 16px !important;
|
||||
padding-bottom: 16px !important;
|
||||
}
|
||||
|
||||
/*th {border-left: 1px solid #d26b26; }*/
|
||||
th {border-left: 1px solid #FFFFFF; }
|
||||
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
|
||||
vertical-align: middle;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #dfe0e1;
|
||||
}
|
||||
table.dataTable.no-footer {
|
||||
border-bottom: none;
|
||||
}
|
||||
.table-striped>tbody>tr:nth-child(odd)>td,
|
||||
.table-striped>tbody>tr:nth-child(odd)>th {
|
||||
background-color: #FDFDFD;
|
||||
}
|
||||
table.table thead .sorting_asc {
|
||||
background: url('../images/sort_asc.png') no-repeat 90% 50%;
|
||||
}
|
||||
table.table thead .sorting_desc {
|
||||
background: url('../images/sort_desc.png') no-repeat 90% 50%;
|
||||
}
|
||||
table.dataTable thead th, table.dataTable thead td, table.invoice-table thead th, table.invoice-table thead td {
|
||||
padding: 12px 10px;
|
||||
}
|
||||
table.dataTable tbody th, table.dataTable tbody td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dataTables_wrapper {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
table.table thead > tr > th {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
table td {
|
||||
max-width: 250px;
|
||||
}
|
||||
.pagination>.active>a, .pagination>.active>span, .pagination>.active>a:hover, .pagination>.active>span:hover, .pagination>.active>a:focus, .pagination>.active>span:focus {
|
||||
background-color: {{ $color }};
|
||||
border-color: {{ $color }};
|
||||
}
|
||||
.pagination>li:first-child>a, .pagination>li:first-child>span {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
/* hide table sorting indicators */
|
||||
table.table thead .sorting { background: url('') no-repeat center right; }
|
||||
table.table thead .sorting:after { content: '' !important }
|
||||
table.table thead .sorting_asc:after { content: '' !important }
|
||||
table.table thead .sorting_desc:after { content: '' !important }
|
||||
table.table thead .sorting_asc_disabled:after { content: '' !important }
|
||||
table.table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
|
||||
</style>
|
||||
|
||||
@ -86,7 +31,7 @@
|
||||
|
||||
<!--
|
||||
<div id="top_right_buttons" class="pull-right">
|
||||
<input id="tableFilter" type="text" style="width:140px;margin-right:17px" class="form-control pull-left" placeholder="{{ trans('texts.filter') }}"/>
|
||||
<input id="tableFilter" type="text" style="width:140px;margin-right:17px" class="form-control pull-left" placeholder="{{ trans('texts.filter') }}"/>
|
||||
</div>
|
||||
-->
|
||||
|
||||
|
2
storage/pdfcache/.gitignore
vendored
2
storage/pdfcache/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
@ -31,7 +31,7 @@ class AcceptanceTester extends \Codeception\Actor
|
||||
$I->amOnPage('/login');
|
||||
$I->fillField(['name' => 'email'], Fixtures::get('username'));
|
||||
$I->fillField(['name' => 'password'], Fixtures::get('password'));
|
||||
$I->click('Let\'s go');
|
||||
$I->click('Login');
|
||||
|
||||
//$I->saveSessionSnapshot('login');
|
||||
}
|
||||
|
@ -27,11 +27,10 @@ class FunctionalTester extends \Codeception\Actor
|
||||
function checkIfLogin(\FunctionalTester $I)
|
||||
{
|
||||
//if ($I->loadSessionSnapshot('login')) return;
|
||||
|
||||
$I->amOnPage('/login');
|
||||
$I->fillField(['name' => 'email'], Fixtures::get('username'));
|
||||
$I->fillField(['name' => 'password'], Fixtures::get('password'));
|
||||
$I->click('Let\'s go');
|
||||
$I->click('#loginButton');
|
||||
|
||||
//$I->saveSessionSnapshot('login');
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php //[STAMP] 6309082647e087b523ad1f61b018a08d
|
||||
<?php //[STAMP] 62f79ae9e6d23b1ab98027060c9e03e4
|
||||
namespace _generated;
|
||||
|
||||
// This class was automatically generated by build task
|
||||
@ -1466,7 +1466,28 @@ trait FunctionalTesterActions
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
*
|
||||
* Grabs either the text content, or attribute values, of nodes
|
||||
* matched by $cssOrXpath and returns them as an array.
|
||||
*
|
||||
* ```html
|
||||
* <a href="#first">First</a>
|
||||
* <a href="#second">Second</a>
|
||||
* <a href="#third">Third</a>
|
||||
* ```
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* // would return ['First', 'Second', 'Third']
|
||||
* $aLinkText = $I->grabMultiple('a');
|
||||
*
|
||||
* // would return ['#first', '#second', '#third']
|
||||
* $aLinks = $I->grabMultiple('a', 'href');
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @param $cssOrXpath
|
||||
* @param $attribute
|
||||
* @return string[]
|
||||
* @see \Codeception\Lib\InnerBrowser::grabMultiple()
|
||||
*/
|
||||
public function grabMultiple($cssOrXpath, $attribute = null) {
|
||||
@ -1972,6 +1993,30 @@ trait FunctionalTesterActions
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
* Switch to iframe or frame on the page.
|
||||
*
|
||||
* Example:
|
||||
* ``` html
|
||||
* <iframe name="another_frame" src="http://example.com">
|
||||
* ```
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* # switch to iframe
|
||||
* $I->switchToIframe("another_frame");
|
||||
* ```
|
||||
*
|
||||
* @param string $name
|
||||
* @see \Codeception\Lib\InnerBrowser::switchToIframe()
|
||||
*/
|
||||
public function switchToIframe($name) {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Action('switchToIframe', func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
@ -2030,6 +2075,60 @@ trait FunctionalTesterActions
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
* Disable events for the next requests.
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* $I->disableEvents();
|
||||
* ?>
|
||||
* ```
|
||||
* @see \Codeception\Module\Laravel5::disableEvents()
|
||||
*/
|
||||
public function disableEvents() {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Action('disableEvents', func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
* Enable events for the next requests.
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* $I->enableEvents();
|
||||
* ?>
|
||||
* ```
|
||||
* @see \Codeception\Module\Laravel5::enableEvents()
|
||||
*/
|
||||
public function enableEvents() {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Action('enableEvents', func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
* Make sure events fired during the test.
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* $I->expectEvents('App\MyEvent');
|
||||
* $I->expectEvents('App\MyEvent', 'App\MyOtherEvent');
|
||||
* $I->expectEvents(['App\MyEvent', 'App\MyOtherEvent']);
|
||||
* ?>
|
||||
* ```
|
||||
* @param $events
|
||||
* @see \Codeception\Module\Laravel5::expectEvents()
|
||||
*/
|
||||
public function expectEvents($events) {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Action('expectEvents', func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
@ -2159,7 +2258,7 @@ trait FunctionalTesterActions
|
||||
* ```
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @param mixed|null $value
|
||||
* @return void
|
||||
* Conditional Assertion: Test won't be stopped on fail
|
||||
* @see \Codeception\Module\Laravel5::seeInSession()
|
||||
@ -2180,7 +2279,7 @@ trait FunctionalTesterActions
|
||||
* ```
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @param mixed|null $value
|
||||
* @return void
|
||||
* @see \Codeception\Module\Laravel5::seeInSession()
|
||||
*/
|
||||
@ -2267,6 +2366,43 @@ trait FunctionalTesterActions
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
* Assert that there are no form errors bound to the View.
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* $I->dontSeeFormErrors();
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @return bool
|
||||
* Conditional Assertion: Test won't be stopped on fail
|
||||
* @see \Codeception\Module\Laravel5::dontSeeFormErrors()
|
||||
*/
|
||||
public function cantSeeFormErrors() {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFormErrors', func_get_args()));
|
||||
}
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
* Assert that there are no form errors bound to the View.
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* $I->dontSeeFormErrors();
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @return bool
|
||||
* @see \Codeception\Module\Laravel5::dontSeeFormErrors()
|
||||
*/
|
||||
public function dontSeeFormErrors() {
|
||||
return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeFormErrors', func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [!] Method is generated. Documentation taken from corresponding module.
|
||||
*
|
||||
@ -2368,8 +2504,21 @@ trait FunctionalTesterActions
|
||||
* Takes either an object that implements the User interface or
|
||||
* an array of credentials.
|
||||
*
|
||||
* Example of Usage
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* // provide array of credentials
|
||||
* $I->amLoggedAs(['username' => 'jane@example.com', 'password' => 'password']);
|
||||
*
|
||||
* // provide User object
|
||||
* $I->amLoggesAs( new User );
|
||||
*
|
||||
* // can be verified with $I->seeAuthentication();
|
||||
* ?>
|
||||
* ```
|
||||
* @param \Illuminate\Contracts\Auth\User|array $user
|
||||
* @param string $driver
|
||||
* @param string|null $driver 'eloquent', 'database', or custom driver
|
||||
* @return void
|
||||
* @see \Codeception\Module\Laravel5::amLoggedAs()
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php //[STAMP] 411f8e49789d4aff7d24b72665b5df9f
|
||||
<?php //[STAMP] 9d680d4d116f13b46f4a15d3ff55e20a
|
||||
namespace _generated;
|
||||
|
||||
// This class was automatically generated by build task
|
||||
|
@ -24,11 +24,11 @@ class GoProCest
|
||||
$I->click('Sign Up');
|
||||
$I->wait(1);
|
||||
|
||||
$I->checkOption('#terms_checkbox');
|
||||
$I->fillField(['name' =>'new_first_name'], $this->faker->firstName);
|
||||
$I->fillField(['name' =>'new_last_name'], $this->faker->lastName);
|
||||
$I->fillField(['name' =>'new_email'], $userEmail);
|
||||
$I->fillField(['name' =>'new_password'], $userPassword);
|
||||
$I->checkOption('#terms_checkbox');
|
||||
$I->click('Save');
|
||||
$I->wait(1);
|
||||
|
||||
|
@ -72,10 +72,10 @@ class InvoiceCest
|
||||
$I->click('Recurring Invoice');
|
||||
$I->see($clientEmail);
|
||||
|
||||
$I->click('#lastInvoiceSent');
|
||||
$I->click('#lastSent');
|
||||
$I->see($invoiceNumber);
|
||||
}
|
||||
|
||||
|
||||
public function editInvoice(AcceptanceTester $I)
|
||||
{
|
||||
$I->wantTo('edit an invoice');
|
||||
@ -106,7 +106,6 @@ class InvoiceCest
|
||||
|
||||
$I->see($invoiceNumber);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public function deleteInvoice(AcceptanceTester $I)
|
||||
|
@ -9,7 +9,9 @@ modules:
|
||||
enabled:
|
||||
- \Helper\Functional
|
||||
- PhpBrowser:
|
||||
url: 'http://ninja.dev/'
|
||||
url: 'http://ninja.dev'
|
||||
curl:
|
||||
CURLOPT_RETURNTRANSFER: true
|
||||
- Laravel5:
|
||||
environment_file: '.env'
|
||||
cleanup: false
|
@ -36,7 +36,6 @@ class SettingsCest
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
$I->seeRecord('accounts', array('name' => $name));
|
||||
}
|
||||
|
||||
@ -48,7 +47,138 @@ class SettingsCest
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
}
|
||||
|
||||
public function createProduct(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('create a product');
|
||||
$I->amOnPage('/products/create');
|
||||
|
||||
$productKey = $this->faker->text(10);
|
||||
|
||||
$I->fillField(['name' => 'product_key'], $productKey);
|
||||
$I->fillField(['name' => 'notes'], $this->faker->text(80));
|
||||
$I->fillField(['name' => 'cost'], $this->faker->numberBetween(1, 20));
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('products', array('product_key' => $productKey));
|
||||
}
|
||||
|
||||
public function updateProduct(FunctionalTester $I)
|
||||
{
|
||||
return;
|
||||
|
||||
$I->wantTo('update a product');
|
||||
$I->amOnPage('/products/1/edit');
|
||||
|
||||
$productKey = $this->faker->text(10);
|
||||
|
||||
$I->fillField(['name' => 'product_key'], $productKey);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('products', array('product_key' => $productKey));
|
||||
}
|
||||
|
||||
public function updateNotifications(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update notification settings');
|
||||
$I->amOnPage('/company/notifications');
|
||||
|
||||
$terms = $this->faker->text(80);
|
||||
|
||||
$I->fillField(['name' => 'invoice_terms'], $terms);
|
||||
$I->fillField(['name' => 'invoice_footer'], $this->faker->text(60));
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('accounts', array('invoice_terms' => $terms));
|
||||
}
|
||||
|
||||
public function updateInvoiceDesign(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update invoice design');
|
||||
$I->amOnPage('/company/advanced_settings/invoice_design');
|
||||
|
||||
$color = $this->faker->hexcolor;
|
||||
|
||||
$I->fillField(['name' => 'labels_item'], $this->faker->text(14));
|
||||
$I->fillField(['name' => 'primary_color'], $color);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('accounts', array('primary_color' => $color));
|
||||
}
|
||||
|
||||
public function updateInvoiceSettings(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update invoice settings');
|
||||
$I->amOnPage('/company/advanced_settings/invoice_settings');
|
||||
|
||||
$label = $this->faker->text(10);
|
||||
|
||||
$I->fillField(['name' => 'custom_client_label1'], $label);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('accounts', array('custom_client_label1' => $label));
|
||||
|
||||
//$I->amOnPage('/clients/create');
|
||||
//$I->see($label);
|
||||
}
|
||||
|
||||
public function updateEmailTemplates(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update email templates');
|
||||
$I->amOnPage('/company/advanced_settings/templates_and_reminders');
|
||||
|
||||
$string = $this->faker->text(100);
|
||||
|
||||
$I->fillField(['name' => 'email_template_invoice'], $string);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('accounts', array('email_template_invoice' => $string));
|
||||
}
|
||||
|
||||
public function runReport(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('run the report');
|
||||
$I->amOnPage('/company/advanced_settings/charts_and_reports');
|
||||
|
||||
$I->click('Run');
|
||||
$I->seeResponseCodeIs(200);
|
||||
}
|
||||
|
||||
public function createUser(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('create a user');
|
||||
$I->amOnPage('/users/create');
|
||||
|
||||
$email = $this->faker->safeEmail;
|
||||
|
||||
$I->fillField(['name' => 'first_name'], $this->faker->firstName);
|
||||
$I->fillField(['name' => 'last_name'], $this->faker->lastName);
|
||||
$I->fillField(['name' => 'email'], $email);
|
||||
$I->click('Send invitation');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('users', array('email' => $email));
|
||||
}
|
||||
|
||||
public function createToken(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('create a token');
|
||||
$I->amOnPage('/tokens/create');
|
||||
|
||||
$name = $this->faker->firstName;
|
||||
|
||||
$I->fillField(['name' => 'name'], $name);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeRecord('account_tokens', array('name' => $name));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -83,145 +213,5 @@ class SettingsCest
|
||||
}
|
||||
*/
|
||||
|
||||
public function createProduct(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('create a product');
|
||||
$I->amOnPage('/products/create');
|
||||
|
||||
$productKey = $this->faker->text(10);
|
||||
|
||||
$I->fillField(['name' => 'product_key'], $productKey);
|
||||
$I->fillField(['name' => 'notes'], $this->faker->text(80));
|
||||
$I->fillField(['name' => 'cost'], $this->faker->numberBetween(1, 20));
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully created product');
|
||||
$I->seeRecord('products', array('product_key' => $productKey));
|
||||
}
|
||||
|
||||
public function updateProduct(FunctionalTester $I)
|
||||
{
|
||||
return;
|
||||
|
||||
$I->wantTo('update a product');
|
||||
$I->amOnPage('/products/1/edit');
|
||||
|
||||
$productKey = $this->faker->text(10);
|
||||
|
||||
$I->fillField(['name' => 'product_key'], $productKey);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated product');
|
||||
$I->seeRecord('products', array('product_key' => $productKey));
|
||||
}
|
||||
|
||||
public function updateNotifications(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update notification settings');
|
||||
$I->amOnPage('/company/notifications');
|
||||
|
||||
$terms = $this->faker->text(80);
|
||||
|
||||
$I->fillField(['name' => 'invoice_terms'], $terms);
|
||||
$I->fillField(['name' => 'invoice_footer'], $this->faker->text(60));
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
$I->seeRecord('accounts', array('invoice_terms' => $terms));
|
||||
}
|
||||
|
||||
public function updateInvoiceDesign(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update invoice design');
|
||||
$I->amOnPage('/company/advanced_settings/invoice_design');
|
||||
|
||||
$color = $this->faker->hexcolor;
|
||||
|
||||
$I->fillField(['name' => 'labels_item'], $this->faker->text(14));
|
||||
$I->fillField(['name' => 'primary_color'], $color);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
$I->seeRecord('accounts', array('primary_color' => $color));
|
||||
}
|
||||
|
||||
public function updateInvoiceSettings(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update invoice settings');
|
||||
$I->amOnPage('/company/advanced_settings/invoice_settings');
|
||||
|
||||
$label = $this->faker->text(10);
|
||||
|
||||
$I->fillField(['name' => 'custom_client_label1'], $label);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
$I->seeRecord('accounts', array('custom_client_label1' => $label));
|
||||
|
||||
$I->amOnPage('/clients/create');
|
||||
$I->see($label);
|
||||
}
|
||||
|
||||
public function updateEmailTemplates(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('update email templates');
|
||||
$I->amOnPage('/company/advanced_settings/templates_and_reminders');
|
||||
|
||||
$string = $this->faker->text(100);
|
||||
|
||||
$I->fillField(['name' => 'email_template_invoice'], $string);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully updated settings');
|
||||
$I->seeRecord('accounts', array('email_template_invoice' => $string));
|
||||
}
|
||||
|
||||
public function runReport(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('run the report');
|
||||
$I->amOnPage('/company/advanced_settings/charts_and_reports');
|
||||
|
||||
$I->click('Run');
|
||||
$I->seeResponseCodeIs(200);
|
||||
}
|
||||
|
||||
public function createUser(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('create a user');
|
||||
$I->amOnPage('/users/create');
|
||||
|
||||
$email = $this->faker->safeEmail;
|
||||
|
||||
$I->fillField(['name' => 'first_name'], $this->faker->firstName);
|
||||
$I->fillField(['name' => 'last_name'], $this->faker->lastName);
|
||||
$I->fillField(['name' => 'email'], $email);
|
||||
$I->click('Send invitation');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully sent invitation');
|
||||
$I->seeRecord('users', array('email' => $email));
|
||||
}
|
||||
|
||||
public function createToken(FunctionalTester $I)
|
||||
{
|
||||
$I->wantTo('create a token');
|
||||
$I->amOnPage('/tokens/create');
|
||||
|
||||
$name = $this->faker->firstName;
|
||||
|
||||
$I->fillField(['name' => 'name'], $name);
|
||||
$I->click('Save');
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->see('Successfully created token');
|
||||
$I->seeRecord('account_tokens', array('name' => $name));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user